rnd-20131209-1-src
[rocksndiamonds.git] / src / init.c
index 8ccf065426e1d77694246063c638bb531219df59..c2344a507175af8ce42059a4d563ca8c35cada54 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-* (c) 1995-2002 Artsoft Entertainment                      *
+* (c) 1995-2006 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
 #include "network.h"
 #include "netserv.h"
 #include "cartoons.h"
+#include "config.h"
 
 #include "conf_e2g.c"  /* include auto-generated data structure definitions */
 #include "conf_esg.c"  /* include auto-generated data structure definitions */
 #include "conf_e2s.c"  /* include auto-generated data structure definitions */
 #include "conf_fnt.c"  /* include auto-generated data structure definitions */
+#include "conf_g2s.c"  /* include auto-generated data structure definitions */
+#include "conf_g2m.c"  /* include auto-generated data structure definitions */
+#include "conf_act.c"  /* include auto-generated data structure definitions */
 
 
 #define CONFIG_TOKEN_FONT_INITIAL              "font.initial"
+#define CONFIG_TOKEN_GLOBAL_BUSY               "global.busy"
 
+#define DEBUG_PRINT_INIT_TIMESTAMPS            TRUE
+#define DEBUG_PRINT_INIT_TIMESTAMPS_DEPTH      0
 
-struct FontBitmapInfo font_initial[NUM_INITIAL_FONTS];
 
+static struct FontBitmapInfo font_initial[NUM_INITIAL_FONTS];
+static struct GraphicInfo    anim_initial;
 
-static void InitTileClipmasks()
+static int copy_properties[][5] =
 {
-#if 0
-#if defined(TARGET_X11)
-  XGCValues clip_gc_values;
-  unsigned long clip_gc_valuemask;
-
-#if defined(TARGET_X11_NATIVE)
-
-#if 0
-  GC copy_clipmask_gc;
-
-  static struct
   {
-    int start;
-    int count;
-  }
-  tile_needs_clipping[] =
-  {
-    { GFX_SPIELER1_UP, 4 },
-    { GFX_SPIELER1_DOWN, 4 },
-    { GFX_SPIELER1_LEFT, 4 },
-    { GFX_SPIELER1_RIGHT, 4 },
-    { GFX_SPIELER1_PUSH_LEFT, 4 },
-    { GFX_SPIELER1_PUSH_RIGHT, 4 },
-    { GFX_SPIELER2_UP, 4 },
-    { GFX_SPIELER2_DOWN, 4 },
-    { GFX_SPIELER2_LEFT, 4 },
-    { GFX_SPIELER2_RIGHT, 4 },
-    { GFX_SPIELER2_PUSH_LEFT, 4 },
-    { GFX_SPIELER2_PUSH_RIGHT, 4 },
-    { GFX_SPIELER3_UP, 4 },
-    { GFX_SPIELER3_DOWN, 4 },
-    { GFX_SPIELER3_LEFT, 4 },
-    { GFX_SPIELER3_RIGHT, 4 },
-    { GFX_SPIELER3_PUSH_LEFT, 4 },
-    { GFX_SPIELER3_PUSH_RIGHT, 4 },
-    { GFX_SPIELER4_UP, 4 },
-    { GFX_SPIELER4_DOWN, 4 },
-    { GFX_SPIELER4_LEFT, 4 },
-    { GFX_SPIELER4_RIGHT, 4 },
-    { GFX_SPIELER4_PUSH_LEFT, 4 },
-    { GFX_SPIELER4_PUSH_RIGHT, 4 },
-    { GFX_SP_MURPHY, 1 },
-    { GFX_MURPHY_GO_LEFT, 3 },
-    { GFX_MURPHY_GO_RIGHT, 3 },
-    { GFX_MURPHY_SNAP_UP, 1 },
-    { GFX_MURPHY_SNAP_DOWN, 1 },
-    { GFX_MURPHY_SNAP_RIGHT, 1 },
-    { GFX_MURPHY_SNAP_LEFT, 1 },
-    { GFX_MURPHY_PUSH_RIGHT, 1 },
-    { GFX_MURPHY_PUSH_LEFT, 1 },
-    { GFX_GEBLUBBER, 4 },
-    { GFX_DYNAMIT, 7 },
-    { GFX_DYNABOMB, 4 },
-    { GFX_EXPLOSION, 8 },
-    { GFX_SOKOBAN_OBJEKT, 1 },
-    { GFX_FUNKELN_BLAU, 3 },
-    { GFX_FUNKELN_WEISS, 3 },
-    { GFX2_SHIELD_PASSIVE, 3 },
-    { GFX2_SHIELD_ACTIVE, 3 },
-    { -1, 0 }
-  };
-#endif
-
-#endif /* TARGET_X11_NATIVE */
-#endif /* TARGET_X11 */
-
-  int i;
+    EL_BUG,
+    EL_BUG_LEFT,               EL_BUG_RIGHT,
+    EL_BUG_UP,                 EL_BUG_DOWN
+  },
+  {
+    EL_SPACESHIP,
+    EL_SPACESHIP_LEFT,         EL_SPACESHIP_RIGHT,
+    EL_SPACESHIP_UP,           EL_SPACESHIP_DOWN
+  },
+  {
+    EL_BD_BUTTERFLY,
+    EL_BD_BUTTERFLY_LEFT,      EL_BD_BUTTERFLY_RIGHT,
+    EL_BD_BUTTERFLY_UP,                EL_BD_BUTTERFLY_DOWN
+  },
+  {
+    EL_BD_FIREFLY,
+    EL_BD_FIREFLY_LEFT,                EL_BD_FIREFLY_RIGHT,
+    EL_BD_FIREFLY_UP,          EL_BD_FIREFLY_DOWN
+  },
+  {
+    EL_PACMAN,
+    EL_PACMAN_LEFT,            EL_PACMAN_RIGHT,
+    EL_PACMAN_UP,              EL_PACMAN_DOWN
+  },
+  {
+    EL_YAMYAM,
+    EL_YAMYAM_LEFT,            EL_YAMYAM_RIGHT,
+    EL_YAMYAM_UP,              EL_YAMYAM_DOWN
+  },
+  {
+    EL_MOLE,
+    EL_MOLE_LEFT,              EL_MOLE_RIGHT,
+    EL_MOLE_UP,                        EL_MOLE_DOWN
+  },
+  {
+    -1,
+    -1, -1, -1, -1
+  }
+};
 
-  /* initialize pixmap array for special X11 tile clipping to Pixmap 'None' */
-  for (i=0; i<NUM_TILES; i++)
-    tile_clipmask[i] = None;
 
-#if defined(TARGET_X11)
-  /* This stuff is needed because X11 (XSetClipOrigin(), to be precise) is
-     often very slow when preparing a masked XCopyArea() for big Pixmaps.
-     To prevent this, create small (tile-sized) mask Pixmaps which will then
-     be set much faster with XSetClipOrigin() and speed things up a lot. */
+static void print_timestamp_ext(char *message, char *mode)
+{
+#if DEBUG
+#if DEBUG_PRINT_INIT_TIMESTAMPS
+  static char *debug_message = NULL;
+  static char *last_message = NULL;
+  static int counter_nr = 0;
+  int max_depth = DEBUG_PRINT_INIT_TIMESTAMPS_DEPTH;
 
-  clip_gc_values.graphics_exposures = False;
-  clip_gc_valuemask = GCGraphicsExposures;
-  tile_clip_gc = XCreateGC(display, window->drawable,
-                          clip_gc_valuemask, &clip_gc_values);
+  checked_free(debug_message);
+  debug_message = getStringCat3(mode, " ", message);
 
-#if 0
-  for (i=0; i<NUM_BITMAPS; i++)
+  if (strEqual(mode, "INIT"))
   {
-    if (pix[i]->clip_mask)
-    {
-      clip_gc_values.graphics_exposures = False;
-      clip_gc_values.clip_mask = pix[i]->clip_mask;
-      clip_gc_valuemask = GCGraphicsExposures | GCClipMask;
-      pix[i]->stored_clip_gc = XCreateGC(display, window->drawable,
-                                        clip_gc_valuemask, &clip_gc_values);
-    }
-  }
-#endif
+    debug_print_timestamp(counter_nr, NULL);
 
-#if defined(TARGET_X11_NATIVE)
+    if (counter_nr + 1 < max_depth)
+      debug_print_timestamp(counter_nr, debug_message);
 
-#if 0
-  /* create graphic context structures needed for clipping */
-  clip_gc_values.graphics_exposures = False;
-  clip_gc_valuemask = GCGraphicsExposures;
-  copy_clipmask_gc = XCreateGC(display, pix[PIX_BACK]->clip_mask,
-                              clip_gc_valuemask, &clip_gc_values);
+    counter_nr++;
 
-  /* create only those clipping Pixmaps we really need */
-  for (i=0; tile_needs_clipping[i].start>=0; i++)
+    debug_print_timestamp(counter_nr, NULL);
+  }
+  else if (strEqual(mode, "DONE"))
   {
-    int j;
+    counter_nr--;
 
-    for (j=0; j<tile_needs_clipping[i].count; j++)
+    if (counter_nr + 1 < max_depth ||
+       (counter_nr == 0 && max_depth == 1))
     {
-      int tile = tile_needs_clipping[i].start + j;
-      int graphic = tile;
-      int src_x, src_y;
-      Bitmap *src_bitmap;
-      Pixmap src_pixmap;
+      last_message = message;
 
-      getGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
-      src_pixmap = src_bitmap->clip_mask;
-
-      tile_clipmask[tile] = XCreatePixmap(display, window->drawable,
-                                         TILEX, TILEY, 1);
+      if (counter_nr == 0 && max_depth == 1)
+      {
+       checked_free(debug_message);
+       debug_message = getStringCat3("TIME", " ", message);
+      }
 
-      XCopyArea(display, src_pixmap, tile_clipmask[tile], copy_clipmask_gc,
-               src_x, src_y, TILEX, TILEY, 0, 0);
+      debug_print_timestamp(counter_nr, debug_message);
     }
   }
-
-  XFreeGC(display, copy_clipmask_gc);
+  else if (!strEqual(mode, "TIME") ||
+          !strEqual(message, last_message))
+  {
+    if (counter_nr < max_depth)
+      debug_print_timestamp(counter_nr, debug_message);
+  }
 #endif
-
-#endif /* TARGET_X11_NATIVE */
-#endif /* TARGET_X11 */
 #endif
 }
 
-void FreeTileClipmasks()
+static void print_timestamp_init(char *message)
 {
-#if 0
-#if defined(TARGET_X11)
-  int i;
+  print_timestamp_ext(message, "INIT");
+}
+
+static void print_timestamp_time(char *message)
+{
+  print_timestamp_ext(message, "TIME");
+}
+
+static void print_timestamp_done(char *message)
+{
+  print_timestamp_ext(message, "DONE");
+}
+
+void DrawInitAnim()
+{
+  struct GraphicInfo *graphic_info_last = graphic_info;
+  int graphic = 0;
+  static unsigned int action_delay = 0;
+  unsigned int action_delay_value = GameFrameDelay;
+  int sync_frame = FrameCounter;
+  int x, y;
+
+  if (game_status != GAME_MODE_LOADING)
+    return;
+
+  if (anim_initial.bitmap == NULL || window == NULL)
+    return;
+
+  if (!DelayReached(&action_delay, action_delay_value))
+    return;
 
-  for (i=0; i<NUM_TILES; i++)
+#if 0
   {
-    if (tile_clipmask[i] != None)
-    {
-      XFreePixmap(display, tile_clipmask[i]);
-      tile_clipmask[i] = None;
-    }
+    static unsigned int last_counter = -1;
+    unsigned int current_counter = Counter();
+    unsigned int delay = current_counter - last_counter;
+
+    if (last_counter != -1 && delay > action_delay_value + 5)
+      printf("::: DrawInitAnim: DELAY TOO LONG: %ld\n", delay);
+
+    last_counter = current_counter;
   }
+#endif
+
+  x = ALIGNED_TEXT_XPOS(&init_last.busy);
+  y = ALIGNED_TEXT_YPOS(&init_last.busy);
 
-  if (tile_clip_gc)
-    XFreeGC(display, tile_clip_gc);
-  tile_clip_gc = None;
+  graphic_info = &anim_initial;                /* graphic == 0 => anim_initial */
 
 #if 0
-  for (i=0; i<NUM_BITMAPS; i++)
   {
-    if (pix[i] != NULL && pix[i]->stored_clip_gc)
-    {
-      XFreeGC(display, pix[i]->stored_clip_gc);
-      pix[i]->stored_clip_gc = None;
-    }
+    static boolean done = FALSE;
+
+    // if (!done)
+      printf("::: %d, %d, %d, %d => %d, %d [%d, %d] [%d, %d]\n",
+            init.busy.x, init.busy.y,
+            init.busy.align, init.busy.valign,
+            x, y,
+            graphic_info[graphic].width,
+            graphic_info[graphic].height,
+            sync_frame, anim_initial.anim_delay);
+
+    done = TRUE;
   }
 #endif
 
-#endif /* TARGET_X11 */
+  if (sync_frame % anim_initial.anim_delay == 0)
+  {
+#if 1
+    Bitmap *src_bitmap;
+    int src_x, src_y;
+    int width = graphic_info[graphic].width;
+    int height = graphic_info[graphic].height;
+    int frame = getGraphicAnimationFrame(graphic, sync_frame);
+
+    getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
+    BlitBitmap(src_bitmap, window, src_x, src_y, width, height, x, y);
+#else
+    /* !!! this can only draw TILEX/TILEY size animations !!! */
+    DrawGraphicAnimationExt(window, x, y, graphic, sync_frame, NO_MASKING);
 #endif
+  }
+
+  graphic_info = graphic_info_last;
+
+  FrameCounter++;
 }
 
 void FreeGadgets()
@@ -233,39 +252,102 @@ void InitGadgets()
   CreateToolButtons();
   CreateScreenGadgets();
 
+  InitGadgetsSoundCallback(PlaySoundActivating, PlaySoundSelecting);
+
   gadgets_initialized = TRUE;
 }
 
+inline void InitElementSmallImagesScaledUp(int graphic)
+{
+#if 0
+  struct FileInfo *fi = getImageListEntryFromImageID(graphic);
+
+  printf("::: '%s' -> '%s'\n", fi->token, fi->filename);
+#endif
+
+  CreateImageWithSmallImages(graphic, graphic_info[graphic].scale_up_factor);
+}
+
 void InitElementSmallImages()
 {
+  static int special_graphics[] =
+  {
+    IMG_EDITOR_ELEMENT_BORDER,
+    IMG_EDITOR_ELEMENT_BORDER_INPUT,
+    IMG_EDITOR_CASCADE_LIST,
+    IMG_EDITOR_CASCADE_LIST_ACTIVE,
+    -1
+  };
   struct PropertyMapping *property_mapping = getImageListPropertyMapping();
   int num_property_mappings = getImageListPropertyMappingSize();
   int i;
 
   /* initialize normal images from static configuration */
-  for (i=0; element_to_graphic[i].element > -1; i++)
-    CreateImageWithSmallImages(element_to_graphic[i].graphic);
+  for (i = 0; element_to_graphic[i].element > -1; i++)
+    InitElementSmallImagesScaledUp(element_to_graphic[i].graphic);
 
   /* initialize special images from static configuration */
-  for (i=0; element_to_special_graphic[i].element > -1; i++)
-    CreateImageWithSmallImages(element_to_special_graphic[i].graphic);
+  for (i = 0; element_to_special_graphic[i].element > -1; i++)
+    InitElementSmallImagesScaledUp(element_to_special_graphic[i].graphic);
+
+  /* initialize images from dynamic configuration (may be elements or other) */
+  for (i = 0; i < num_property_mappings; i++)
+    InitElementSmallImagesScaledUp(property_mapping[i].artwork_index);
+
+  /* initialize special images from above list (non-element images) */
+  for (i = 0; special_graphics[i] > -1; i++)
+    InitElementSmallImagesScaledUp(special_graphics[i]);
+}
+
+void InitScaledImages()
+{
+  int i;
+
+  /* scale normal images from static configuration, if not already scaled */
+  for (i = 0; i < NUM_IMAGE_FILES; i++)
+    ScaleImage(i, graphic_info[i].scale_up_factor);
+}
 
-  /* initialize images from dynamic configuration */
-  for (i=0; i < num_property_mappings; i++)
-    if (property_mapping[i].artwork_index < MAX_NUM_ELEMENTS)
-      CreateImageWithSmallImages(property_mapping[i].artwork_index);
+#if 1
+/* !!! FIX THIS (CHANGE TO USING NORMAL ELEMENT GRAPHIC DEFINITIONS) !!! */
+void SetBitmaps_EM(Bitmap **em_bitmap)
+{
+  em_bitmap[0] = graphic_info[IMG_EMC_OBJECT].bitmap;
+  em_bitmap[1] = graphic_info[IMG_EMC_SPRITE].bitmap;
+}
+#endif
+
+#if 0
+/* !!! FIX THIS (CHANGE TO USING NORMAL ELEMENT GRAPHIC DEFINITIONS) !!! */
+void SetBitmaps_SP(Bitmap **sp_bitmap)
+{
+  *sp_bitmap = graphic_info[IMG_SP_OBJECTS].bitmap;
 }
+#endif
 
 static int getFontBitmapID(int font_nr)
 {
   int special = -1;
 
-  if (game_status >= GAME_MODE_MAIN && game_status <= GAME_MODE_PSEUDO_PREVIEW)
+  /* (special case: do not use special font for GAME_MODE_LOADING) */
+  if (game_status >= GAME_MODE_TITLE_INITIAL &&
+      game_status <= GAME_MODE_PSEUDO_PREVIEW)
     special = game_status;
   else if (game_status == GAME_MODE_PSEUDO_TYPENAME)
     special = GFX_SPECIAL_ARG_MAIN;
+#if 0
   else if (game_status == GAME_MODE_PLAYING)
     special = GFX_SPECIAL_ARG_DOOR;
+#endif
+
+#if 0
+  if (special != -1)
+  {
+    printf("%s%s\n",
+          font_info[font_nr].token_name,
+          special_suffix_info[special].suffix);
+  }
+#endif
 
   if (special != -1)
     return font_info[font_nr].special_bitmap_id[special];
@@ -273,6 +355,26 @@ static int getFontBitmapID(int font_nr)
     return font_nr;
 }
 
+static int getFontFromToken(char *token)
+{
+#if 1
+  char *value = getHashEntry(font_token_hash, token);
+
+  if (value != NULL)
+    return atoi(value);
+#else
+  int i;
+
+  /* !!! OPTIMIZE THIS BY USING HASH !!! */
+  for (i = 0; i < NUM_FONTS; i++)
+    if (strEqual(token, font_info[i].token_name))
+      return i;
+#endif
+
+  /* if font not found, use reliable default value */
+  return FONT_INITIAL_1;
+}
+
 void InitFontGraphicInfo()
 {
   static struct FontBitmapInfo *font_bitmap_info = NULL;
@@ -283,7 +385,8 @@ void InitFontGraphicInfo()
 
   if (graphic_info == NULL)            /* still at startup phase */
   {
-    InitFontInfo(font_initial, NUM_INITIAL_FONTS, getFontBitmapID);
+    InitFontInfo(font_initial, NUM_INITIAL_FONTS,
+                getFontBitmapID, getFontFromToken);
 
     return;
   }
@@ -291,11 +394,11 @@ void InitFontGraphicInfo()
   /* ---------- initialize font graphic definitions ---------- */
 
   /* always start with reliable default values (normal font graphics) */
-  for (i=0; i < NUM_FONTS; i++)
-    font_info[i].graphic = FONT_INITIAL_1;
+  for (i = 0; i < NUM_FONTS; i++)
+    font_info[i].graphic = IMG_FONT_INITIAL_1;
 
   /* initialize normal font/graphic mapping from static configuration */
-  for (i=0; font_to_graphic[i].font_nr > -1; i++)
+  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;
@@ -308,9 +411,9 @@ void InitFontGraphicInfo()
   }
 
   /* always start with reliable default values (special font graphics) */
-  for (i=0; i < NUM_FONTS; i++)
+  for (i = 0; i < NUM_FONTS; i++)
   {
-    for (j=0; j < NUM_SPECIAL_GFX_ARGS; j++)
+    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;
@@ -318,22 +421,38 @@ void InitFontGraphicInfo()
   }
 
   /* initialize special font/graphic mapping from static configuration */
-  for (i=0; font_to_graphic[i].font_nr > -1; i++)
+  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;
+    int font_nr      = font_to_graphic[i].font_nr;
+    int special      = font_to_graphic[i].special;
+    int graphic      = font_to_graphic[i].graphic;
+    int base_graphic = font2baseimg(font_nr);
 
-    if (special >= 0 && special < NUM_SPECIAL_GFX_ARGS)
+    if (IS_SPECIAL_GFX_ARG(special))
     {
+      boolean base_redefined =
+       getImageListEntryFromImageID(base_graphic)->redefined;
+      boolean special_redefined =
+       getImageListEntryFromImageID(graphic)->redefined;
+      boolean special_cloned = (graphic_info[graphic].clone_from != -1);
+
+      /* if the base font ("font.title_1", for example) has been redefined,
+        but not the special font ("font.title_1.LEVELS", for example), do not
+        use an existing (in this case considered obsolete) special font
+        anymore, but use the automatically determined default font */
+      /* special case: cloned special fonts must be explicitly redefined,
+        but are not automatically redefined by redefining base font */
+      if (base_redefined && !special_redefined && !special_cloned)
+       continue;
+
       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++)
+  /* initialize special font/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;
@@ -342,7 +461,7 @@ void InitFontGraphicInfo()
     if (font_nr < 0)
       continue;
 
-    if (special >= 0 && special < NUM_SPECIAL_GFX_ARGS)
+    if (IS_SPECIAL_GFX_ARG(special))
     {
       font_info[font_nr].special_graphic[special] = graphic;
       font_info[font_nr].special_bitmap_id[special] = num_font_bitmaps;
@@ -350,6 +469,93 @@ void InitFontGraphicInfo()
     }
   }
 
+  /* correct special font/graphic mapping for cloned fonts for downwards
+     compatibility of PREVIEW fonts -- this is only needed for implicit
+     redefinition of special font by redefined base font, and only if other
+     fonts are cloned from this special font (like in the "Zelda" level set) */
+  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 (IS_SPECIAL_GFX_ARG(special))
+    {
+      boolean special_redefined =
+       getImageListEntryFromImageID(graphic)->redefined;
+      boolean special_cloned = (graphic_info[graphic].clone_from != -1);
+
+      if (special_cloned && !special_redefined)
+      {
+       int j;
+
+       for (j = 0; font_to_graphic[j].font_nr > -1; j++)
+       {
+         int font_nr2 = font_to_graphic[j].font_nr;
+         int special2 = font_to_graphic[j].special;
+         int graphic2 = font_to_graphic[j].graphic;
+
+         if (IS_SPECIAL_GFX_ARG(special2) &&
+             graphic2 == graphic_info[graphic].clone_from)
+         {
+           font_info[font_nr].special_graphic[special] =
+             font_info[font_nr2].special_graphic[special2];
+           font_info[font_nr].special_bitmap_id[special] =
+             font_info[font_nr2].special_bitmap_id[special2];
+         }
+       }
+      }
+    }
+  }
+
+  /* reset non-redefined ".active" font graphics if normal font is redefined */
+  /* (this different treatment is needed because normal and active fonts are
+     independently defined ("active" is not a property of font definitions!) */
+  for (i = 0; i < NUM_FONTS; i++)
+  {
+    int font_nr_base = i;
+    int font_nr_active = FONT_ACTIVE(font_nr_base);
+
+    /* check only those fonts with exist as normal and ".active" variant */
+    if (font_nr_base != font_nr_active)
+    {
+      int base_graphic = font_info[font_nr_base].graphic;
+      int active_graphic = font_info[font_nr_active].graphic;
+      boolean base_redefined =
+       getImageListEntryFromImageID(base_graphic)->redefined;
+      boolean active_redefined =
+       getImageListEntryFromImageID(active_graphic)->redefined;
+
+      /* if the base font ("font.menu_1", for example) has been redefined,
+        but not the active font ("font.menu_1.active", for example), do not
+        use an existing (in this case considered obsolete) active font
+        anymore, but use the automatically determined default font */
+      if (base_redefined && !active_redefined)
+       font_info[font_nr_active].graphic = base_graphic;
+
+      /* now also check each "special" font (which may be the same as above) */
+      for (j = 0; j < NUM_SPECIAL_GFX_ARGS; j++)
+      {
+       int base_graphic = font_info[font_nr_base].special_graphic[j];
+       int active_graphic = font_info[font_nr_active].special_graphic[j];
+       boolean base_redefined =
+         getImageListEntryFromImageID(base_graphic)->redefined;
+       boolean active_redefined =
+         getImageListEntryFromImageID(active_graphic)->redefined;
+
+       /* same as above, but check special graphic definitions, for example:
+          redefined "font.menu_1.MAIN" invalidates "font.menu_1.active.MAIN" */
+       if (base_redefined && !active_redefined)
+       {
+         font_info[font_nr_active].special_graphic[j] =
+           font_info[font_nr_base].special_graphic[j];
+         font_info[font_nr_active].special_bitmap_id[j] =
+           font_info[font_nr_base].special_bitmap_id[j];
+       }
+      }
+    }
+  }
+
   /* ---------- initialize font bitmap array ---------- */
 
   if (font_bitmap_info != NULL)
@@ -360,7 +566,7 @@ void InitFontGraphicInfo()
 
   /* ---------- initialize font bitmap definitions ---------- */
 
-  for (i=0; i < NUM_FONTS; i++)
+  for (i = 0; i < NUM_FONTS; i++)
   {
     if (i < NUM_INITIAL_FONTS)
     {
@@ -368,7 +574,7 @@ void InitFontGraphicInfo()
       continue;
     }
 
-    for (j=0; j < NUM_SPECIAL_GFX_ARGS; j++)
+    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];
@@ -377,7 +583,7 @@ void InitFontGraphicInfo()
       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;
+       graphic_info[graphic].anim_frames_per_line = DEFAULT_NUM_CHARS_PER_LINE;
       }
 
       /* copy font relevant information from graphics information */
@@ -386,8 +592,11 @@ void InitFontGraphicInfo()
       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].draw_xoffset =
+       graphic_info[graphic].draw_xoffset;
+      font_bitmap_info[font_bitmap_id].draw_yoffset =
+       graphic_info[graphic].draw_yoffset;
 
       font_bitmap_info[font_bitmap_id].num_chars =
        graphic_info[graphic].anim_frames;
@@ -396,7 +605,8 @@ void InitFontGraphicInfo()
     }
   }
 
-  InitFontInfo(font_bitmap_info, num_font_bitmaps, getFontBitmapID);
+  InitFontInfo(font_bitmap_info, num_font_bitmaps,
+              getFontBitmapID, getFontFromToken);
 }
 
 void InitElementGraphicInfo()
@@ -405,33 +615,47 @@ void InitElementGraphicInfo()
   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 (i = 0; i < MAX_NUM_ELEMENTS; i++)
   {
-    for (act=0; act<NUM_ACTIONS; act++)
+    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++)
+      for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
+      {
        element_info[i].direction_graphic[act][dir] = -1;
+       element_info[i].direction_crumbled[act][dir] = -1;
+      }
     }
   }
 
+  UPDATE_BUSY_STATE();
+
   /* initialize normal element/graphic mapping from static configuration */
-  for (i=0; element_to_graphic[i].element > -1; i++)
+  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;
-    int graphic   = element_to_graphic[i].graphic;
+    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) && el2img(element) != -1)
+    if ((action > -1 || direction > -1 || crumbled == TRUE) &&
+       base_graphic != -1)
     {
-      boolean base_redefined = getImageListEntry(el2img(element))->redefined;
-      boolean act_dir_redefined = getImageListEntry(graphic)->redefined;
+      boolean base_redefined =
+       getImageListEntryFromImageID(base_graphic)->redefined;
+      boolean act_dir_redefined =
+       getImageListEntryFromImageID(graphic)->redefined;
 
       /* if the base graphic ("emerald", for example) has been redefined,
         but not the action graphic ("emerald.falling", for example), do not
@@ -444,20 +668,47 @@ void InitElementGraphicInfo()
     if (action < 0)
       action = ACTION_DEFAULT;
 
-    if (direction > -1)
-      element_info[element].direction_graphic[action][direction] = graphic;
+    if (crumbled)
+    {
+      if (direction > -1)
+       element_info[element].direction_crumbled[action][direction] = graphic;
+      else
+       element_info[element].crumbled[action] = graphic;
+    }
     else
-      element_info[element].graphic[action] = graphic;
+    {
+      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++)
+  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 0
+    if ((element == EL_EM_DYNAMITE ||
+        element == EL_EM_DYNAMITE_ACTIVE) &&
+       action == ACTION_ACTIVE &&
+       (special == GFX_SPECIAL_ARG_EDITOR ||
+        special == GFX_SPECIAL_ARG_PANEL))
+      printf("::: DYNAMIC: %d, %d, %d -> %d\n",
+            element, action, special, graphic);
+#endif
+
+    if (special == GFX_SPECIAL_ARG_CRUMBLED)
+    {
+      special = -1;
+      crumbled = TRUE;
+    }
 
     if (graphic_info[graphic].bitmap == NULL)
       continue;
@@ -468,69 +719,360 @@ void InitElementGraphicInfo()
     if (action < 0)
       action = ACTION_DEFAULT;
 
-    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;
+    if (crumbled)
+    {
+      if (direction < 0)
+       for (dir = 0; dir < NUM_DIRECTIONS_FULL; 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
-      element_info[element].graphic[action] = graphic;
+    {
+      if (direction < 0)
+       for (dir = 0; dir < NUM_DIRECTIONS_FULL; 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_FULL; 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_FULL; 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
+  /* set hardcoded definitions for some internal elements without graphic */
+  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+  {
+    if (IS_EDITOR_CASCADE_INACTIVE(i))
+      element_info[i].graphic[ACTION_DEFAULT] = IMG_EDITOR_CASCADE_LIST;
+    else if (IS_EDITOR_CASCADE_ACTIVE(i))
+      element_info[i].graphic[ACTION_DEFAULT] = IMG_EDITOR_CASCADE_LIST_ACTIVE;
+  }
+#endif
+
+  /* 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_FULL; 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;
+      }
+    }
+  }
+
+  UPDATE_BUSY_STATE();
+
+  /* adjust graphics with 2nd tile for movement according to direction
+     (do this before correcting '-1' values to minimize calculations) */
+  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+  {
+    for (act = 0; act < NUM_ACTIONS; act++)
+    {
+      for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
+      {
+       int graphic = element_info[i].direction_graphic[act][dir];
+       int move_dir = (act == ACTION_FALLING ? MV_BIT_DOWN : dir);
+
+       if (act == ACTION_FALLING)      /* special case */
+         graphic = element_info[i].graphic[act];
+
+       if (graphic != -1 &&
+           graphic_info[graphic].double_movement &&
+           graphic_info[graphic].swap_double_tiles != 0)
+       {
+         struct GraphicInfo *g = &graphic_info[graphic];
+         int src_x_front = g->src_x;
+         int src_y_front = g->src_y;
+         int src_x_back = g->src_x + g->offset2_x;
+         int src_y_back = g->src_y + g->offset2_y;
+         boolean frames_are_ordered_diagonally = (g->offset_x != 0 &&
+                                                  g->offset_y != 0);
+         boolean front_is_left_or_upper = (src_x_front < src_x_back ||
+                                           src_y_front < src_y_back);
+         boolean swap_movement_tiles_always = (g->swap_double_tiles == 1);
+         boolean swap_movement_tiles_autodetected =
+           (!frames_are_ordered_diagonally &&
+            ((move_dir == MV_BIT_LEFT  && !front_is_left_or_upper) ||
+             (move_dir == MV_BIT_UP    && !front_is_left_or_upper) ||
+             (move_dir == MV_BIT_RIGHT &&  front_is_left_or_upper) ||
+             (move_dir == MV_BIT_DOWN  &&  front_is_left_or_upper)));
+         Bitmap *dummy;
+
+         /* swap frontside and backside graphic tile coordinates, if needed */
+         if (swap_movement_tiles_always || swap_movement_tiles_autodetected)
+         {
+           /* get current (wrong) backside tile coordinates */
+           getFixedGraphicSourceExt(graphic, 0, &dummy,
+                                    &src_x_back, &src_y_back, TRUE);
+
+           /* set frontside tile coordinates to backside tile coordinates */
+           g->src_x = src_x_back;
+           g->src_y = src_y_back;
+
+           /* invert tile offset to point to new backside tile coordinates */
+           g->offset2_x *= -1;
+           g->offset2_y *= -1;
+
+           /* do not swap front and backside tiles again after correction */
+           g->swap_double_tiles = 0;
+         }
+       }
+      }
+    }
   }
 
+  UPDATE_BUSY_STATE();
+
   /* now set all '-1' values to element specific default values */
-  for (i=0; i<MAX_NUM_ELEMENTS; i++)
+  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
   {
-    int default_action_graphic = element_info[i].graphic[ACTION_DEFAULT];
-    int default_action_direction_graphic[NUM_DIRECTIONS];
+    int default_graphic = element_info[i].graphic[ACTION_DEFAULT];
+    int default_crumbled = element_info[i].crumbled[ACTION_DEFAULT];
+    int default_direction_graphic[NUM_DIRECTIONS_FULL];
+    int default_direction_crumbled[NUM_DIRECTIONS_FULL];
 
-    if (default_action_graphic == -1)
-      default_action_graphic = IMG_CHAR_QUESTION;
+    if (default_graphic == -1)
+      default_graphic = IMG_UNKNOWN;
+#if 1
+    if (default_crumbled == -1)
+      default_crumbled = default_graphic;
+#else
+    /* !!! THIS LOOKS CRAPPY FOR SAND ETC. WITHOUT CRUMBLED GRAPHICS !!! */
+    if (default_crumbled == -1)
+      default_crumbled = IMG_EMPTY;
+#endif
 
-    for (dir=0; dir<NUM_DIRECTIONS; dir++)
+    for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
     {
-      default_action_direction_graphic[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_action_direction_graphic[dir] == -1)
-       default_action_direction_graphic[dir] = default_action_graphic;
+      if (default_direction_graphic[dir] == -1)
+       default_direction_graphic[dir] = default_graphic;
+#if 1
+      if (default_direction_crumbled[dir] == -1)
+       default_direction_crumbled[dir] = default_direction_graphic[dir];
+#else
+      /* !!! THIS LOOKS CRAPPY FOR SAND ETC. WITHOUT CRUMBLED GRAPHICS !!! */
+      if (default_direction_crumbled[dir] == -1)
+       default_direction_crumbled[dir] = default_crumbled;
+#endif
     }
 
-    for (act=0; act<NUM_ACTIONS; act++)
+    for (act = 0; act < NUM_ACTIONS; act++)
     {
-      boolean act_remove = (act == ACTION_DIGGING ||
-                           act == ACTION_SNAPPING ||
-                           act == ACTION_COLLECTING);
+      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];
+      int default_remove_graphic = IMG_EMPTY;
+
+      if (act_remove && default_action_graphic != -1)
+       default_remove_graphic = default_action_graphic;
+
+      /* 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 1
+      if (default_action_crumbled == -1)
+       default_action_crumbled = default_action_graphic;
+#else
+      /* !!! THIS LOOKS CRAPPY FOR SAND ETC. WITHOUT CRUMBLED GRAPHICS !!! */
+      if (default_action_crumbled == -1)
+       default_action_crumbled = default_crumbled;
+#endif
 
-      for (dir=0; dir<NUM_DIRECTIONS; dir++)
+      for (dir = 0; dir < NUM_DIRECTIONS_FULL; dir++)
       {
-       int default_direction_graphic = element_info[i].graphic[act];
+       /* use action graphic as the default direction graphic, if undefined */
+       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_direction_graphic == -1)
-         default_direction_graphic =
-           (act_remove ? IMG_EMPTY : default_action_direction_graphic[dir]);
+       if (default_action_direction_graphic == -1)
+         default_action_direction_graphic =
+           (act_remove ? default_remove_graphic :
+            act_turning ?
+            element_info[i].direction_graphic[ACTION_TURNING][dir] :
+            default_action_graphic != default_graphic ?
+            default_action_graphic :
+            default_direction_graphic[dir]);
 
        if (element_info[i].direction_graphic[act][dir] == -1)
          element_info[i].direction_graphic[act][dir] =
-           default_direction_graphic;
+           default_action_direction_graphic;
+
+#if 1
+       if (default_action_direction_crumbled == -1)
+         default_action_direction_crumbled =
+           element_info[i].direction_graphic[act][dir];
+#else
+       if (default_action_direction_crumbled == -1)
+         default_action_direction_crumbled =
+           (act_remove ? default_remove_graphic :
+            act_turning ?
+            element_info[i].direction_crumbled[ACTION_TURNING][dir] :
+            default_action_crumbled != default_crumbled ?
+            default_action_crumbled :
+            default_direction_crumbled[dir]);
+#endif
+
+       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 : default_action_graphic);
+         (act_remove ? default_remove_graphic :
+          act_turning ? element_info[i].graphic[ACTION_TURNING] :
+          default_action_graphic);
+#if 1
+      if (element_info[i].crumbled[act] == -1)
+       element_info[i].crumbled[act] = element_info[i].graphic[act];
+#else
+      if (element_info[i].crumbled[act] == -1)
+       element_info[i].crumbled[act] =
+         (act_remove ? default_remove_graphic :
+          act_turning ? element_info[i].crumbled[ACTION_TURNING] :
+          default_action_crumbled);
+#endif
+    }
+  }
+
+  UPDATE_BUSY_STATE();
+
+#if 0
+  /* !!! THIS ALSO CLEARS SPECIAL FLAGS (AND IS NOT NEEDED ANYWAY) !!! */
+  /* 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_FULL; 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_CHAR_QUESTION &&
-         i != EL_CHAR_QUESTION)
-       Error(ERR_RETURN, "warning: no graphic for element '%s' (%d)",
+    for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+      if (element_info[i].graphic[ACTION_DEFAULT] == IMG_UNKNOWN &&
+         i != EL_UNKNOWN)
+       Error(ERR_INFO, "warning: no graphic for element '%s' (%d)",
              element_info[i].token_name, i);
   }
 #endif
@@ -544,21 +1086,33 @@ void InitElementSpecialGraphicInfo()
   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++)
+  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++)
+  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;
-    boolean base_redefined = getImageListEntry(el2img(element))->redefined;
-    boolean special_redefined = getImageListEntry(graphic)->redefined;
+    int base_graphic = el2baseimg(element);
+    boolean base_redefined =
+      getImageListEntryFromImageID(base_graphic)->redefined;
+    boolean special_redefined =
+      getImageListEntryFromImageID(graphic)->redefined;
 
-    /* if the base graphic ("emerald", for example) has been redefined,
+#if 0
+    if ((element == EL_EM_DYNAMITE ||
+        element == EL_EM_DYNAMITE_ACTIVE) &&
+       (special == GFX_SPECIAL_ARG_EDITOR ||
+        special == GFX_SPECIAL_ARG_PANEL))
+      printf("::: SPECIAL STATIC: %d, %d -> %d\n",
+            element, special, graphic);
+#endif
+
+    /* 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 */
@@ -569,43 +1123,503 @@ void InitElementSpecialGraphicInfo()
   }
 
   /* initialize special element/graphic mapping from dynamic configuration */
-  for (i=0; i < num_property_mappings; i++)
+  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;
+    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;
+
+#if 0
+    if ((element == EL_EM_DYNAMITE ||
+        element == EL_EM_DYNAMITE_ACTIVE ||
+        element == EL_CONVEYOR_BELT_1_MIDDLE ||
+        element == EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE) &&
+       (special == GFX_SPECIAL_ARG_EDITOR ||
+        special == GFX_SPECIAL_ARG_PANEL))
+      printf("::: SPECIAL DYNAMIC: %d, %d -> %d [%d]\n",
+            element, special, graphic, property_mapping[i].ext1_index);
+#endif
+
+#if 0
+    if (element == EL_CONVEYOR_BELT_1_MIDDLE &&
+       action == ACTION_ACTIVE)
+    {
+      element = EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE;
+      action = -1;
+    }
+#endif
+
+#if 0
+    if (element == EL_MAGIC_WALL &&
+       action == ACTION_ACTIVE)
+    {
+      element = EL_MAGIC_WALL_ACTIVE;
+      action = -1;
+    }
+#endif
+
+#if 1
+    /* for action ".active", replace element with active element, if exists */
+    if (action == ACTION_ACTIVE && element != ELEMENT_ACTIVE(element))
+    {
+      element = ELEMENT_ACTIVE(element);
+      action = -1;
+    }
+#endif
 
     if (element >= MAX_NUM_ELEMENTS)
       continue;
 
-    if (special >= 0 && special < NUM_SPECIAL_GFX_ARGS)
+    /* do not change special graphic if action or direction was specified */
+    if (action != -1 || direction != -1)
+      continue;
+
+    if (IS_SPECIAL_GFX_ARG(special))
       element_info[element].special_graphic[special] = graphic;
   }
+
+  /* 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];
+}
+
+static int get_graphic_parameter_value(char *value_raw, char *suffix, int type)
+{
+  if (type != TYPE_ELEMENT && type != TYPE_GRAPHIC)
+    return get_parameter_value(value_raw, suffix, type);
+
+  if (strEqual(value_raw, ARG_UNDEFINED))
+    return ARG_UNDEFINED_VALUE;
+
+#if 1
+  if (type == TYPE_ELEMENT)
+  {
+    char *value = getHashEntry(element_token_hash, value_raw);
+
+    return (value != NULL ? atoi(value) : EL_UNDEFINED);
+  }
+  else if (type == TYPE_GRAPHIC)
+  {
+    char *value = getHashEntry(graphic_token_hash, value_raw);
+
+    return (value != NULL ? atoi(value) : IMG_UNDEFINED);
+  }
+
+#else
+
+  int i;
+  int x = 0;
+
+  /* !!! THIS IS BUGGY !!! NOT SURE IF YOU GET ELEMENT ID OR GRAPHIC ID !!! */
+  /* !!! (possible reason why ".clone_from" with elements doesn't work) !!! */
+
+  /* !!! OPTIMIZE THIS BY USING HASH !!! */
+  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+    if (strEqual(element_info[i].token_name, value_raw))
+      return i;
+
+  /* !!! OPTIMIZE THIS BY USING HASH !!! */
+  for (i = 0; image_config[i].token != NULL; i++)
+  {
+    int len_config_value = strlen(image_config[i].value);
+
+    if (!strEqual(&image_config[i].value[len_config_value - 4], ".pcx") &&
+       !strEqual(&image_config[i].value[len_config_value - 4], ".wav") &&
+       !strEqual(image_config[i].value, UNDEFINED_FILENAME))
+      continue;
+
+    if (strEqual(image_config[i].token, value_raw))
+      return x;
+
+    x++;
+  }
+#endif
+
+  return -1;
+}
+
+static int get_scaled_graphic_width(int graphic)
+{
+  int original_width = getOriginalImageWidthFromImageID(graphic);
+  int scale_up_factor = graphic_info[graphic].scale_up_factor;
+
+  return original_width * scale_up_factor;
+}
+
+static int get_scaled_graphic_height(int graphic)
+{
+  int original_height = getOriginalImageHeightFromImageID(graphic);
+  int scale_up_factor = graphic_info[graphic].scale_up_factor;
+
+  return original_height * scale_up_factor;
+}
+
+static void set_graphic_parameters_ext(int graphic, int *parameter,
+                                      Bitmap *src_bitmap)
+{
+  struct GraphicInfo *g = &graphic_info[graphic];
+  int anim_frames_per_row = 1, anim_frames_per_col = 1;
+  int anim_frames_per_line = 1;
+
+  /* always start with reliable default values */
+  g->src_image_width = 0;
+  g->src_image_height = 0;
+  g->src_x = 0;
+  g->src_y = 0;
+  g->width  = TILEX;                   /* default for element graphics */
+  g->height = TILEY;                   /* default for element graphics */
+  g->offset_x = 0;                     /* one or both of these values ... */
+  g->offset_y = 0;                     /* ... will be corrected later */
+  g->offset2_x = 0;                    /* one or both of these values ... */
+  g->offset2_y = 0;                    /* ... will be corrected later */
+  g->swap_double_tiles = -1;           /* auto-detect tile swapping */
+  g->crumbled_like = -1;               /* do not use clone element */
+  g->diggable_like = -1;               /* do not use clone element */
+  g->border_size = TILEX / 8;          /* "CRUMBLED" border size */
+  g->scale_up_factor = 1;              /* default: no scaling up */
+  g->clone_from = -1;                  /* do not use clone graphic */
+  g->anim_delay_fixed = 0;
+  g->anim_delay_random = 0;
+  g->post_delay_fixed = 0;
+  g->post_delay_random = 0;
+  g->fade_mode = FADE_MODE_DEFAULT;
+  g->fade_delay = -1;
+  g->post_delay = -1;
+  g->auto_delay = -1;
+  g->align = ALIGN_CENTER;             /* default for title screens */
+  g->valign = VALIGN_MIDDLE;           /* default for title screens */
+  g->sort_priority = 0;                        /* default for title screens */
+  g->class = 0;
+  g->style = STYLE_DEFAULT;
+
+  g->bitmap = src_bitmap;
+
+#if 1
+  /* optional zoom factor for scaling up the image to a larger size */
+  if (parameter[GFX_ARG_SCALE_UP_FACTOR] != ARG_UNDEFINED_VALUE)
+    g->scale_up_factor = parameter[GFX_ARG_SCALE_UP_FACTOR];
+  if (g->scale_up_factor < 1)
+    g->scale_up_factor = 1;            /* no scaling */
+#endif
+
+#if 1
+  if (g->use_image_size)
+  {
+    /* set new default bitmap size (with scaling, but without small images) */
+    g->width  = get_scaled_graphic_width(graphic);
+    g->height = get_scaled_graphic_height(graphic);
+  }
+#endif
+
+  /* optional x and y tile position of animation frame sequence */
+  if (parameter[GFX_ARG_XPOS] != ARG_UNDEFINED_VALUE)
+    g->src_x = parameter[GFX_ARG_XPOS] * TILEX;
+  if (parameter[GFX_ARG_YPOS] != ARG_UNDEFINED_VALUE)
+    g->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)
+    g->src_x = parameter[GFX_ARG_X];
+  if (parameter[GFX_ARG_Y] != ARG_UNDEFINED_VALUE)
+    g->src_y = parameter[GFX_ARG_Y];
+
+  /* optional width and height of each animation frame */
+  if (parameter[GFX_ARG_WIDTH] != ARG_UNDEFINED_VALUE)
+    g->width = parameter[GFX_ARG_WIDTH];
+  if (parameter[GFX_ARG_HEIGHT] != ARG_UNDEFINED_VALUE)
+    g->height = parameter[GFX_ARG_HEIGHT];
+
+  if (src_bitmap)
+  {
+    if (g->width <= 0)
+    {
+      Error(ERR_INFO_LINE, "-");
+      Error(ERR_WARN, "invalid value %d for '%s.width' (fallback done to %d)",
+           g->width, getTokenFromImageID(graphic), TILEX);
+      Error(ERR_INFO_LINE, "-");
+
+      g->width = TILEX;                /* will be checked to be inside bitmap later */
+    }
+
+    if (g->height <= 0)
+    {
+      Error(ERR_INFO_LINE, "-");
+      Error(ERR_WARN, "invalid value %d for '%s.height' (fallback done to %d)",
+           g->height, getTokenFromImageID(graphic), TILEY);
+      Error(ERR_INFO_LINE, "-");
+
+      g->height = TILEY;       /* will be checked to be inside bitmap later */
+    }
+  }
+
+#if 0
+  /* optional zoom factor for scaling up the image to a larger size */
+  if (parameter[GFX_ARG_SCALE_UP_FACTOR] != ARG_UNDEFINED_VALUE)
+    g->scale_up_factor = parameter[GFX_ARG_SCALE_UP_FACTOR];
+  if (g->scale_up_factor < 1)
+    g->scale_up_factor = 1;            /* no scaling */
+#endif
+
+  if (src_bitmap)
+  {
+    /* get final bitmap size (with scaling, but without small images) */
+    int src_image_width  = get_scaled_graphic_width(graphic);
+    int src_image_height = get_scaled_graphic_height(graphic);
+
+    if (src_image_width == 0 || src_image_height == 0)
+    {
+      /* only happens when loaded outside artwork system (like "global.busy") */
+      src_image_width  = src_bitmap->width;
+      src_image_height = src_bitmap->height;
+    }
+
+    anim_frames_per_row = src_image_width  / g->width;
+    anim_frames_per_col = src_image_height / g->height;
+
+    g->src_image_width  = src_image_width;
+    g->src_image_height = src_image_height;
+  }
+
+  /* correct x or y offset dependent of vertical or horizontal frame order */
+  if (parameter[GFX_ARG_VERTICAL])     /* frames are ordered vertically */
+  {
+    g->offset_y = (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
+                  parameter[GFX_ARG_OFFSET] : g->height);
+    anim_frames_per_line = anim_frames_per_col;
+  }
+  else                                 /* frames are ordered horizontally */
+  {
+    g->offset_x = (parameter[GFX_ARG_OFFSET] != ARG_UNDEFINED_VALUE ?
+                  parameter[GFX_ARG_OFFSET] : g->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)
+    g->offset_x = parameter[GFX_ARG_XOFFSET];
+  if (parameter[GFX_ARG_YOFFSET] != ARG_UNDEFINED_VALUE)
+    g->offset_y = parameter[GFX_ARG_YOFFSET];
+
+  /* optionally, moving animations may have separate start and end graphics */
+  g->double_movement = parameter[GFX_ARG_2ND_MOVEMENT_TILE];
+
+  if (parameter[GFX_ARG_2ND_VERTICAL] == ARG_UNDEFINED_VALUE)
+    parameter[GFX_ARG_2ND_VERTICAL] = !parameter[GFX_ARG_VERTICAL];
+
+  /* correct x or y offset2 dependent of vertical or horizontal frame order */
+  if (parameter[GFX_ARG_2ND_VERTICAL]) /* frames are ordered vertically */
+    g->offset2_y = (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
+                   parameter[GFX_ARG_2ND_OFFSET] : g->height);
+  else                                 /* frames are ordered horizontally */
+    g->offset2_x = (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
+                   parameter[GFX_ARG_2ND_OFFSET] : g->width);
+
+  /* optionally, the x and y offset of 2nd graphic can be specified directly */
+  if (parameter[GFX_ARG_2ND_XOFFSET] != ARG_UNDEFINED_VALUE)
+    g->offset2_x = parameter[GFX_ARG_2ND_XOFFSET];
+  if (parameter[GFX_ARG_2ND_YOFFSET] != ARG_UNDEFINED_VALUE)
+    g->offset2_y = parameter[GFX_ARG_2ND_YOFFSET];
+
+  /* optionally, the second movement tile can be specified as start tile */
+  if (parameter[GFX_ARG_2ND_SWAP_TILES] != ARG_UNDEFINED_VALUE)
+    g->swap_double_tiles= parameter[GFX_ARG_2ND_SWAP_TILES];
+
+  /* automatically determine correct number of frames, if not defined */
+  if (parameter[GFX_ARG_FRAMES] != ARG_UNDEFINED_VALUE)
+    g->anim_frames = parameter[GFX_ARG_FRAMES];
+  else if (parameter[GFX_ARG_XPOS] == 0 && !parameter[GFX_ARG_VERTICAL])
+    g->anim_frames = anim_frames_per_row;
+  else if (parameter[GFX_ARG_YPOS] == 0 && parameter[GFX_ARG_VERTICAL])
+    g->anim_frames = anim_frames_per_col;
+  else
+    g->anim_frames = 1;
+
+  if (g->anim_frames == 0)             /* frames must be at least 1 */
+    g->anim_frames = 1;
+
+  g->anim_frames_per_line =
+    (parameter[GFX_ARG_FRAMES_PER_LINE] != ARG_UNDEFINED_VALUE ?
+     parameter[GFX_ARG_FRAMES_PER_LINE] : anim_frames_per_line);
+
+  g->anim_delay = parameter[GFX_ARG_DELAY];
+  if (g->anim_delay == 0)              /* delay must be at least 1 */
+    g->anim_delay = 1;
+
+  g->anim_mode = parameter[GFX_ARG_ANIM_MODE];
+#if 0
+  if (g->anim_frames == 1)
+    g->anim_mode = ANIM_NONE;
+#endif
+
+  /* automatically determine correct start frame, if not defined */
+  if (parameter[GFX_ARG_START_FRAME] == ARG_UNDEFINED_VALUE)
+    g->anim_start_frame = 0;
+  else if (g->anim_mode & ANIM_REVERSE)
+    g->anim_start_frame = g->anim_frames - parameter[GFX_ARG_START_FRAME] - 1;
+  else
+    g->anim_start_frame = parameter[GFX_ARG_START_FRAME];
+
+  /* animation synchronized with global frame counter, not move position */
+  g->anim_global_sync = parameter[GFX_ARG_GLOBAL_SYNC];
+
+  /* optional element for cloning crumble graphics */
+  if (parameter[GFX_ARG_CRUMBLED_LIKE] != ARG_UNDEFINED_VALUE)
+    g->crumbled_like = parameter[GFX_ARG_CRUMBLED_LIKE];
+
+  /* optional element for cloning digging graphics */
+  if (parameter[GFX_ARG_DIGGABLE_LIKE] != ARG_UNDEFINED_VALUE)
+    g->diggable_like = parameter[GFX_ARG_DIGGABLE_LIKE];
+
+  /* optional border size for "crumbling" diggable graphics */
+  if (parameter[GFX_ARG_BORDER_SIZE] != ARG_UNDEFINED_VALUE)
+    g->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)
+    g->anim_delay_fixed = parameter[GFX_ARG_ANIM_DELAY_FIXED];
+  if (parameter[GFX_ARG_ANIM_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
+    g->anim_delay_random = parameter[GFX_ARG_ANIM_DELAY_RANDOM];
+  if (parameter[GFX_ARG_POST_DELAY_FIXED] != ARG_UNDEFINED_VALUE)
+    g->post_delay_fixed = parameter[GFX_ARG_POST_DELAY_FIXED];
+  if (parameter[GFX_ARG_POST_DELAY_RANDOM] != ARG_UNDEFINED_VALUE)
+    g->post_delay_random = parameter[GFX_ARG_POST_DELAY_RANDOM];
+
+  /* this is only used for toon animations */
+  g->step_offset = parameter[GFX_ARG_STEP_OFFSET];
+  g->step_delay  = parameter[GFX_ARG_STEP_DELAY];
+
+  /* this is only used for drawing font characters */
+  g->draw_xoffset = parameter[GFX_ARG_DRAW_XOFFSET];
+  g->draw_yoffset = parameter[GFX_ARG_DRAW_YOFFSET];
+
+  /* this is only used for drawing envelope graphics */
+  g->draw_masked = parameter[GFX_ARG_DRAW_MASKED];
+
+  /* optional graphic for cloning all graphics settings */
+  if (parameter[GFX_ARG_CLONE_FROM] != ARG_UNDEFINED_VALUE)
+    g->clone_from = parameter[GFX_ARG_CLONE_FROM];
+
+  /* optional settings for drawing title screens and title messages */
+  if (parameter[GFX_ARG_FADE_MODE] != ARG_UNDEFINED_VALUE)
+    g->fade_mode = parameter[GFX_ARG_FADE_MODE];
+  if (parameter[GFX_ARG_FADE_DELAY] != ARG_UNDEFINED_VALUE)
+    g->fade_delay = parameter[GFX_ARG_FADE_DELAY];
+  if (parameter[GFX_ARG_POST_DELAY] != ARG_UNDEFINED_VALUE)
+    g->post_delay = parameter[GFX_ARG_POST_DELAY];
+  if (parameter[GFX_ARG_AUTO_DELAY] != ARG_UNDEFINED_VALUE)
+    g->auto_delay = parameter[GFX_ARG_AUTO_DELAY];
+  if (parameter[GFX_ARG_ALIGN] != ARG_UNDEFINED_VALUE)
+    g->align = parameter[GFX_ARG_ALIGN];
+  if (parameter[GFX_ARG_VALIGN] != ARG_UNDEFINED_VALUE)
+    g->valign = parameter[GFX_ARG_VALIGN];
+  if (parameter[GFX_ARG_SORT_PRIORITY] != ARG_UNDEFINED_VALUE)
+    g->sort_priority = parameter[GFX_ARG_SORT_PRIORITY];
+
+  if (parameter[GFX_ARG_CLASS] != ARG_UNDEFINED_VALUE)
+    g->class = parameter[GFX_ARG_CLASS];
+  if (parameter[GFX_ARG_STYLE] != ARG_UNDEFINED_VALUE)
+    g->style = parameter[GFX_ARG_STYLE];
+
+  /* this is only used for drawing menu buttons and text */
+  g->active_xoffset = parameter[GFX_ARG_ACTIVE_XOFFSET];
+  g->active_yoffset = parameter[GFX_ARG_ACTIVE_YOFFSET];
+  g->pressed_xoffset = parameter[GFX_ARG_PRESSED_XOFFSET];
+  g->pressed_yoffset = parameter[GFX_ARG_PRESSED_YOFFSET];
 }
 
-static void set_graphic_parameters(int graphic, char **parameter_raw)
+static void set_graphic_parameters(int graphic)
 {
+#if 1
+  struct FileInfo *image = getImageListEntryFromImageID(graphic);
+  char **parameter_raw = image->parameter;
+  Bitmap *src_bitmap = getBitmapFromImageID(graphic);
+  int parameter[NUM_GFX_ARGS];
+  int i;
+
+  /* if fallback to default artwork is done, also use the default parameters */
+  if (image->fallback_to_default)
+    parameter_raw = image->default_parameter;
+
+  /* get integer values from string parameters */
+  for (i = 0; i < NUM_GFX_ARGS; i++)
+    parameter[i] = get_graphic_parameter_value(parameter_raw[i],
+                                              image_config_suffix[i].token,
+                                              image_config_suffix[i].type);
+
+  set_graphic_parameters_ext(graphic, parameter, src_bitmap);
+
+#else
+
+  struct FileInfo *image = getImageListEntryFromImageID(graphic);
+  char **parameter_raw = image->parameter;
   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;
 
+  /* if fallback to default artwork is done, also use the default parameters */
+  if (image->fallback_to_default)
+    parameter_raw = image->default_parameter;
+
   /* 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);
+  for (i = 0; i < NUM_GFX_ARGS; i++)
+    parameter[i] = get_graphic_parameter_value(parameter_raw[i],
+                                              image_config_suffix[i].token,
+                                              image_config_suffix[i].type);
 
   graphic_info[graphic].bitmap = src_bitmap;
 
-  /* start with reliable default values */
+  /* always start with reliable default values */
+  graphic_info[graphic].src_image_width = 0;
+  graphic_info[graphic].src_image_height = 0;
   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].width  = TILEX;        /* default for element graphics */
+  graphic_info[graphic].height = TILEY;        /* default for element graphics */
   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].offset2_x = 0; /* one or both of these values ... */
+  graphic_info[graphic].offset2_y = 0; /* ... will be corrected later */
+  graphic_info[graphic].swap_double_tiles = -1;        /* auto-detect tile swapping */
+  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].clone_from = -1;       /* do not use clone graphic */
+  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;
+  graphic_info[graphic].fade_mode = FADE_MODE_DEFAULT;
+  graphic_info[graphic].fade_delay = -1;
+  graphic_info[graphic].post_delay = -1;
+  graphic_info[graphic].auto_delay = -1;
+  graphic_info[graphic].align = ALIGN_CENTER;  /* default for title screens */
+  graphic_info[graphic].valign = VALIGN_MIDDLE;        /* default for title screens */
+  graphic_info[graphic].sort_priority = 0;     /* default for title screens */
+
+#if 1
+  /* 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 */
+#endif
+
+#if 1
+  if (graphic_info[graphic].use_image_size)
+  {
+    /* set new default bitmap size (with scaling, but without small images) */
+    graphic_info[graphic].width  = get_scaled_graphic_width(graphic);
+    graphic_info[graphic].height = get_scaled_graphic_height(graphic);
+  }
+#endif
 
   /* optional x and y tile position of animation frame sequence */
   if (parameter[GFX_ARG_XPOS] != ARG_UNDEFINED_VALUE)
@@ -625,13 +1639,28 @@ static void set_graphic_parameters(int graphic, char **parameter_raw)
   if (parameter[GFX_ARG_HEIGHT] != ARG_UNDEFINED_VALUE)
     graphic_info[graphic].height = parameter[GFX_ARG_HEIGHT];
 
+#if 0
+  /* 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 */
+#endif
+
   if (src_bitmap)
   {
-    anim_frames_per_row = src_bitmap->width  / graphic_info[graphic].width;
-    anim_frames_per_col = src_bitmap->height / graphic_info[graphic].height;
+    /* get final bitmap size (with scaling, but without small images) */
+    int src_image_width  = get_scaled_graphic_width(graphic);
+    int src_image_height = get_scaled_graphic_height(graphic);
+
+    anim_frames_per_row = src_image_width  / graphic_info[graphic].width;
+    anim_frames_per_col = src_image_height / graphic_info[graphic].height;
+
+    graphic_info[graphic].src_image_width  = src_image_width;
+    graphic_info[graphic].src_image_height = src_image_height;
   }
 
-  /* correct x or y offset dependant of vertical or horizontal frame order */
+  /* 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 =
@@ -653,6 +1682,32 @@ static void set_graphic_parameters(int graphic, char **parameter_raw)
   if (parameter[GFX_ARG_YOFFSET] != ARG_UNDEFINED_VALUE)
     graphic_info[graphic].offset_y = parameter[GFX_ARG_YOFFSET];
 
+  /* optionally, moving animations may have separate start and end graphics */
+  graphic_info[graphic].double_movement = parameter[GFX_ARG_2ND_MOVEMENT_TILE];
+
+  if (parameter[GFX_ARG_2ND_VERTICAL] == ARG_UNDEFINED_VALUE)
+    parameter[GFX_ARG_2ND_VERTICAL] = !parameter[GFX_ARG_VERTICAL];
+
+  /* correct x or y offset2 dependent of vertical or horizontal frame order */
+  if (parameter[GFX_ARG_2ND_VERTICAL]) /* frames are ordered vertically */
+    graphic_info[graphic].offset2_y =
+      (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
+       parameter[GFX_ARG_2ND_OFFSET] : graphic_info[graphic].height);
+  else                                 /* frames are ordered horizontally */
+    graphic_info[graphic].offset2_x =
+      (parameter[GFX_ARG_2ND_OFFSET] != ARG_UNDEFINED_VALUE ?
+       parameter[GFX_ARG_2ND_OFFSET] : graphic_info[graphic].width);
+
+  /* optionally, the x and y offset of 2nd graphic can be specified directly */
+  if (parameter[GFX_ARG_2ND_XOFFSET] != ARG_UNDEFINED_VALUE)
+    graphic_info[graphic].offset2_x = parameter[GFX_ARG_2ND_XOFFSET];
+  if (parameter[GFX_ARG_2ND_YOFFSET] != ARG_UNDEFINED_VALUE)
+    graphic_info[graphic].offset2_y = parameter[GFX_ARG_2ND_YOFFSET];
+
+  /* optionally, the second movement tile can be specified as start tile */
+  if (parameter[GFX_ARG_2ND_SWAP_TILES] != ARG_UNDEFINED_VALUE)
+    graphic_info[graphic].swap_double_tiles= parameter[GFX_ARG_2ND_SWAP_TILES];
+
   /* 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];
@@ -663,6 +1718,9 @@ static void set_graphic_parameters(int graphic, char **parameter_raw)
   else
     graphic_info[graphic].anim_frames = 1;
 
+  if (graphic_info[graphic].anim_frames == 0)  /* frames must be at least 1 */
+    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);
@@ -672,8 +1730,10 @@ static void set_graphic_parameters(int graphic, char **parameter_raw)
     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)
@@ -687,20 +1747,109 @@ static void set_graphic_parameters(int graphic, char **parameter_raw)
   /* 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];
+  graphic_info[graphic].draw_xoffset = parameter[GFX_ARG_DRAW_XOFFSET];
+  graphic_info[graphic].draw_yoffset = parameter[GFX_ARG_DRAW_YOFFSET];
+
+  /* this is only used for drawing envelope graphics */
+  graphic_info[graphic].draw_masked = parameter[GFX_ARG_DRAW_MASKED];
+
+  /* optional graphic for cloning all graphics settings */
+  if (parameter[GFX_ARG_CLONE_FROM] != ARG_UNDEFINED_VALUE)
+    graphic_info[graphic].clone_from = parameter[GFX_ARG_CLONE_FROM];
+
+  /* optional settings for drawing title screens and title messages */
+  if (parameter[GFX_ARG_FADE_MODE] != ARG_UNDEFINED_VALUE)
+    graphic_info[graphic].fade_mode = parameter[GFX_ARG_FADE_MODE];
+  if (parameter[GFX_ARG_FADE_DELAY] != ARG_UNDEFINED_VALUE)
+    graphic_info[graphic].fade_delay = parameter[GFX_ARG_FADE_DELAY];
+  if (parameter[GFX_ARG_POST_DELAY] != ARG_UNDEFINED_VALUE)
+    graphic_info[graphic].post_delay = parameter[GFX_ARG_POST_DELAY];
+  if (parameter[GFX_ARG_AUTO_DELAY] != ARG_UNDEFINED_VALUE)
+    graphic_info[graphic].auto_delay = parameter[GFX_ARG_AUTO_DELAY];
+  if (parameter[GFX_ARG_ALIGN] != ARG_UNDEFINED_VALUE)
+    graphic_info[graphic].align = parameter[GFX_ARG_ALIGN];
+  if (parameter[GFX_ARG_VALIGN] != ARG_UNDEFINED_VALUE)
+    graphic_info[graphic].valign = parameter[GFX_ARG_VALIGN];
+  if (parameter[GFX_ARG_SORT_PRIORITY] != ARG_UNDEFINED_VALUE)
+    graphic_info[graphic].sort_priority = parameter[GFX_ARG_SORT_PRIORITY];
+#endif
+
+  UPDATE_BUSY_STATE();
+}
+
+static void set_cloned_graphic_parameters(int graphic)
+{
+  int fallback_graphic = IMG_CHAR_EXCLAM;
+  int max_num_images = getImageListSize();
+  int clone_graphic = graphic_info[graphic].clone_from;
+  int num_references_followed = 1;
+
+  while (graphic_info[clone_graphic].clone_from != -1 &&
+        num_references_followed < max_num_images)
+  {
+    clone_graphic = graphic_info[clone_graphic].clone_from;
+
+    num_references_followed++;
+  }
+
+  if (num_references_followed >= max_num_images)
+  {
+    Error(ERR_INFO_LINE, "-");
+    Error(ERR_INFO, "warning: error found in config file:");
+    Error(ERR_INFO, "- config file: '%s'", getImageConfigFilename());
+    Error(ERR_INFO, "- config token: '%s'", getTokenFromImageID(graphic));
+    Error(ERR_INFO, "error: loop discovered when resolving cloned graphics");
+    Error(ERR_INFO, "custom graphic rejected for this element/action");
+
+    if (graphic == fallback_graphic)
+      Error(ERR_EXIT, "fatal error: no fallback graphic available");
+
+    Error(ERR_INFO, "fallback done to 'char_exclam' for this graphic");
+    Error(ERR_INFO_LINE, "-");
+
+    graphic_info[graphic] = graphic_info[fallback_graphic];
+  }
+  else
+  {
+    graphic_info[graphic] = graphic_info[clone_graphic];
+    graphic_info[graphic].clone_from = clone_graphic;
+  }
 }
 
 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;
 
@@ -708,19 +1857,72 @@ static void InitGraphicInfo()
   static boolean clipmasks_initialized = FALSE;
   Pixmap src_pixmap;
   XGCValues clip_gc_values;
-  unsigned long clip_gc_valuemask;
+  unsigned int clip_gc_valuemask;
   GC copy_clipmask_gc = None;
 #endif
 
-  if (graphic_info != NULL)
-    free(graphic_info);
+  /* use image size as default values for width and height for these images */
+  static int full_size_graphics[] =
+  {
+    IMG_GLOBAL_BORDER,
+    IMG_GLOBAL_DOOR,
+
+    IMG_BACKGROUND_ENVELOPE_1,
+    IMG_BACKGROUND_ENVELOPE_2,
+    IMG_BACKGROUND_ENVELOPE_3,
+    IMG_BACKGROUND_ENVELOPE_4,
+
+    IMG_BACKGROUND,
+    IMG_BACKGROUND_TITLE_INITIAL,
+    IMG_BACKGROUND_TITLE,
+    IMG_BACKGROUND_MAIN,
+    IMG_BACKGROUND_LEVELS,
+    IMG_BACKGROUND_LEVELNR,
+    IMG_BACKGROUND_SCORES,
+    IMG_BACKGROUND_EDITOR,
+    IMG_BACKGROUND_INFO,
+    IMG_BACKGROUND_INFO_ELEMENTS,
+    IMG_BACKGROUND_INFO_MUSIC,
+    IMG_BACKGROUND_INFO_CREDITS,
+    IMG_BACKGROUND_INFO_PROGRAM,
+    IMG_BACKGROUND_INFO_LEVELSET,
+    IMG_BACKGROUND_SETUP,
+    IMG_BACKGROUND_DOOR,
+    IMG_BACKGROUND_TAPE,
+    IMG_BACKGROUND_PANEL,
+
+    IMG_TITLESCREEN_INITIAL_1,
+    IMG_TITLESCREEN_INITIAL_2,
+    IMG_TITLESCREEN_INITIAL_3,
+    IMG_TITLESCREEN_INITIAL_4,
+    IMG_TITLESCREEN_INITIAL_5,
+    IMG_TITLESCREEN_1,
+    IMG_TITLESCREEN_2,
+    IMG_TITLESCREEN_3,
+    IMG_TITLESCREEN_4,
+    IMG_TITLESCREEN_5,
+
+    -1
+  };
+
+  checked_free(graphic_info);
 
   graphic_info = checked_calloc(num_images * sizeof(struct GraphicInfo));
 
+#if 1
+  /* initialize "use_image_size" flag with default value */
+  for (i = 0; i < num_images; i++)
+    graphic_info[i].use_image_size = FALSE;
+
+  /* initialize "use_image_size" flag from static configuration above */
+  for (i = 0; full_size_graphics[i] != -1; i++)
+    graphic_info[full_size_graphics[i]].use_image_size = TRUE;
+#endif
+
 #if defined(TARGET_X11_NATIVE_PERFORMANCE_WORKAROUND)
   if (clipmasks_initialized)
   {
-    for (i=0; i<num_images; i++)
+    for (i = 0; i < num_images; i++)
     {
       if (graphic_info[i].clip_mask)
        XFreePixmap(display, graphic_info[i].clip_mask);
@@ -733,81 +1935,102 @@ static void InitGraphicInfo()
   }
 #endif
 
-  for (i=0; i<num_images; i++)
+  /* first set all graphic paramaters ... */
+  for (i = 0; i < num_images; i++)
+    set_graphic_parameters(i);
+
+  /* ... then copy these parameters for cloned graphics */
+  for (i = 0; i < num_images; i++)
+    if (graphic_info[i].clone_from != -1)
+      set_cloned_graphic_parameters(i);
+
+  for (i = 0; i < num_images; i++)
   {
-    struct FileInfo *image = getImageListEntry(i);
     Bitmap *src_bitmap;
     int src_x, src_y;
+    int width, height;
     int first_frame, last_frame;
-
-    set_graphic_parameters(i, image->parameter);
+    int src_bitmap_width, src_bitmap_height;
 
     /* 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 */
 
+    /* get image size (this can differ from the standard element tile size!) */
+    width  = graphic_info[i].width;
+    height = graphic_info[i].height;
+
+    /* get final bitmap size (with scaling, but without small images) */
+    src_bitmap_width  = graphic_info[i].src_image_width;
+    src_bitmap_height = graphic_info[i].src_image_height;
+
+    /* check if first animation frame is inside specified bitmap */
+
     first_frame = 0;
-    getGraphicSource(i, first_frame, &src_bitmap, &src_x, &src_y);
+    getFixedGraphicSource(i, first_frame, &src_bitmap, &src_x, &src_y);
+
+#if 1
+    /* this avoids calculating wrong start position for out-of-bounds frame */
+    src_x = graphic_info[i].src_x;
+    src_y = graphic_info[i].src_y;
+#endif
+
     if (src_x < 0 || src_y < 0 ||
-       src_x + TILEX > src_bitmap->width ||
-       src_y + TILEY > src_bitmap->height)
+       src_x + width  > src_bitmap_width ||
+       src_y + height > 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");
+      Error(ERR_INFO_LINE, "-");
+      Error(ERR_INFO, "warning: error found in config file:");
+      Error(ERR_INFO, "- config file: '%s'", getImageConfigFilename());
+      Error(ERR_INFO, "- config token: '%s'", getTokenFromImageID(i));
+      Error(ERR_INFO, "- image file: '%s'", src_bitmap->source_filename);
+      Error(ERR_INFO,
+           "error: first animation frame out of bounds (%d, %d) [%d, %d]",
+           src_x, src_y, src_bitmap_width, src_bitmap_height);
+      Error(ERR_INFO, "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, "-");
+      Error(ERR_INFO, "fallback done to 'char_exclam' for this graphic");
+      Error(ERR_INFO_LINE, "-");
 
-      set_graphic_parameters(i, fallback_image->default_parameter);
-      graphic_info[i].bitmap = fallback_bitmap;
+      graphic_info[i] = graphic_info[fallback_graphic];
     }
 
+    /* check if last animation frame is inside specified bitmap */
+
     last_frame = graphic_info[i].anim_frames - 1;
-    getGraphicSource(i, last_frame, &src_bitmap, &src_x, &src_y);
+    getFixedGraphicSource(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)
+       src_x + width  > src_bitmap_width ||
+       src_y + height > 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");
+      Error(ERR_INFO_LINE, "-");
+      Error(ERR_INFO, "warning: error found in config file:");
+      Error(ERR_INFO, "- config file: '%s'", getImageConfigFilename());
+      Error(ERR_INFO, "- config token: '%s'", getTokenFromImageID(i));
+      Error(ERR_INFO, "- image file: '%s'", src_bitmap->source_filename);
+      Error(ERR_INFO,
+           "error: last animation frame (%d) out of bounds (%d, %d) [%d, %d]",
+           last_frame, src_x, src_y, src_bitmap_width, src_bitmap_height);
+      Error(ERR_INFO, "::: %d, %d", width, height);
+      Error(ERR_INFO, "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, "-");
+      Error(ERR_INFO, "fallback done to 'char_exclam' for this graphic");
+      Error(ERR_INFO_LINE, "-");
 
-      set_graphic_parameters(i, fallback_image->default_parameter);
-      graphic_info[i].bitmap = fallback_bitmap;
+      graphic_info[i] = graphic_info[fallback_graphic];
     }
 
 #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);
+    /* currently we only need a tile clip mask from the first frame */
+    getFixedGraphicSource(i, first_frame, &src_bitmap, &src_x, &src_y);
 
     if (copy_clipmask_gc == None)
     {
@@ -841,55 +2064,93 @@ static void InitGraphicInfo()
 #endif
 }
 
-static void InitElementSoundInfo()
+static void InitGraphicCompatibilityInfo()
 {
-  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;
+  struct FileInfo *fi_global_door =
+    getImageListEntryFromImageID(IMG_GLOBAL_DOOR);
+  int num_images = getImageListSize();
+  int i;
 
-  /* initialize element/sound mapping from static configuration */
-  for (i=0; element_to_sound[i].element > -1; i++)
+  /* the following compatibility handling is needed for the following case:
+     versions up to 3.3.0.0 used one large bitmap "global.door" for various
+     graphics mainly used for door and panel graphics, like editor, tape and
+     in-game buttons with hard-coded bitmap positions and button sizes; as
+     these graphics now have individual definitions, redefining "global.door"
+     to change all these graphics at once like before does not work anymore
+     (because all those individual definitions still have their default values);
+     to solve this, remap all those individual definitions that are not
+     redefined to the new bitmap of "global.door" if it was redefined */
+
+  /* special compatibility handling if image "global.door" was redefined */
+  if (fi_global_door->redefined)
   {
-    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;
+    for (i = 0; i < num_images; i++)
+    {
+      struct FileInfo *fi = getImageListEntryFromImageID(i);
 
-    if (action < 0)
-      action = ACTION_DEFAULT;
+      /* process only those images that still use the default settings */
+      if (!fi->redefined)
+      {
+       /* process all images which default to same image as "global.door" */
+       if (strEqual(fi->default_filename, fi_global_door->default_filename))
+       {
+         // printf("::: special treatment needed for token '%s'\n", fi->token);
 
-    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;
+         graphic_info[i].bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
+       }
+      }
+    }
   }
 
-  /* initialize element/sound mapping from dynamic configuration */
-  for (i=0; i < num_property_mappings; i++)
+#if 0
+  for (i = 0; i < num_images; i++)
   {
-    int element = property_mapping[i].base_index;
-    int action  = property_mapping[i].ext1_index;
-    int sound   = property_mapping[i].artwork_index;
+    struct FileInfo *fi = getImageListEntryFromImageID(i);
 
-    if (element >= MAX_NUM_ELEMENTS)
-      continue;
+    if (i == IMG_GLOBAL_DOOR)
+    {
+      printf("::: %s, %s, %d\n",
+            fi->default_filename,
+            fi->filename,
+            fi->redefined);
+    }
+  }
+#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;
 
-    element_info[element].sound[action] = sound;
+    if (!is_class)
+      element_info[element].sound[action] = sound;
+    else
+      for (j = 0; j < MAX_NUM_ELEMENTS; j++)
+       if (strEqual(element_info[j].class_name,
+                    element_info[element].class_name))
+         element_info[j].sound[action] = sound;
   }
 
   /* initialize element class/sound mapping from dynamic configuration */
-  for (i=0; i < num_property_mappings; i++)
+  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;
@@ -901,24 +2162,103 @@ static void InitElementSoundInfo()
     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)
+    for (j = 0; j < MAX_NUM_ELEMENTS; j++)
+      if (strEqual(element_info[j].class_name,
+                  element_info[element_class].class_name))
        element_info[j].sound[action] = sound;
   }
 
-  /* now set all '-1' values to element specific default values */
-  for (i=0; i<MAX_NUM_ELEMENTS; i++)
+  /* initialize element/sound mapping from dynamic configuration */
+  for (i = 0; i < num_property_mappings; i++)
   {
-    int default_action_sound = element_info[i].sound[ACTION_DEFAULT];
+    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;
+  }
 
-    for (act=0; act < NUM_ACTIONS; act++)
+  /* 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;
     }
   }
+
+  /* copy sound settings to some elements that are only stored in level file
+     in native R'n'D levels, but are used by game engine in native EM levels */
+  for (i = 0; copy_properties[i][0] != -1; i++)
+    for (j = 1; j <= 4; j++)
+      for (act = 0; act < NUM_ACTIONS; act++)
+       element_info[copy_properties[i][j]].sound[act] =
+         element_info[copy_properties[i][0]].sound[act];
+}
+
+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
+  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)
@@ -927,63 +2267,71 @@ static void set_sound_parameters(int sound, char **parameter_raw)
   int i;
 
   /* get integer values from string parameters */
-  for (i=0; i < NUM_SND_ARGS; i++)
+  for (i = 0; i < NUM_SND_ARGS; i++)
     parameter[i] =
-      get_parameter_value(sound_config_suffix[i].token, parameter_raw[i],
+      get_parameter_value(parameter_raw[i],
+                         sound_config_suffix[i].token,
                          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].priority = parameter[SND_ARG_PRIORITY];
 }
 
 static void InitSoundInfo()
 {
-  struct PropertyMapping *property_mapping = getSoundListPropertyMapping();
-  int num_property_mappings = getSoundListPropertyMappingSize();
   int *sound_effect_properties;
   int num_sounds = getSoundListSize();
   int i, j;
 
-  if (sound_info != NULL)
-    free(sound_info);
+  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++)
+  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++)
+  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;
+    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++)
+    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)
+         strEqual(&sound->token[len_effect_text - len_action_text],
+                  element_action_info[j].suffix))
       {
        sound_effect_properties[i] = element_action_info[j].value;
+       sound_info[i].loop = element_action_info[j].is_loop_sound;
 
-       if (element_action_info[j].is_loop_sound)
-         sound_info[i].loop = TRUE;
+       break;
       }
     }
 
     /* associate elements and some selected sound actions */
 
-    for (j=0; j<MAX_NUM_ELEMENTS; j++)
+    for (j = 0; j < MAX_NUM_ELEMENTS; j++)
     {
       if (element_info[j].class_name)
       {
@@ -1005,92 +2353,386 @@ static void InitSoundInfo()
   }
 
   free(sound_effect_properties);
+}
 
-  /* initialize element/sound mapping from dynamic configuration */
-  for (i=0; i < num_property_mappings; i++)
+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 element   = property_mapping[i].base_index;
-    int action    = property_mapping[i].ext1_index;
-    int sound     = property_mapping[i].artwork_index;
+    int gamemode = gamemode_to_music[i].gamemode;
+    int music    = gamemode_to_music[i].music;
 
-    if (action < 0)
-      action = ACTION_DEFAULT;
+#if 0
+    printf("::: gamemode == %d, music == %d\n", gamemode, music);
+#endif
 
-    element_info[element].sound[action] = sound;
+    if (gamemode < 0)
+      gamemode = GAME_MODE_DEFAULT;
+
+    menu.music[gamemode] = music;
   }
 
-#if 0
-  /* TEST ONLY */
+  /* initialize gamemode/music mapping from dynamic configuration */
+  for (i = 0; i < num_property_mappings; i++)
   {
-    int element = EL_CUSTOM_11;
-    int j = 0;
+    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;
 
-    while (element_action_info[j].suffix)
+    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)
     {
-      printf("element %d, sound action '%s'  == %d\n",
-            element, element_action_info[j].suffix,
-            element_info[element].sound[j]);
-      j++;
+      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;
   }
 
-  PlaySoundLevelElementAction(0,0, EL_CUSTOM_11, ACTION_PUSHING);
-#endif
+  /* 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(parameter_raw[i],
+                         music_config_suffix[i].token,
+                         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++)
   {
-    int element = EL_SAND;
-    int sound_action = ACTION_DIGGING;
-    int j = 0;
+    struct FileInfo *music = getMusicListEntry(i);
+    int len_music_text = strlen(music->token);
+
+    music_info[i].loop = TRUE;         /* default: play music in loop mode */
 
-    while (element_action_info[j].suffix)
+    /* determine all loop music */
+
+    for (j = 0; music_prefix_info[j].prefix; j++)
     {
-      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++;
+      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);
   }
-#endif
 }
 
 static void ReinitializeGraphics()
 {
+  print_timestamp_init("ReinitializeGraphics");
+
   InitGraphicInfo();                   /* graphic properties mapping */
+  print_timestamp_time("InitGraphicInfo");
   InitElementGraphicInfo();            /* element game graphic mapping */
+  print_timestamp_time("InitElementGraphicInfo");
   InitElementSpecialGraphicInfo();     /* element special graphic mapping */
+  print_timestamp_time("InitElementSpecialGraphicInfo");
 
-  InitElementSmallImages();            /* create editor and preview images */
+  InitElementSmallImages();            /* scale elements to all needed sizes */
+  print_timestamp_time("InitElementSmallImages");
+  InitScaledImages();                  /* scale all other images, if needed */
+  print_timestamp_time("InitScaledImages");
   InitFontGraphicInfo();               /* initialize text drawing functions */
+  print_timestamp_time("InitFontGraphicInfo");
+
+  InitGraphicInfo_EM();                        /* graphic mapping for EM engine */
+  print_timestamp_time("InitGraphicInfo_EM");
+
+  InitGraphicCompatibilityInfo();
+  print_timestamp_time("InitGraphicCompatibilityInfo");
 
   SetMainBackgroundImage(IMG_BACKGROUND);
+  print_timestamp_time("SetMainBackgroundImage");
   SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
+  print_timestamp_time("SetDoorBackgroundImage");
 
   InitGadgets();
+  print_timestamp_time("InitGadgets");
   InitToons();
+  print_timestamp_time("InitToons");
+
+  print_timestamp_done("ReinitializeGraphics");
 }
 
 static void ReinitializeSounds()
 {
   InitSoundInfo();             /* sound properties mapping */
   InitElementSoundInfo();      /* element game sound mapping */
+  InitGameModeSoundInfo();     /* game mode sound mapping */
 
-#if 1
-  InitElementSoundInfo();      /* element game sound mapping */
-#endif
-
-  InitPlaySoundLevel();                /* internal game sound settings */
+  InitPlayLevelSound();                /* internal game sound settings */
 }
 
 static void ReinitializeMusic()
 {
-  /* currently nothing to do */
+  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 elements 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_YAMYAM_LEFT,          5       },
+    { EL_YAMYAM_RIGHT,         5       },
+    { EL_YAMYAM_UP,            5       },
+    { EL_YAMYAM_DOWN,          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      },
+    { EL_EMC_ANDROID,          18      },
+
+    { -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;
+}
+
+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;
+}
+
+static void ResolveGroupElementExt(int group_element, int recursion_depth)
+{
+  static int group_nr;
+  static struct ElementGroupInfo *group;
+  struct ElementGroupInfo *actual_group = element_info[group_element].group;
+  int i;
+
+  if (actual_group == NULL)                    /* not yet initialized */
+    return;
+
+  if (recursion_depth > NUM_GROUP_ELEMENTS)    /* recursion too deep */
+  {
+    Error(ERR_WARN, "recursion too deep when resolving group element %d",
+         group_element - EL_GROUP_START + 1);
+
+    /* replace element which caused too deep recursion by question mark */
+    group->element_resolved[group->num_elements_resolved++] = EL_UNKNOWN;
+
+    return;
+  }
+
+  if (recursion_depth == 0)                    /* initialization */
+  {
+    group = actual_group;
+    group_nr = GROUP_NR(group_element);
+
+    group->num_elements_resolved = 0;
+    group->choice_pos = 0;
+
+    for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+      element_info[i].in_group[group_nr] = FALSE;
+  }
+
+  for (i = 0; i < actual_group->num_elements; i++)
+  {
+    int element = actual_group->element[i];
+
+    if (group->num_elements_resolved == NUM_FILE_ELEMENTS)
+      break;
+
+    if (IS_GROUP_ELEMENT(element))
+      ResolveGroupElementExt(element, recursion_depth + 1);
+    else
+    {
+      group->element_resolved[group->num_elements_resolved++] = element;
+      element_info[element].in_group[group_nr] = TRUE;
+    }
+  }
+}
+
+void ResolveGroupElement(int group_element)
+{
+  ResolveGroupElementExt(group_element, 0);
 }
 
 void InitElementPropertiesStatic()
 {
+  static boolean clipboard_elements_initialized = FALSE;
+
   static int ep_diggable[] =
   {
     EL_SAND,
@@ -1100,17 +2742,22 @@ void InitElementPropertiesStatic()
     EL_TRAP,
     EL_INVISIBLE_SAND,
     EL_INVISIBLE_SAND_ACTIVE,
+    EL_EMC_GRASS,
 
     /* !!! 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_DC_LANDMINE,
     EL_TRAP_ACTIVE,
     EL_SP_BUGGY_BASE_ACTIVE,
+    EL_EMC_PLANT,
 #endif
+
     -1
   };
 
-  static int ep_collectible[] =
+  static int ep_collectible_only[] =
   {
     EL_BD_DIAMOND,
     EL_EMERALD,
@@ -1126,7 +2773,12 @@ void InitElementPropertiesStatic()
     EL_EM_KEY_2,
     EL_EM_KEY_3,
     EL_EM_KEY_4,
+    EL_EMC_KEY_5,
+    EL_EMC_KEY_6,
+    EL_EMC_KEY_7,
+    EL_EMC_KEY_8,
     EL_DYNAMITE,
+    EL_EM_DYNAMITE,
     EL_DYNABOMB_INCREASE_NUMBER,
     EL_DYNABOMB_INCREASE_SIZE,
     EL_DYNABOMB_INCREASE_POWER,
@@ -1134,12 +2786,23 @@ void InitElementPropertiesStatic()
     EL_SP_DISK_RED,
     EL_PEARL,
     EL_CRYSTAL,
-    EL_KEY_WHITE,
+    EL_DC_KEY_WHITE,
     EL_SHIELD_NORMAL,
     EL_SHIELD_DEADLY,
     EL_EXTRA_TIME,
-    EL_ENVELOPE,
+    EL_ENVELOPE_1,
+    EL_ENVELOPE_2,
+    EL_ENVELOPE_3,
+    EL_ENVELOPE_4,
     EL_SPEED_PILL,
+    EL_EMC_LENSES,
+    EL_EMC_MAGNIFIER,
+
+#if 0
+    /* !!! handle separately !!! */
+    EL_DC_LANDMINE,    /* deadly when running into, but can be snapped */
+#endif
+
     -1
   };
 
@@ -1165,10 +2828,13 @@ void InitElementPropertiesStatic()
 
     /* !!! maybe this should better be handled by 'ep_diggable' !!! */
 #if 1
-    EL_SP_BUGGY_BASE_ACTIVE,
-    EL_TRAP_ACTIVE,
     EL_LANDMINE,
+    EL_DC_LANDMINE,
+    EL_TRAP_ACTIVE,
+    EL_SP_BUGGY_BASE_ACTIVE,
+    EL_EMC_PLANT,
 #endif
+
     -1
   };
 
@@ -1187,6 +2853,7 @@ void InitElementPropertiesStatic()
     EL_PACMAN,
     EL_SP_SNIKSNAK,
     EL_SP_ELECTRON,
+
     -1
   };
 
@@ -1196,6 +2863,7 @@ void InitElementPropertiesStatic()
     EL_SPACESHIP,
     EL_BD_BUTTERFLY,
     EL_BD_FIREFLY,
+
     -1
   };
 
@@ -1240,14 +2908,49 @@ void InitElementPropertiesStatic()
     EL_SIGN_STOP,
     EL_SIGN_WHEELCHAIR,
     EL_SIGN_PARKING,
-    EL_SIGN_ONEWAY,
+    EL_SIGN_NO_ENTRY,
+    EL_SIGN_UNUSED_1,
+    EL_SIGN_GIVE_WAY,
+    EL_SIGN_ENTRY_FORBIDDEN,
+    EL_SIGN_EMERGENCY_EXIT,
+    EL_SIGN_YIN_YANG,
+    EL_SIGN_UNUSED_2,
+    EL_SIGN_SPERMS,
+    EL_SIGN_BULLET,
     EL_SIGN_HEART,
-    EL_SIGN_TRIANGLE,
-    EL_SIGN_ROUND,
-    EL_SIGN_EXIT,
-    EL_SIGN_YINYANG,
-    EL_SIGN_OTHER,
-    EL_STEELWALL_SLANTED,
+    EL_SIGN_CROSS,
+    EL_SIGN_FRANKIE,
+    EL_STEEL_EXIT_CLOSED,
+    EL_STEEL_EXIT_OPEN,
+    EL_STEEL_EXIT_OPENING,
+    EL_STEEL_EXIT_CLOSING,
+    EL_EM_STEEL_EXIT_CLOSED,
+    EL_EM_STEEL_EXIT_OPEN,
+    EL_EM_STEEL_EXIT_OPENING,
+    EL_EM_STEEL_EXIT_CLOSING,
+    EL_DC_STEELWALL_1_LEFT,
+    EL_DC_STEELWALL_1_RIGHT,
+    EL_DC_STEELWALL_1_TOP,
+    EL_DC_STEELWALL_1_BOTTOM,
+    EL_DC_STEELWALL_1_HORIZONTAL,
+    EL_DC_STEELWALL_1_VERTICAL,
+    EL_DC_STEELWALL_1_TOPLEFT,
+    EL_DC_STEELWALL_1_TOPRIGHT,
+    EL_DC_STEELWALL_1_BOTTOMLEFT,
+    EL_DC_STEELWALL_1_BOTTOMRIGHT,
+    EL_DC_STEELWALL_1_TOPLEFT_2,
+    EL_DC_STEELWALL_1_TOPRIGHT_2,
+    EL_DC_STEELWALL_1_BOTTOMLEFT_2,
+    EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
+    EL_DC_STEELWALL_2_LEFT,
+    EL_DC_STEELWALL_2_RIGHT,
+    EL_DC_STEELWALL_2_TOP,
+    EL_DC_STEELWALL_2_BOTTOM,
+    EL_DC_STEELWALL_2_HORIZONTAL,
+    EL_DC_STEELWALL_2_VERTICAL,
+    EL_DC_STEELWALL_2_MIDDLE,
+    EL_DC_STEELWALL_2_SINGLE,
+    EL_STEELWALL_SLIPPERY,
     EL_EMC_STEELWALL_1,
     EL_EMC_STEELWALL_2,
     EL_EMC_STEELWALL_3,
@@ -1261,6 +2964,10 @@ void InitElementPropertiesStatic()
     EL_GATE_2_GRAY,
     EL_GATE_3_GRAY,
     EL_GATE_4_GRAY,
+    EL_GATE_1_GRAY_ACTIVE,
+    EL_GATE_2_GRAY_ACTIVE,
+    EL_GATE_3_GRAY_ACTIVE,
+    EL_GATE_4_GRAY_ACTIVE,
     EL_EM_GATE_1,
     EL_EM_GATE_2,
     EL_EM_GATE_3,
@@ -1269,21 +2976,41 @@ void InitElementPropertiesStatic()
     EL_EM_GATE_2_GRAY,
     EL_EM_GATE_3_GRAY,
     EL_EM_GATE_4_GRAY,
+    EL_EM_GATE_1_GRAY_ACTIVE,
+    EL_EM_GATE_2_GRAY_ACTIVE,
+    EL_EM_GATE_3_GRAY_ACTIVE,
+    EL_EM_GATE_4_GRAY_ACTIVE,
+    EL_EMC_GATE_5,
+    EL_EMC_GATE_6,
+    EL_EMC_GATE_7,
+    EL_EMC_GATE_8,
+    EL_EMC_GATE_5_GRAY,
+    EL_EMC_GATE_6_GRAY,
+    EL_EMC_GATE_7_GRAY,
+    EL_EMC_GATE_8_GRAY,
+    EL_EMC_GATE_5_GRAY_ACTIVE,
+    EL_EMC_GATE_6_GRAY_ACTIVE,
+    EL_EMC_GATE_7_GRAY_ACTIVE,
+    EL_EMC_GATE_8_GRAY_ACTIVE,
+    EL_DC_GATE_WHITE,
+    EL_DC_GATE_WHITE_GRAY,
+    EL_DC_GATE_WHITE_GRAY_ACTIVE,
+    EL_DC_GATE_FAKE_GRAY,
     EL_SWITCHGATE_OPEN,
     EL_SWITCHGATE_OPENING,
     EL_SWITCHGATE_CLOSED,
     EL_SWITCHGATE_CLOSING,
 #if 1
-    EL_SWITCHGATE_SWITCH_UP,
-    EL_SWITCHGATE_SWITCH_DOWN,
+    EL_DC_SWITCHGATE_SWITCH_UP,
+    EL_DC_SWITCHGATE_SWITCH_DOWN,
 #endif
     EL_TIMEGATE_OPEN,
     EL_TIMEGATE_OPENING,
     EL_TIMEGATE_CLOSED,
     EL_TIMEGATE_CLOSING,
 #if 1
-    EL_TIMEGATE_SWITCH,
-    EL_TIMEGATE_SWITCH_ACTIVE,
+    EL_DC_TIMEGATE_SWITCH,
+    EL_DC_TIMEGATE_SWITCH_ACTIVE,
 #endif
     EL_TUBE_ANY,
     EL_TUBE_VERTICAL,
@@ -1296,12 +3023,16 @@ void InitElementPropertiesStatic()
     EL_TUBE_LEFT_DOWN,
     EL_TUBE_RIGHT_UP,
     EL_TUBE_RIGHT_DOWN,
+    EL_EXPANDABLE_STEELWALL_HORIZONTAL,
+    EL_EXPANDABLE_STEELWALL_VERTICAL,
+    EL_EXPANDABLE_STEELWALL_ANY,
+
     -1
   };
 
   static int ep_slippery[] =
   {
-    EL_WALL_CRUMBLED,
+    EL_WALL_SLIPPERY,
     EL_BD_WALL,
     EL_ROCK,
     EL_BD_ROCK,
@@ -1330,9 +3061,16 @@ void InitElementPropertiesStatic()
     EL_SP_CHIP_TOP,
     EL_SP_CHIP_BOTTOM,
     EL_SPEED_PILL,
-    EL_STEELWALL_SLANTED,
+    EL_STEELWALL_SLIPPERY,
     EL_PEARL,
     EL_CRYSTAL,
+    EL_EMC_WALL_SLIPPERY_1,
+    EL_EMC_WALL_SLIPPERY_2,
+    EL_EMC_WALL_SLIPPERY_3,
+    EL_EMC_WALL_SLIPPERY_4,
+    EL_EMC_MAGIC_BALL,
+    EL_EMC_MAGIC_BALL_ACTIVE,
+
     -1
   };
 
@@ -1343,6 +3081,7 @@ void InitElementPropertiesStatic()
 
   static int ep_can_move[] =
   {
+    /* same elements as in 'pb_can_move_into_acid' */
     EL_BUG,
     EL_SPACESHIP,
     EL_BD_BUTTERFLY,
@@ -1360,6 +3099,8 @@ void InitElementPropertiesStatic()
     EL_SP_ELECTRON,
     EL_BALLOON,
     EL_SPRING,
+    EL_EMC_ANDROID,
+
     -1
   };
 
@@ -1377,8 +3118,10 @@ void InitElementPropertiesStatic()
     EL_NUT,
     EL_AMOEBA_DROP,
     EL_QUICKSAND_FULL,
+    EL_QUICKSAND_FAST_FULL,
     EL_MAGIC_WALL_FULL,
     EL_BD_MAGIC_WALL_FULL,
+    EL_DC_MAGIC_WALL_FULL,
     EL_TIME_ORB_FULL,
     EL_TIME_ORB_EMPTY,
     EL_SP_ZONK,
@@ -1388,6 +3131,7 @@ void InitElementPropertiesStatic()
     EL_CRYSTAL,
     EL_SPRING,
     EL_DX_SUPABOMB,
+
     -1
   };
 
@@ -1413,6 +3157,7 @@ void InitElementPropertiesStatic()
     EL_CRYSTAL,
     EL_SPRING,
     EL_DX_SUPABOMB,
+
     -1
   };
 
@@ -1421,6 +3166,7 @@ void InitElementPropertiesStatic()
     EL_ROCK,
     EL_BD_ROCK,
     EL_SP_ZONK,
+
     -1
   };
 
@@ -1429,25 +3175,28 @@ void InitElementPropertiesStatic()
     EL_ROCK,
     EL_BD_ROCK,
     EL_SP_ZONK,
+
     -1
   };
 
-  static int ep_can_explode_by_fire[] =
+  static int ep_explodes_by_fire[] =
   {
-    /* same elements as in 'ep_can_explode_impact' */
+    /* same elements as in 'ep_explodes_impact' */
     EL_BOMB,
     EL_SP_DISK_ORANGE,
     EL_DX_SUPABOMB,
 
-    /* same elements as in 'ep_can_explode_smashed' */
+    /* same elements as in 'ep_explodes_smashed' */
     EL_SATELLITE,
     EL_PIG,
     EL_DRAGON,
     EL_MOLE,
 
     /* new elements */
-    EL_DYNAMITE_ACTIVE,
     EL_DYNAMITE,
+    EL_DYNAMITE_ACTIVE,
+    EL_EM_DYNAMITE,
+    EL_EM_DYNAMITE_ACTIVE,
     EL_DYNABOMB_PLAYER_1_ACTIVE,
     EL_DYNABOMB_PLAYER_2_ACTIVE,
     EL_DYNABOMB_PLAYER_3_ACTIVE,
@@ -1462,12 +3211,16 @@ void InitElementPropertiesStatic()
     EL_SP_DISK_YELLOW,
     EL_SP_SNIKSNAK,
     EL_SP_ELECTRON,
+#if 0
+    EL_BLACK_ORB,
+#endif
+
     -1
   };
 
-  static int ep_can_explode_smashed[] =
+  static int ep_explodes_smashed[] =
   {
-    /* same elements as in 'ep_can_explode_impact' */
+    /* same elements as in 'ep_explodes_impact' */
     EL_BOMB,
     EL_SP_DISK_ORANGE,
     EL_DX_SUPABOMB,
@@ -1477,14 +3230,16 @@ void InitElementPropertiesStatic()
     EL_PIG,
     EL_DRAGON,
     EL_MOLE,
+
     -1
   };
 
-  static int ep_can_explode_impact[] =
+  static int ep_explodes_impact[] =
   {
     EL_BOMB,
     EL_SP_DISK_ORANGE,
     EL_DX_SUPABOMB,
+
     -1
   };
 
@@ -1494,7 +3249,17 @@ void InitElementPropertiesStatic()
     EL_SP_EMPTY_SPACE,
     EL_SOKOBAN_FIELD_EMPTY,
     EL_EXIT_OPEN,
+    EL_EM_EXIT_OPEN,
+#if 1
+    EL_EM_EXIT_OPENING,
+#endif
     EL_SP_EXIT_OPEN,
+    EL_SP_EXIT_OPENING,
+    EL_STEEL_EXIT_OPEN,
+    EL_EM_STEEL_EXIT_OPEN,
+#if 1
+    EL_EM_STEEL_EXIT_OPENING,
+#endif
     EL_GATE_1,
     EL_GATE_2,
     EL_GATE_3,
@@ -1503,6 +3268,14 @@ void InitElementPropertiesStatic()
     EL_GATE_2_GRAY,
     EL_GATE_3_GRAY,
     EL_GATE_4_GRAY,
+    EL_GATE_1_GRAY_ACTIVE,
+    EL_GATE_2_GRAY_ACTIVE,
+    EL_GATE_3_GRAY_ACTIVE,
+    EL_GATE_4_GRAY_ACTIVE,
+    EL_PENGUIN,
+    EL_PIG,
+    EL_DRAGON,
+
     -1
   };
 
@@ -1519,6 +3292,7 @@ void InitElementPropertiesStatic()
     EL_TUBE_LEFT_DOWN,
     EL_TUBE_RIGHT_UP,
     EL_TUBE_RIGHT_DOWN,
+
     -1
   };
 
@@ -1537,8 +3311,28 @@ void InitElementPropertiesStatic()
     EL_EM_GATE_2_GRAY,
     EL_EM_GATE_3_GRAY,
     EL_EM_GATE_4_GRAY,
+    EL_EM_GATE_1_GRAY_ACTIVE,
+    EL_EM_GATE_2_GRAY_ACTIVE,
+    EL_EM_GATE_3_GRAY_ACTIVE,
+    EL_EM_GATE_4_GRAY_ACTIVE,
+    EL_EMC_GATE_5,
+    EL_EMC_GATE_6,
+    EL_EMC_GATE_7,
+    EL_EMC_GATE_8,
+    EL_EMC_GATE_5_GRAY,
+    EL_EMC_GATE_6_GRAY,
+    EL_EMC_GATE_7_GRAY,
+    EL_EMC_GATE_8_GRAY,
+    EL_EMC_GATE_5_GRAY_ACTIVE,
+    EL_EMC_GATE_6_GRAY_ACTIVE,
+    EL_EMC_GATE_7_GRAY_ACTIVE,
+    EL_EMC_GATE_8_GRAY_ACTIVE,
+    EL_DC_GATE_WHITE,
+    EL_DC_GATE_WHITE_GRAY,
+    EL_DC_GATE_WHITE_GRAY_ACTIVE,
     EL_SWITCHGATE_OPEN,
     EL_TIMEGATE_OPEN,
+
     -1
   };
 
@@ -1555,6 +3349,15 @@ void InitElementPropertiesStatic()
     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
   };
 
@@ -1563,12 +3366,22 @@ void InitElementPropertiesStatic()
     -1
   };
 
-  static int ep_pushable[] =
+  static int ep_droppable[] =
   {
-    EL_ROCK,
-    EL_BOMB,
-    EL_DX_SUPABOMB,
-    EL_NUT,
+    -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,
@@ -1579,15 +3392,158 @@ void InitElementPropertiesStatic()
     EL_SATELLITE,
     EL_SP_DISK_YELLOW,
     EL_BALLOON,
+    EL_EMC_ANDROID,
+
+    -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_EM_GATE_1_GRAY_ACTIVE,
+    EL_EM_GATE_2_GRAY_ACTIVE,
+    EL_EM_GATE_3_GRAY_ACTIVE,
+    EL_EM_GATE_4_GRAY_ACTIVE,
+    EL_EMC_GATE_5,
+    EL_EMC_GATE_6,
+    EL_EMC_GATE_7,
+    EL_EMC_GATE_8,
+    EL_EMC_GATE_5_GRAY,
+    EL_EMC_GATE_6_GRAY,
+    EL_EMC_GATE_7_GRAY,
+    EL_EMC_GATE_8_GRAY,
+    EL_EMC_GATE_5_GRAY_ACTIVE,
+    EL_EMC_GATE_6_GRAY_ACTIVE,
+    EL_EMC_GATE_7_GRAY_ACTIVE,
+    EL_EMC_GATE_8_GRAY_ACTIVE,
+    EL_DC_GATE_WHITE,
+    EL_DC_GATE_WHITE_GRAY,
+    EL_DC_GATE_WHITE_GRAY_ACTIVE,
+    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,
+    EL_DYNAMITE_ACTIVE,
+    EL_EM_DYNAMITE,
+    EL_EM_DYNAMITE_ACTIVE,
+    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_can_be_crumbled[] =
+  static int ep_gravity_reachable[] =
   {
     EL_SAND,
-    EL_LANDMINE,
+    EL_SP_BASE,
     EL_TRAP,
-    EL_TRAP_ACTIVE,
+    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,
+    EL_EMC_GRASS,
+
     -1
   };
 
@@ -1597,6 +3553,10 @@ void InitElementPropertiesStatic()
     EL_PLAYER_2,
     EL_PLAYER_3,
     EL_PLAYER_4,
+    EL_SP_MURPHY,
+    EL_SOKOBAN_FIELD_PLAYER,
+    EL_TRIGGER_PLAYER,
+
     -1
   };
 
@@ -1610,6 +3570,23 @@ void InitElementPropertiesStatic()
     EL_EMERALD_RED,
     EL_EMERALD_PURPLE,
     EL_DIAMOND,
+
+    -1
+  };
+
+  static int ep_can_pass_dc_magic_wall[] =
+  {
+    EL_ROCK,
+    EL_BD_ROCK,
+    EL_EMERALD,
+    EL_BD_DIAMOND,
+    EL_EMERALD_YELLOW,
+    EL_EMERALD_RED,
+    EL_EMERALD_PURPLE,
+    EL_DIAMOND,
+    EL_PEARL,
+    EL_CRYSTAL,
+
     -1
   };
 
@@ -1631,16 +3608,23 @@ void InitElementPropertiesStatic()
     EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
     EL_SWITCHGATE_SWITCH_UP,
     EL_SWITCHGATE_SWITCH_DOWN,
+    EL_DC_SWITCHGATE_SWITCH_UP,
+    EL_DC_SWITCHGATE_SWITCH_DOWN,
     EL_LIGHT_SWITCH,
     EL_LIGHT_SWITCH_ACTIVE,
     EL_TIMEGATE_SWITCH,
+    EL_DC_TIMEGATE_SWITCH,
     EL_BALLOON_SWITCH_LEFT,
     EL_BALLOON_SWITCH_RIGHT,
     EL_BALLOON_SWITCH_UP,
     EL_BALLOON_SWITCH_DOWN,
     EL_BALLOON_SWITCH_ANY,
+    EL_BALLOON_SWITCH_NONE,
     EL_LAMP,
     EL_TIME_ORB_FULL,
+    EL_EMC_MAGIC_BALL_SWITCH,
+    EL_EMC_MAGIC_BALL_SWITCH_ACTIVE,
+
     -1
   };
 
@@ -1648,7 +3632,7 @@ void InitElementPropertiesStatic()
   {
     EL_EMPTY,
     EL_SAND,
-    EL_WALL_CRUMBLED,
+    EL_WALL_SLIPPERY,
     EL_BD_WALL,
     EL_ROCK,
     EL_BD_ROCK,
@@ -1658,6 +3642,9 @@ void InitElementPropertiesStatic()
     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,
@@ -1670,11 +3657,17 @@ void InitElementPropertiesStatic()
     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,
@@ -1716,10 +3709,31 @@ void InitElementPropertiesStatic()
     EL_SP_HARDWARE_BASE_6,
     EL_SP_CHIP_TOP,
     EL_SP_CHIP_BOTTOM,
+
     /* additional elements that appeared in newer Supaplex levels */
     EL_INVISIBLE_WALL,
-    /* more than one murphy in a level results in an inactive clone */
+
+    /* 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
   };
 
@@ -1730,8 +3744,13 @@ void InitElementPropertiesStatic()
     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
   };
 
@@ -1743,6 +3762,7 @@ void InitElementPropertiesStatic()
     EL_EMERALD_RED,
     EL_EMERALD_PURPLE,
     EL_DIAMOND,
+
     -1
   };
 
@@ -1770,6 +3790,7 @@ void InitElementPropertiesStatic()
     EL_DIAMOND,
     EL_PEARL,
     EL_CRYSTAL,
+
     -1
   };
 
@@ -1783,6 +3804,7 @@ void InitElementPropertiesStatic()
     EL_DIAMOND,
     EL_PEARL,
     EL_CRYSTAL,
+
     -1
   };
 
@@ -1794,6 +3816,7 @@ void InitElementPropertiesStatic()
     EL_EMERALD_RED,
     EL_EMERALD_PURPLE,
     EL_DIAMOND,
+
     -1
   };
 
@@ -1808,6 +3831,10 @@ void InitElementPropertiesStatic()
     EL_GATE_2_GRAY,
     EL_GATE_3_GRAY,
     EL_GATE_4_GRAY,
+    EL_GATE_1_GRAY_ACTIVE,
+    EL_GATE_2_GRAY_ACTIVE,
+    EL_GATE_3_GRAY_ACTIVE,
+    EL_GATE_4_GRAY_ACTIVE,
     EL_EM_GATE_1,
     EL_EM_GATE_2,
     EL_EM_GATE_3,
@@ -1816,16 +3843,21 @@ void InitElementPropertiesStatic()
     EL_EM_GATE_2_GRAY,
     EL_EM_GATE_3_GRAY,
     EL_EM_GATE_4_GRAY,
+    EL_EM_GATE_1_GRAY_ACTIVE,
+    EL_EM_GATE_2_GRAY_ACTIVE,
+    EL_EM_GATE_3_GRAY_ACTIVE,
+    EL_EM_GATE_4_GRAY_ACTIVE,
     EL_EXIT_CLOSED,
     EL_EXIT_OPENING,
     EL_EXIT_OPEN,
     EL_WALL,
-    EL_WALL_CRUMBLED,
+    EL_WALL_SLIPPERY,
     EL_EXPANDABLE_WALL,
     EL_EXPANDABLE_WALL_HORIZONTAL,
     EL_EXPANDABLE_WALL_VERTICAL,
     EL_EXPANDABLE_WALL_ANY,
     EL_EXPANDABLE_WALL_GROWING,
+    EL_BD_EXPANDABLE_WALL,
     EL_BD_WALL,
     EL_SP_CHIP_SINGLE,
     EL_SP_CHIP_LEFT,
@@ -1851,7 +3883,7 @@ void InitElementPropertiesStatic()
     EL_INVISIBLE_STEELWALL_ACTIVE,
     EL_INVISIBLE_WALL,
     EL_INVISIBLE_WALL_ACTIVE,
-    EL_STEELWALL_SLANTED,
+    EL_STEELWALL_SLIPPERY,
     EL_EMC_STEELWALL_1,
     EL_EMC_STEELWALL_2,
     EL_EMC_STEELWALL_3,
@@ -1864,6 +3896,7 @@ void InitElementPropertiesStatic()
     EL_EMC_WALL_6,
     EL_EMC_WALL_7,
     EL_EMC_WALL_8,
+
     -1
   };
 
@@ -1874,8 +3907,9 @@ void InitElementPropertiesStatic()
     EL_EXPANDABLE_WALL_HORIZONTAL,
     EL_EXPANDABLE_WALL_VERTICAL,
     EL_EXPANDABLE_WALL_ANY,
+    EL_BD_EXPANDABLE_WALL,
     EL_BD_WALL,
-    EL_WALL_CRUMBLED,
+    EL_WALL_SLIPPERY,
     EL_EXIT_CLOSED,
     EL_EXIT_OPENING,
     EL_EXIT_OPEN,
@@ -1915,8 +3949,12 @@ void InitElementPropertiesStatic()
     EL_INVISIBLE_WALL_ACTIVE,
     EL_SWITCHGATE_SWITCH_UP,
     EL_SWITCHGATE_SWITCH_DOWN,
+    EL_DC_SWITCHGATE_SWITCH_UP,
+    EL_DC_SWITCHGATE_SWITCH_DOWN,
     EL_TIMEGATE_SWITCH,
     EL_TIMEGATE_SWITCH_ACTIVE,
+    EL_DC_TIMEGATE_SWITCH,
+    EL_DC_TIMEGATE_SWITCH_ACTIVE,
     EL_EMC_WALL_1,
     EL_EMC_WALL_2,
     EL_EMC_WALL_3,
@@ -1971,14 +4009,43 @@ void InitElementPropertiesStatic()
     EL_SIGN_STOP,
     EL_SIGN_WHEELCHAIR,
     EL_SIGN_PARKING,
-    EL_SIGN_ONEWAY,
+    EL_SIGN_NO_ENTRY,
+    EL_SIGN_UNUSED_1,
+    EL_SIGN_GIVE_WAY,
+    EL_SIGN_ENTRY_FORBIDDEN,
+    EL_SIGN_EMERGENCY_EXIT,
+    EL_SIGN_YIN_YANG,
+    EL_SIGN_UNUSED_2,
+    EL_SIGN_SPERMS,
+    EL_SIGN_BULLET,
     EL_SIGN_HEART,
-    EL_SIGN_TRIANGLE,
-    EL_SIGN_ROUND,
-    EL_SIGN_EXIT,
-    EL_SIGN_YINYANG,
-    EL_SIGN_OTHER,
-    EL_STEELWALL_SLANTED,
+    EL_SIGN_CROSS,
+    EL_SIGN_FRANKIE,
+    EL_STEEL_EXIT_CLOSED,
+    EL_STEEL_EXIT_OPEN,
+    EL_DC_STEELWALL_1_LEFT,
+    EL_DC_STEELWALL_1_RIGHT,
+    EL_DC_STEELWALL_1_TOP,
+    EL_DC_STEELWALL_1_BOTTOM,
+    EL_DC_STEELWALL_1_HORIZONTAL,
+    EL_DC_STEELWALL_1_VERTICAL,
+    EL_DC_STEELWALL_1_TOPLEFT,
+    EL_DC_STEELWALL_1_TOPRIGHT,
+    EL_DC_STEELWALL_1_BOTTOMLEFT,
+    EL_DC_STEELWALL_1_BOTTOMRIGHT,
+    EL_DC_STEELWALL_1_TOPLEFT_2,
+    EL_DC_STEELWALL_1_TOPRIGHT_2,
+    EL_DC_STEELWALL_1_BOTTOMLEFT_2,
+    EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
+    EL_DC_STEELWALL_2_LEFT,
+    EL_DC_STEELWALL_2_RIGHT,
+    EL_DC_STEELWALL_2_TOP,
+    EL_DC_STEELWALL_2_BOTTOM,
+    EL_DC_STEELWALL_2_HORIZONTAL,
+    EL_DC_STEELWALL_2_VERTICAL,
+    EL_DC_STEELWALL_2_MIDDLE,
+    EL_DC_STEELWALL_2_SINGLE,
+    EL_STEELWALL_SLIPPERY,
     EL_EMC_STEELWALL_1,
     EL_EMC_STEELWALL_2,
     EL_EMC_STEELWALL_3,
@@ -1992,6 +4059,10 @@ void InitElementPropertiesStatic()
     EL_GATE_2_GRAY,
     EL_GATE_3_GRAY,
     EL_GATE_4_GRAY,
+    EL_GATE_1_GRAY_ACTIVE,
+    EL_GATE_2_GRAY_ACTIVE,
+    EL_GATE_3_GRAY_ACTIVE,
+    EL_GATE_4_GRAY_ACTIVE,
     EL_EM_GATE_1,
     EL_EM_GATE_2,
     EL_EM_GATE_3,
@@ -2000,6 +4071,10 @@ void InitElementPropertiesStatic()
     EL_EM_GATE_2_GRAY,
     EL_EM_GATE_3_GRAY,
     EL_EM_GATE_4_GRAY,
+    EL_EM_GATE_1_GRAY_ACTIVE,
+    EL_EM_GATE_2_GRAY_ACTIVE,
+    EL_EM_GATE_3_GRAY_ACTIVE,
+    EL_EM_GATE_4_GRAY_ACTIVE,
     EL_SWITCHGATE_OPEN,
     EL_SWITCHGATE_OPENING,
     EL_SWITCHGATE_CLOSED,
@@ -2019,6 +4094,7 @@ void InitElementPropertiesStatic()
     EL_TUBE_LEFT_DOWN,
     EL_TUBE_RIGHT_UP,
     EL_TUBE_RIGHT_DOWN,
+
     -1
   };
 
@@ -2035,6 +4111,7 @@ void InitElementPropertiesStatic()
     EL_PACMAN,
     EL_SP_SNIKSNAK,
     EL_SP_ELECTRON,
+
     -1
   };
 
@@ -2052,6 +4129,7 @@ void InitElementPropertiesStatic()
     EL_CONVEYOR_BELT_4_LEFT,
     EL_CONVEYOR_BELT_4_MIDDLE,
     EL_CONVEYOR_BELT_4_RIGHT,
+
     -1
   };
 
@@ -2069,6 +4147,7 @@ void InitElementPropertiesStatic()
     EL_CONVEYOR_BELT_4_LEFT_ACTIVE,
     EL_CONVEYOR_BELT_4_MIDDLE_ACTIVE,
     EL_CONVEYOR_BELT_4_RIGHT_ACTIVE,
+
     -1
   };
 
@@ -2086,6 +4165,7 @@ void InitElementPropertiesStatic()
     EL_CONVEYOR_BELT_4_SWITCH_LEFT,
     EL_CONVEYOR_BELT_4_SWITCH_MIDDLE,
     EL_CONVEYOR_BELT_4_SWITCH_RIGHT,
+
     -1
   };
 
@@ -2102,6 +4182,18 @@ void InitElementPropertiesStatic()
     EL_TUBE_VERTICAL_LEFT,
     EL_TUBE_VERTICAL_RIGHT,
     EL_TUBE_ANY,
+
+    -1
+  };
+
+  static int ep_acid_pool[] =
+  {
+    EL_ACID_POOL_TOPLEFT,
+    EL_ACID_POOL_TOPRIGHT,
+    EL_ACID_POOL_BOTTOMLEFT,
+    EL_ACID_POOL_BOTTOM,
+    EL_ACID_POOL_BOTTOMRIGHT,
+
     -1
   };
 
@@ -2115,6 +4207,10 @@ void InitElementPropertiesStatic()
     EL_GATE_2_GRAY,
     EL_GATE_3_GRAY,
     EL_GATE_4_GRAY,
+    EL_GATE_1_GRAY_ACTIVE,
+    EL_GATE_2_GRAY_ACTIVE,
+    EL_GATE_3_GRAY_ACTIVE,
+    EL_GATE_4_GRAY_ACTIVE,
     EL_EM_GATE_1,
     EL_EM_GATE_2,
     EL_EM_GATE_3,
@@ -2123,6 +4219,26 @@ void InitElementPropertiesStatic()
     EL_EM_GATE_2_GRAY,
     EL_EM_GATE_3_GRAY,
     EL_EM_GATE_4_GRAY,
+    EL_EM_GATE_1_GRAY_ACTIVE,
+    EL_EM_GATE_2_GRAY_ACTIVE,
+    EL_EM_GATE_3_GRAY_ACTIVE,
+    EL_EM_GATE_4_GRAY_ACTIVE,
+    EL_EMC_GATE_5,
+    EL_EMC_GATE_6,
+    EL_EMC_GATE_7,
+    EL_EMC_GATE_8,
+    EL_EMC_GATE_5_GRAY,
+    EL_EMC_GATE_6_GRAY,
+    EL_EMC_GATE_7_GRAY,
+    EL_EMC_GATE_8_GRAY,
+    EL_EMC_GATE_5_GRAY_ACTIVE,
+    EL_EMC_GATE_6_GRAY_ACTIVE,
+    EL_EMC_GATE_7_GRAY_ACTIVE,
+    EL_EMC_GATE_8_GRAY_ACTIVE,
+    EL_DC_GATE_WHITE,
+    EL_DC_GATE_WHITE_GRAY,
+    EL_DC_GATE_WHITE_GRAY_ACTIVE,
+
     -1
   };
 
@@ -2133,6 +4249,8 @@ void InitElementPropertiesStatic()
     EL_AMOEBA_DRY,
     EL_AMOEBA_FULL,
     EL_BD_AMOEBA,
+    EL_EMC_DRIPPER,
+
     -1
   };
 
@@ -2142,27 +4260,64 @@ void InitElementPropertiesStatic()
     EL_AMOEBA_DRY,
     EL_AMOEBA_FULL,
     EL_BD_AMOEBA,
+    EL_EMC_DRIPPER,
+
     -1
   };
 
-  static int ep_has_content[] =
+  static int ep_has_editor_content[] =
   {
+    EL_PLAYER_1,
+    EL_PLAYER_2,
+    EL_PLAYER_3,
+    EL_PLAYER_4,
+    EL_SOKOBAN_FIELD_PLAYER,
+    EL_SP_MURPHY,
     EL_YAMYAM,
+    EL_YAMYAM_LEFT,
+    EL_YAMYAM_RIGHT,
+    EL_YAMYAM_UP,
+    EL_YAMYAM_DOWN,
     EL_AMOEBA_WET,
     EL_AMOEBA_DRY,
     EL_AMOEBA_FULL,
     EL_BD_AMOEBA,
+    EL_EMC_MAGIC_BALL,
+    EL_EMC_ANDROID,
+
+    -1
+  };
+
+  static int ep_can_turn_each_move[] =
+  {
+    /* !!! do something with this one !!! */
+    -1
+  };
+
+  static int ep_can_grow[] =
+  {
+    EL_BD_AMOEBA,
+    EL_AMOEBA_DROP,
+    EL_AMOEBA_WET,
+    EL_AMOEBA_DRY,
+    EL_AMOEBA_FULL,
+    EL_GAME_OF_LIFE,
+    EL_BIOMAZE,
+    EL_EMC_DRIPPER,
+
     -1
   };
 
   static int ep_active_bomb[] =
   {
     EL_DYNAMITE_ACTIVE,
+    EL_EM_DYNAMITE_ACTIVE,
     EL_DYNABOMB_PLAYER_1_ACTIVE,
     EL_DYNABOMB_PLAYER_2_ACTIVE,
     EL_DYNABOMB_PLAYER_3_ACTIVE,
     EL_DYNABOMB_PLAYER_4_ACTIVE,
     EL_SP_DISK_RED_ACTIVE,
+
     -1
   };
 
@@ -2172,10 +4327,11 @@ void InitElementPropertiesStatic()
     EL_SAND,
     EL_WALL,
     EL_BD_WALL,
-    EL_WALL_CRUMBLED,
+    EL_WALL_SLIPPERY,
     EL_STEELWALL,
     EL_AMOEBA_DEAD,
     EL_QUICKSAND_EMPTY,
+    EL_QUICKSAND_FAST_EMPTY,
     EL_STONEBLOCK,
     EL_ROBOT_WHEEL,
     EL_KEY_1,
@@ -2186,6 +4342,10 @@ void InitElementPropertiesStatic()
     EL_EM_KEY_2,
     EL_EM_KEY_3,
     EL_EM_KEY_4,
+    EL_EMC_KEY_5,
+    EL_EMC_KEY_6,
+    EL_EMC_KEY_7,
+    EL_EMC_KEY_8,
     EL_GATE_1,
     EL_GATE_2,
     EL_GATE_3,
@@ -2194,6 +4354,10 @@ void InitElementPropertiesStatic()
     EL_GATE_2_GRAY,
     EL_GATE_3_GRAY,
     EL_GATE_4_GRAY,
+    EL_GATE_1_GRAY_ACTIVE,
+    EL_GATE_2_GRAY_ACTIVE,
+    EL_GATE_3_GRAY_ACTIVE,
+    EL_GATE_4_GRAY_ACTIVE,
     EL_EM_GATE_1,
     EL_EM_GATE_2,
     EL_EM_GATE_3,
@@ -2202,7 +4366,28 @@ void InitElementPropertiesStatic()
     EL_EM_GATE_2_GRAY,
     EL_EM_GATE_3_GRAY,
     EL_EM_GATE_4_GRAY,
+    EL_EM_GATE_1_GRAY_ACTIVE,
+    EL_EM_GATE_2_GRAY_ACTIVE,
+    EL_EM_GATE_3_GRAY_ACTIVE,
+    EL_EM_GATE_4_GRAY_ACTIVE,
+    EL_EMC_GATE_5,
+    EL_EMC_GATE_6,
+    EL_EMC_GATE_7,
+    EL_EMC_GATE_8,
+    EL_EMC_GATE_5_GRAY,
+    EL_EMC_GATE_6_GRAY,
+    EL_EMC_GATE_7_GRAY,
+    EL_EMC_GATE_8_GRAY,
+    EL_EMC_GATE_5_GRAY_ACTIVE,
+    EL_EMC_GATE_6_GRAY_ACTIVE,
+    EL_EMC_GATE_7_GRAY_ACTIVE,
+    EL_EMC_GATE_8_GRAY_ACTIVE,
+    EL_DC_GATE_WHITE,
+    EL_DC_GATE_WHITE_GRAY,
+    EL_DC_GATE_WHITE_GRAY_ACTIVE,
+    EL_DC_GATE_FAKE_GRAY,
     EL_DYNAMITE,
+    EL_EM_DYNAMITE,
     EL_INVISIBLE_STEELWALL,
     EL_INVISIBLE_WALL,
     EL_INVISIBLE_SAND,
@@ -2215,7 +4400,9 @@ void InitElementPropertiesStatic()
     EL_DYNABOMB_INCREASE_NUMBER,
     EL_DYNABOMB_INCREASE_SIZE,
     EL_DYNABOMB_INCREASE_POWER,
+#if 0
     EL_SOKOBAN_OBJECT,
+#endif
     EL_SOKOBAN_FIELD_EMPTY,
     EL_SOKOBAN_FIELD_FULL,
     EL_WALL_EMERALD_RED,
@@ -2229,6 +4416,8 @@ void InitElementPropertiesStatic()
     EL_MAGIC_WALL_DEAD,
     EL_BD_MAGIC_WALL,
     EL_BD_MAGIC_WALL_DEAD,
+    EL_DC_MAGIC_WALL,
+    EL_DC_MAGIC_WALL_DEAD,
     EL_AMOEBA_TO_DIAMOND,
     EL_BLOCKED,
     EL_SP_EMPTY,
@@ -2245,7 +4434,9 @@ void InitElementPropertiesStatic()
     EL_SP_PORT_VERTICAL,
     EL_SP_PORT_ANY,
     EL_SP_DISK_RED,
+#if 0
     EL_SP_DISK_YELLOW,
+#endif
     EL_SP_CHIP_SINGLE,
     EL_SP_CHIP_LEFT,
     EL_SP_CHIP_RIGHT,
@@ -2262,6 +4453,14 @@ void InitElementPropertiesStatic()
     EL_SP_HARDWARE_BASE_4,
     EL_SP_HARDWARE_BASE_5,
     EL_SP_HARDWARE_BASE_6,
+    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,
     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
     EL_CONVEYOR_BELT_1_SWITCH_MIDDLE,
     EL_CONVEYOR_BELT_1_SWITCH_RIGHT,
@@ -2279,18 +4478,49 @@ void InitElementPropertiesStatic()
     EL_SIGN_STOP,
     EL_SIGN_WHEELCHAIR,
     EL_SIGN_PARKING,
-    EL_SIGN_ONEWAY,
+    EL_SIGN_NO_ENTRY,
+    EL_SIGN_UNUSED_1,
+    EL_SIGN_GIVE_WAY,
+    EL_SIGN_ENTRY_FORBIDDEN,
+    EL_SIGN_EMERGENCY_EXIT,
+    EL_SIGN_YIN_YANG,
+    EL_SIGN_UNUSED_2,
+    EL_SIGN_SPERMS,
+    EL_SIGN_BULLET,
     EL_SIGN_HEART,
-    EL_SIGN_TRIANGLE,
-    EL_SIGN_ROUND,
-    EL_SIGN_EXIT,
-    EL_SIGN_YINYANG,
-    EL_SIGN_OTHER,
-    EL_STEELWALL_SLANTED,
+    EL_SIGN_CROSS,
+    EL_SIGN_FRANKIE,
+    EL_DC_STEELWALL_1_LEFT,
+    EL_DC_STEELWALL_1_RIGHT,
+    EL_DC_STEELWALL_1_TOP,
+    EL_DC_STEELWALL_1_BOTTOM,
+    EL_DC_STEELWALL_1_HORIZONTAL,
+    EL_DC_STEELWALL_1_VERTICAL,
+    EL_DC_STEELWALL_1_TOPLEFT,
+    EL_DC_STEELWALL_1_TOPRIGHT,
+    EL_DC_STEELWALL_1_BOTTOMLEFT,
+    EL_DC_STEELWALL_1_BOTTOMRIGHT,
+    EL_DC_STEELWALL_1_TOPLEFT_2,
+    EL_DC_STEELWALL_1_TOPRIGHT_2,
+    EL_DC_STEELWALL_1_BOTTOMLEFT_2,
+    EL_DC_STEELWALL_1_BOTTOMRIGHT_2,
+    EL_DC_STEELWALL_2_LEFT,
+    EL_DC_STEELWALL_2_RIGHT,
+    EL_DC_STEELWALL_2_TOP,
+    EL_DC_STEELWALL_2_BOTTOM,
+    EL_DC_STEELWALL_2_HORIZONTAL,
+    EL_DC_STEELWALL_2_VERTICAL,
+    EL_DC_STEELWALL_2_MIDDLE,
+    EL_DC_STEELWALL_2_SINGLE,
+    EL_STEELWALL_SLIPPERY,
     EL_EMC_STEELWALL_1,
     EL_EMC_STEELWALL_2,
     EL_EMC_STEELWALL_3,
     EL_EMC_STEELWALL_4,
+    EL_EMC_WALL_SLIPPERY_1,
+    EL_EMC_WALL_SLIPPERY_2,
+    EL_EMC_WALL_SLIPPERY_3,
+    EL_EMC_WALL_SLIPPERY_4,
     EL_EMC_WALL_1,
     EL_EMC_WALL_2,
     EL_EMC_WALL_3,
@@ -2299,6 +4529,86 @@ void InitElementPropertiesStatic()
     EL_EMC_WALL_6,
     EL_EMC_WALL_7,
     EL_EMC_WALL_8,
+    EL_EMC_WALL_9,
+    EL_EMC_WALL_10,
+    EL_EMC_WALL_11,
+    EL_EMC_WALL_12,
+    EL_EMC_WALL_13,
+    EL_EMC_WALL_14,
+    EL_EMC_WALL_15,
+    EL_EMC_WALL_16,
+
+    -1
+  };
+
+  static int ep_em_slippery_wall[] =
+  {
+    -1
+  };
+
+  static int ep_gfx_crumbled[] =
+  {
+    EL_SAND,
+    EL_LANDMINE,
+    EL_DC_LANDMINE,
+    EL_TRAP,
+    EL_TRAP_ACTIVE,
+
+    -1
+  };
+
+  static int ep_editor_cascade_active[] =
+  {
+    EL_INTERNAL_CASCADE_BD_ACTIVE,
+    EL_INTERNAL_CASCADE_EM_ACTIVE,
+    EL_INTERNAL_CASCADE_EMC_ACTIVE,
+    EL_INTERNAL_CASCADE_RND_ACTIVE,
+    EL_INTERNAL_CASCADE_SB_ACTIVE,
+    EL_INTERNAL_CASCADE_SP_ACTIVE,
+    EL_INTERNAL_CASCADE_DC_ACTIVE,
+    EL_INTERNAL_CASCADE_DX_ACTIVE,
+    EL_INTERNAL_CASCADE_CHARS_ACTIVE,
+    EL_INTERNAL_CASCADE_STEEL_CHARS_ACTIVE,
+    EL_INTERNAL_CASCADE_CE_ACTIVE,
+    EL_INTERNAL_CASCADE_GE_ACTIVE,
+    EL_INTERNAL_CASCADE_REF_ACTIVE,
+    EL_INTERNAL_CASCADE_USER_ACTIVE,
+    EL_INTERNAL_CASCADE_DYNAMIC_ACTIVE,
+
+    -1
+  };
+
+  static int ep_editor_cascade_inactive[] =
+  {
+    EL_INTERNAL_CASCADE_BD,
+    EL_INTERNAL_CASCADE_EM,
+    EL_INTERNAL_CASCADE_EMC,
+    EL_INTERNAL_CASCADE_RND,
+    EL_INTERNAL_CASCADE_SB,
+    EL_INTERNAL_CASCADE_SP,
+    EL_INTERNAL_CASCADE_DC,
+    EL_INTERNAL_CASCADE_DX,
+    EL_INTERNAL_CASCADE_CHARS,
+    EL_INTERNAL_CASCADE_STEEL_CHARS,
+    EL_INTERNAL_CASCADE_CE,
+    EL_INTERNAL_CASCADE_GE,
+    EL_INTERNAL_CASCADE_REF,
+    EL_INTERNAL_CASCADE_USER,
+    EL_INTERNAL_CASCADE_DYNAMIC,
+
+    -1
+  };
+
+  static int ep_obsolete[] =
+  {
+    EL_PLAYER_OBSOLETE,
+    EL_KEY_OBSOLETE,
+    EL_EM_KEY_1_FILE_OBSOLETE,
+    EL_EM_KEY_2_FILE_OBSOLETE,
+    EL_EM_KEY_3_FILE_OBSOLETE,
+    EL_EM_KEY_4_FILE_OBSOLETE,
+    EL_ENVELOPE_OBSOLETE,
+
     -1
   };
 
@@ -2308,152 +4618,113 @@ void InitElementPropertiesStatic()
     int property;
   } element_properties[] =
   {
-    { ep_diggable,             EP_DIGGABLE             },
-    { ep_collectible,          EP_COLLECTIBLE          },
-    { ep_dont_run_into,                EP_DONT_RUN_INTO        },
-    { ep_dont_collide_with,    EP_DONT_COLLIDE_WITH    },
-    { ep_dont_touch,           EP_DONT_TOUCH           },
-    { ep_indestructible,       EP_INDESTRUCTIBLE       },
-    { ep_slippery,             EP_SLIPPERY             },
-    { ep_can_change,           EP_CAN_CHANGE           },
-    { ep_can_move,             EP_CAN_MOVE             },
-    { ep_can_fall,             EP_CAN_FALL             },
-    { ep_can_smash_player,     EP_CAN_SMASH_PLAYER     },
-    { ep_can_smash_enemies,    EP_CAN_SMASH_ENEMIES    },
-    { ep_can_smash_everything, EP_CAN_SMASH_EVERYTHING },
-    { ep_can_explode_by_fire,  EP_CAN_EXPLODE_BY_FIRE  },
-    { ep_can_explode_smashed,  EP_CAN_EXPLODE_SMASHED  },
-    { ep_can_explode_impact,   EP_CAN_EXPLODE_IMPACT   },
-    { ep_walkable_over,                EP_WALKABLE_OVER        },
-    { ep_walkable_inside,      EP_WALKABLE_INSIDE      },
-    { ep_walkable_under,       EP_WALKABLE_UNDER       },
-    { ep_passable_over,                EP_PASSABLE_OVER        },
-    { ep_passable_inside,      EP_PASSABLE_INSIDE      },
-    { ep_passable_under,       EP_PASSABLE_UNDER       },
-    { ep_pushable,             EP_PUSHABLE             },
-
-    { ep_can_be_crumbled,      EP_CAN_BE_CRUMBLED      },
-
-    { ep_player,               EP_PLAYER               },
-    { ep_can_pass_magic_wall,  EP_CAN_PASS_MAGIC_WALL  },
-    { ep_switchable,           EP_SWITCHABLE           },
-    { ep_bd_element,           EP_BD_ELEMENT           },
-    { ep_sp_element,           EP_SP_ELEMENT           },
-    { ep_sb_element,           EP_SB_ELEMENT           },
-    { ep_gem,                  EP_GEM                  },
-    { ep_food_dark_yamyam,     EP_FOOD_DARK_YAMYAM     },
-    { ep_food_penguin,         EP_FOOD_PENGUIN         },
-    { ep_food_pig,             EP_FOOD_PIG             },
-    { ep_historic_wall,                EP_HISTORIC_WALL        },
-    { ep_historic_solid,       EP_HISTORIC_SOLID       },
-    { ep_classic_enemy,                EP_CLASSIC_ENEMY        },
-    { ep_belt,                 EP_BELT                 },
-    { ep_belt_active,          EP_BELT_ACTIVE          },
-    { ep_belt_switch,          EP_BELT_SWITCH          },
-    { ep_tube,                 EP_TUBE                 },
-    { ep_keygate,              EP_KEYGATE              },
-    { ep_amoeboid,             EP_AMOEBOID             },
-    { ep_amoebalive,           EP_AMOEBALIVE           },
-    { ep_has_content,          EP_HAS_CONTENT          },
-    { ep_active_bomb,          EP_ACTIVE_BOMB          },
-    { ep_inactive,             EP_INACTIVE             },
-
-    { NULL,                    -1                      }
-  };
-
-  static int copy_properties[][5] =
-  {
-    {
-      EL_BUG,
-      EL_BUG_LEFT,             EL_BUG_RIGHT,
-      EL_BUG_UP,               EL_BUG_DOWN
-    },
-    {
-      EL_SPACESHIP,
-      EL_SPACESHIP_LEFT,       EL_SPACESHIP_RIGHT,
-      EL_SPACESHIP_UP,         EL_SPACESHIP_DOWN
-    },
-    {
-      EL_BD_BUTTERFLY,
-      EL_BD_BUTTERFLY_LEFT,    EL_BD_BUTTERFLY_RIGHT,
-      EL_BD_BUTTERFLY_UP,      EL_BD_BUTTERFLY_DOWN
-    },
-    {
-      EL_BD_FIREFLY,
-      EL_BD_FIREFLY_LEFT,      EL_BD_FIREFLY_RIGHT,
-      EL_BD_FIREFLY_UP,                EL_BD_FIREFLY_DOWN
-    },
-    {
-      EL_PACMAN,
-      EL_PACMAN_LEFT,          EL_PACMAN_RIGHT,
-      EL_PACMAN_UP,            EL_PACMAN_DOWN
-    },
-    {
-      -1,
-      -1, -1, -1, -1
-    }
+    { ep_diggable,                     EP_DIGGABLE                     },
+    { ep_collectible_only,             EP_COLLECTIBLE_ONLY             },
+    { ep_dont_run_into,                        EP_DONT_RUN_INTO                },
+    { ep_dont_collide_with,            EP_DONT_COLLIDE_WITH            },
+    { ep_dont_touch,                   EP_DONT_TOUCH                   },
+    { ep_indestructible,               EP_INDESTRUCTIBLE               },
+    { ep_slippery,                     EP_SLIPPERY                     },
+    { ep_can_change,                   EP_CAN_CHANGE                   },
+    { ep_can_move,                     EP_CAN_MOVE                     },
+    { ep_can_fall,                     EP_CAN_FALL                     },
+    { ep_can_smash_player,             EP_CAN_SMASH_PLAYER             },
+    { ep_can_smash_enemies,            EP_CAN_SMASH_ENEMIES            },
+    { ep_can_smash_everything,         EP_CAN_SMASH_EVERYTHING         },
+    { ep_explodes_by_fire,             EP_EXPLODES_BY_FIRE             },
+    { ep_explodes_smashed,             EP_EXPLODES_SMASHED             },
+    { ep_explodes_impact,              EP_EXPLODES_IMPACT              },
+    { ep_walkable_over,                        EP_WALKABLE_OVER                },
+    { ep_walkable_inside,              EP_WALKABLE_INSIDE              },
+    { ep_walkable_under,               EP_WALKABLE_UNDER               },
+    { ep_passable_over,                        EP_PASSABLE_OVER                },
+    { ep_passable_inside,              EP_PASSABLE_INSIDE              },
+    { ep_passable_under,               EP_PASSABLE_UNDER               },
+    { ep_droppable,                    EP_DROPPABLE                    },
+    { ep_explodes_1x1_old,             EP_EXPLODES_1X1_OLD             },
+    { ep_pushable,                     EP_PUSHABLE                     },
+    { ep_explodes_cross_old,           EP_EXPLODES_CROSS_OLD           },
+    { ep_protected,                    EP_PROTECTED                    },
+    { ep_throwable,                    EP_THROWABLE                    },
+    { ep_can_explode,                  EP_CAN_EXPLODE                  },
+    { ep_gravity_reachable,            EP_GRAVITY_REACHABLE            },
+
+    { ep_player,                       EP_PLAYER                       },
+    { ep_can_pass_magic_wall,          EP_CAN_PASS_MAGIC_WALL          },
+    { ep_can_pass_dc_magic_wall,       EP_CAN_PASS_DC_MAGIC_WALL       },
+    { ep_switchable,                   EP_SWITCHABLE                   },
+    { ep_bd_element,                   EP_BD_ELEMENT                   },
+    { ep_sp_element,                   EP_SP_ELEMENT                   },
+    { ep_sb_element,                   EP_SB_ELEMENT                   },
+    { ep_gem,                          EP_GEM                          },
+    { ep_food_dark_yamyam,             EP_FOOD_DARK_YAMYAM             },
+    { ep_food_penguin,                 EP_FOOD_PENGUIN                 },
+    { ep_food_pig,                     EP_FOOD_PIG                     },
+    { ep_historic_wall,                        EP_HISTORIC_WALL                },
+    { ep_historic_solid,               EP_HISTORIC_SOLID               },
+    { ep_classic_enemy,                        EP_CLASSIC_ENEMY                },
+    { ep_belt,                         EP_BELT                         },
+    { ep_belt_active,                  EP_BELT_ACTIVE                  },
+    { ep_belt_switch,                  EP_BELT_SWITCH                  },
+    { ep_tube,                         EP_TUBE                         },
+    { ep_acid_pool,                    EP_ACID_POOL                    },
+    { ep_keygate,                      EP_KEYGATE                      },
+    { ep_amoeboid,                     EP_AMOEBOID                     },
+    { ep_amoebalive,                   EP_AMOEBALIVE                   },
+    { ep_has_editor_content,           EP_HAS_EDITOR_CONTENT           },
+    { ep_can_turn_each_move,           EP_CAN_TURN_EACH_MOVE           },
+    { ep_can_grow,                     EP_CAN_GROW                     },
+    { ep_active_bomb,                  EP_ACTIVE_BOMB                  },
+    { ep_inactive,                     EP_INACTIVE                     },
+
+    { ep_em_slippery_wall,             EP_EM_SLIPPERY_WALL             },
+
+    { ep_gfx_crumbled,                 EP_GFX_CRUMBLED                 },
+
+    { ep_editor_cascade_active,                EP_EDITOR_CASCADE_ACTIVE        },
+    { ep_editor_cascade_inactive,      EP_EDITOR_CASCADE_INACTIVE      },
+
+    { ep_obsolete,                     EP_OBSOLETE                     },
+
+    { NULL,                            -1                              }
   };
 
   int i, j, k;
 
   /* always start with reliable default values (element has no properties) */
-  for (i=0; i < MAX_NUM_ELEMENTS; i++)
-    for (j=0; j < NUM_ELEMENT_PROPERTIES; j++)
-      SET_PROPERTY(i, j, FALSE);
+  /* (but never initialize clipboard elements after the very first time) */
+  /* (to be able to use clipboard elements between several levels) */
+  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+    if (!IS_CLIPBOARD_ELEMENT(i) || !clipboard_elements_initialized)
+      for (j = 0; j < NUM_ELEMENT_PROPERTIES; j++)
+       SET_PROPERTY(i, j, FALSE);
 
   /* set all base element properties from above array definitions */
-  for (i=0; element_properties[i].elements != NULL; i++)
-    for (j=0; (element_properties[i].elements)[j] != -1; j++)
+  for (i = 0; element_properties[i].elements != NULL; i++)
+    for (j = 0; (element_properties[i].elements)[j] != -1; j++)
       SET_PROPERTY((element_properties[i].elements)[j],
                   element_properties[i].property, TRUE);
 
   /* copy properties to some elements that are only stored in level file */
-  for (i=0; i < NUM_ELEMENT_PROPERTIES; i++)
-    for (j=0; copy_properties[j][0] != -1; j++)
+  for (i = 0; i < NUM_ELEMENT_PROPERTIES; i++)
+    for (j = 0; copy_properties[j][0] != -1; j++)
       if (HAS_PROPERTY(copy_properties[j][0], i))
-       for (k=1; k<=4; k++)
+       for (k = 1; k <= 4; k++)
          SET_PROPERTY(copy_properties[j][k], i, TRUE);
+
+  /* set static element properties that are not listed in array definitions */
+  for (i = EL_STEEL_CHAR_START; i <= EL_STEEL_CHAR_END; i++)
+    SET_PROPERTY(i, EP_INDESTRUCTIBLE, TRUE);
+
+  clipboard_elements_initialized = TRUE;
 }
 
 void InitElementPropertiesEngine(int engine_version)
 {
-#if 0
-  static int active_properties[] =
-  {
-    EP_AMOEBALIVE,
-    EP_AMOEBOID,
-    EP_PFORTE,
-    EP_DONT_COLLIDE_WITH,
-    EP_MAUER,
-    EP_CAN_FALL,
-    EP_CAN_SMASH,
-    EP_CAN_PASS_MAGIC_WALL,
-    EP_CAN_MOVE,
-    EP_DONT_TOUCH,
-    EP_DONT_RUN_INTO,
-    EP_GEM,
-    EP_CAN_EXPLODE_BY_FIRE,
-    EP_PUSHABLE,
-    EP_PLAYER,
-    EP_HAS_CONTENT,
-    EP_DIGGABLE,
-    EP_PASSABLE_INSIDE,
-    EP_OVER_PLAYER,
-    EP_ACTIVE_BOMB,
-
-    EP_BELT,
-    EP_BELT_ACTIVE,
-    EP_BELT_SWITCH,
-    EP_WALKABLE_UNDER,
-    EP_EM_SLIPPERY_WALL,
-    EP_CAN_BE_CRUMBLED,
-  };
-#endif
-
   static int no_wall_properties[] =
   {
     EP_DIGGABLE,
-    EP_COLLECTIBLE,
+    EP_COLLECTIBLE_ONLY,
     EP_DONT_RUN_INTO,
     EP_DONT_COLLIDE_WITH,
     EP_CAN_MOVE,
@@ -2463,8 +4734,6 @@ void InitElementPropertiesEngine(int engine_version)
     EP_CAN_SMASH_EVERYTHING,
     EP_PUSHABLE,
 
-    EP_CAN_BE_CRUMBLED,
-
     EP_PLAYER,
     EP_GEM,
     EP_FOOD_DARK_YAMYAM,
@@ -2477,26 +4746,34 @@ void InitElementPropertiesEngine(int engine_version)
     EP_ACTIVE_BOMB,
 
     EP_ACCESSIBLE,
+
     -1
   };
 
   int i, j;
 
-#if 0
-  InitElementPropertiesStatic();
-#endif
+  /* important: after initialization in InitElementPropertiesStatic(), the
+     elements are not again initialized to a default value; therefore all
+     changes have to make sure that they leave the element with a defined
+     property (which means that conditional property changes must be set to
+     a reliable default value before) */
 
-  /* set all special, combined or engine dependant element properties */
-  for (i=0; i < MAX_NUM_ELEMENTS; i++)
+  /* resolve group elements */
+  for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
+    ResolveGroupElement(EL_GROUP_START + i);
+
+  /* set all special, combined or engine dependent element properties */
+  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
   {
-#if 0
-    for (j=EP_ACCESSIBLE_OVER; j < NUM_ELEMENT_PROPERTIES; j++)
-      SET_PROPERTY(i, j, FALSE);
-#endif
+    /* do not change (already initialized) clipboard elements here */
+    if (IS_CLIPBOARD_ELEMENT(i))
+      continue;
 
     /* ---------- INACTIVE ------------------------------------------------- */
-    if (i >= EL_CHAR_START && i <= EL_CHAR_END)
-      SET_PROPERTY(i, EP_INACTIVE, TRUE);
+    SET_PROPERTY(i, EP_INACTIVE, ((i >= EL_CHAR_START &&
+                                  i <= EL_CHAR_END) ||
+                                 (i >= EL_STEEL_CHAR_START &&
+                                  i <= EL_STEEL_CHAR_END)));
 
     /* ---------- WALKABLE, PASSABLE, ACCESSIBLE --------------------------- */
     SET_PROPERTY(i, EP_WALKABLE, (IS_WALKABLE_OVER(i) ||
@@ -2519,6 +4796,11 @@ void InitElementPropertiesEngine(int engine_version)
     SET_PROPERTY(i, EP_ACCESSIBLE, (IS_WALKABLE(i) ||
                                    IS_PASSABLE(i)));
 
+    /* ---------- COLLECTIBLE ---------------------------------------------- */
+    SET_PROPERTY(i, EP_COLLECTIBLE, (IS_COLLECTIBLE_ONLY(i) ||
+                                    IS_DROPPABLE(i) ||
+                                    IS_THROWABLE(i)));
+
     /* ---------- SNAPPABLE ------------------------------------------------ */
     SET_PROPERTY(i, EP_SNAPPABLE, (IS_DIGGABLE(i) ||
                                   IS_COLLECTIBLE(i) ||
@@ -2528,7 +4810,7 @@ void InitElementPropertiesEngine(int engine_version)
     /* ---------- WALL ----------------------------------------------------- */
     SET_PROPERTY(i, EP_WALL, TRUE);    /* default: element is wall */
 
-    for (j=0; no_wall_properties[j] != -1; j++)
+    for (j = 0; no_wall_properties[j] != -1; j++)
       if (HAS_PROPERTY(i, no_wall_properties[j]) ||
          i >= EL_FIRST_RUNTIME_UNREAL)
        SET_PROPERTY(i, EP_WALL, FALSE);
@@ -2537,7 +4819,7 @@ void InitElementPropertiesEngine(int engine_version)
       SET_PROPERTY(i, EP_WALL, TRUE);
 
     /* ---------- SOLID_FOR_PUSHING ---------------------------------------- */
-    if (engine_version < VERSION_IDENT(2,2,0))
+    if (engine_version < VERSION_IDENT(2,2,0,0))
       SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, IS_HISTORIC_SOLID(i));
     else
       SET_PROPERTY(i, EP_SOLID_FOR_PUSHING, (!IS_WALKABLE(i) &&
@@ -2545,7 +4827,6 @@ void InitElementPropertiesEngine(int engine_version)
                                             !IS_COLLECTIBLE(i)));
 
     /* ---------- DRAGONFIRE_PROOF ----------------------------------------- */
-
     if (IS_HISTORIC_SOLID(i) || i == EL_EXPLOSION)
       SET_PROPERTY(i, EP_DRAGONFIRE_PROOF, TRUE);
     else
@@ -2555,15 +4836,17 @@ void InitElementPropertiesEngine(int engine_version)
     /* ---------- EXPLOSION_PROOF ------------------------------------------ */
     if (i == EL_FLAMES)
       SET_PROPERTY(i, EP_EXPLOSION_PROOF, TRUE);
-    else if (engine_version < VERSION_IDENT(2,2,0))
+    else if (engine_version < VERSION_IDENT(2,2,0,0))
       SET_PROPERTY(i, EP_EXPLOSION_PROOF, IS_INDESTRUCTIBLE(i));
     else
       SET_PROPERTY(i, EP_EXPLOSION_PROOF, (IS_INDESTRUCTIBLE(i) &&
-                                          !IS_WALKABLE_OVER(i) &&
-                                          !IS_WALKABLE_UNDER(i)));
+                                          (!IS_WALKABLE(i) ||
+                                           IS_PROTECTED(i))));
 
     if (IS_CUSTOM_ELEMENT(i))
     {
+      /* these are additional properties which are initially false when set */
+
       /* ---------- DONT_COLLIDE_WITH / DONT_RUN_INTO ---------------------- */
       if (DONT_TOUCH(i))
        SET_PROPERTY(i, EP_DONT_COLLIDE_WITH, TRUE);
@@ -2582,146 +4865,382 @@ void InitElementPropertiesEngine(int engine_version)
                                   CAN_SMASH_ENEMIES(i) ||
                                   CAN_SMASH_EVERYTHING(i)));
 
-    /* ---------- CAN_EXPLODE ---------------------------------------------- */
-    SET_PROPERTY(i, EP_CAN_EXPLODE, (CAN_EXPLODE_BY_FIRE(i) ||
-                                    CAN_EXPLODE_SMASHED(i) ||
-                                    CAN_EXPLODE_BY_FIRE(i)));
-  }
-
-#if 0
-  /* determine inactive elements (used for engine main loop optimization) */
-  for (i=0; i < MAX_NUM_ELEMENTS; i++)
-  {
-    boolean active = FALSE;
-
-    for (j=0; i < NUM_ELEMENT_PROPERTIES; j++)
-    {
-      if (HAS_PROPERTY(i, j))
-       active = TRUE;
-    }
-
-#if 0
-    if (!active)
-      SET_PROPERTY(i, EP_INACTIVE, TRUE);
+    /* ---------- CAN_EXPLODE_BY_FIRE -------------------------------------- */
+    SET_PROPERTY(i, EP_CAN_EXPLODE_BY_FIRE, (CAN_EXPLODE(i) &&
+                                            EXPLODES_BY_FIRE(i)));
+
+    /* ---------- CAN_EXPLODE_SMASHED -------------------------------------- */
+    SET_PROPERTY(i, EP_CAN_EXPLODE_SMASHED, (CAN_EXPLODE(i) &&
+                                            EXPLODES_SMASHED(i)));
+
+    /* ---------- CAN_EXPLODE_IMPACT --------------------------------------- */
+    SET_PROPERTY(i, EP_CAN_EXPLODE_IMPACT, (CAN_EXPLODE(i) &&
+                                           EXPLODES_IMPACT(i)));
+
+    /* ---------- CAN_EXPLODE_BY_DRAGONFIRE -------------------------------- */
+    SET_PROPERTY(i, EP_CAN_EXPLODE_BY_DRAGONFIRE, CAN_EXPLODE_BY_FIRE(i));
+
+    /* ---------- CAN_EXPLODE_BY_EXPLOSION --------------------------------- */
+    SET_PROPERTY(i, EP_CAN_EXPLODE_BY_EXPLOSION, (CAN_EXPLODE_BY_FIRE(i) ||
+                                                 i == EL_BLACK_ORB));
+
+    /* ---------- COULD_MOVE_INTO_ACID ------------------------------------- */
+    SET_PROPERTY(i, EP_COULD_MOVE_INTO_ACID, (ELEM_IS_PLAYER(i) ||
+                                             CAN_MOVE(i) ||
+                                             IS_CUSTOM_ELEMENT(i)));
+
+    /* ---------- MAYBE_DONT_COLLIDE_WITH ---------------------------------- */
+    SET_PROPERTY(i, EP_MAYBE_DONT_COLLIDE_WITH, (i == EL_SP_SNIKSNAK ||
+                                                i == EL_SP_ELECTRON));
+
+    /* ---------- CAN_MOVE_INTO_ACID --------------------------------------- */
+    if (COULD_MOVE_INTO_ACID(i) && !IS_CUSTOM_ELEMENT(i))
+      SET_PROPERTY(i, EP_CAN_MOVE_INTO_ACID,
+                  getMoveIntoAcidProperty(&level, i));
+
+    /* ---------- DONT_COLLIDE_WITH ---------------------------------------- */
+    if (MAYBE_DONT_COLLIDE_WITH(i))
+      SET_PROPERTY(i, EP_DONT_COLLIDE_WITH,
+                  getDontCollideWithProperty(&level, i));
+
+    /* ---------- SP_PORT -------------------------------------------------- */
+    SET_PROPERTY(i, EP_SP_PORT, (IS_SP_ELEMENT(i) &&
+                                IS_PASSABLE_INSIDE(i)));
+
+    /* ---------- CAN_BE_CLONED_BY_ANDROID --------------------------------- */
+    for (j = 0; j < level.num_android_clone_elements; j++)
+      SET_PROPERTY(i, EP_CAN_BE_CLONED_BY_ANDROID,
+                  (i != EL_EMPTY &&
+                   IS_EQUAL_OR_IN_GROUP(i, level.android_clone_element[j])));
+
+    /* ---------- CAN_CHANGE ----------------------------------------------- */
+    SET_PROPERTY(i, EP_CAN_CHANGE, FALSE);     /* default: cannot change */
+    for (j = 0; j < element_info[i].num_change_pages; j++)
+      if (element_info[i].change_page[j].can_change)
+       SET_PROPERTY(i, EP_CAN_CHANGE, TRUE);
+
+    /* ---------- HAS_ACTION ----------------------------------------------- */
+    SET_PROPERTY(i, EP_HAS_ACTION, FALSE);     /* default: has no action */
+    for (j = 0; j < element_info[i].num_change_pages; j++)
+      if (element_info[i].change_page[j].has_action)
+       SET_PROPERTY(i, EP_HAS_ACTION, TRUE);
+
+    /* ---------- CAN_CHANGE_OR_HAS_ACTION --------------------------------- */
+    SET_PROPERTY(i, EP_CAN_CHANGE_OR_HAS_ACTION, (CAN_CHANGE(i) ||
+                                                 HAS_ACTION(i)));
+
+    /* ---------- GFX_CRUMBLED --------------------------------------------- */
+#if 1
+    SET_PROPERTY(i, EP_GFX_CRUMBLED,
+                element_info[i].crumbled[ACTION_DEFAULT] !=
+                element_info[i].graphic[ACTION_DEFAULT]);
+#else
+    /* !!! THIS LOOKS CRAPPY FOR SAND ETC. WITHOUT CRUMBLED GRAPHICS !!! */
+    SET_PROPERTY(i, EP_GFX_CRUMBLED,
+                element_info[i].crumbled[ACTION_DEFAULT] != IMG_EMPTY);
 #endif
+
+    /* ---------- EDITOR_CASCADE ------------------------------------------- */
+    SET_PROPERTY(i, EP_EDITOR_CASCADE, (IS_EDITOR_CASCADE_ACTIVE(i) ||
+                                       IS_EDITOR_CASCADE_INACTIVE(i)));
   }
-#endif
 
   /* dynamically adjust element properties according to game engine version */
   {
     static int ep_em_slippery_wall[] =
     {
-      EL_STEELWALL,
       EL_WALL,
+      EL_STEELWALL,
       EL_EXPANDABLE_WALL,
       EL_EXPANDABLE_WALL_HORIZONTAL,
       EL_EXPANDABLE_WALL_VERTICAL,
       EL_EXPANDABLE_WALL_ANY,
+      EL_EXPANDABLE_STEELWALL_HORIZONTAL,
+      EL_EXPANDABLE_STEELWALL_VERTICAL,
+      EL_EXPANDABLE_STEELWALL_ANY,
+      EL_EXPANDABLE_STEELWALL_GROWING,
+      -1
+    };
+
+    static int ep_em_explodes_by_fire[] =
+    {
+      EL_EM_DYNAMITE,
+      EL_EM_DYNAMITE_ACTIVE,
+      EL_MOLE,
       -1
     };
 
     /* special EM style gems behaviour */
-    for (i=0; ep_em_slippery_wall[i] != -1; i++)
+    for (i = 0; ep_em_slippery_wall[i] != -1; i++)
       SET_PROPERTY(ep_em_slippery_wall[i], EP_EM_SLIPPERY_WALL,
                   level.em_slippery_gems);
 
     /* "EL_EXPANDABLE_WALL_GROWING" wasn't slippery for EM gems in 2.0.1 */
     SET_PROPERTY(EL_EXPANDABLE_WALL_GROWING, EP_EM_SLIPPERY_WALL,
                 (level.em_slippery_gems &&
-                 engine_version > VERSION_IDENT(2,0,1)));
+                 engine_version > VERSION_IDENT(2,0,1,0)));
+
+    /* special EM style explosion behaviour regarding chain reactions */
+    for (i = 0; ep_em_explodes_by_fire[i] != -1; i++)
+      SET_PROPERTY(ep_em_explodes_by_fire[i], EP_EXPLODES_BY_FIRE,
+                  level.em_explodes_by_fire);
   }
 
-  /* dynamically adjust element properties according to game engine version */
-#if 0
-  if (engine_version < RELEASE_IDENT(2,2,0,7))
-#endif
+  /* this is needed because some graphics depend on element properties */
+  if (game_status == GAME_MODE_PLAYING)
+    InitElementGraphicInfo();
+}
+
+void InitElementPropertiesAfterLoading(int engine_version)
+{
+  int i;
+
+  /* set some other uninitialized values of custom elements in older levels */
+  if (engine_version < VERSION_IDENT(3,1,0,0))
   {
-    for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+    for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
     {
       int element = EL_CUSTOM_START + i;
 
-      element_info[element].push_delay_fixed = 2;
-      element_info[element].push_delay_random = 8;
+      element_info[element].access_direction = MV_ALL_DIRECTIONS;
+
+      element_info[element].explosion_delay = 17;
+      element_info[element].ignition_delay = 8;
     }
   }
 }
 
+void InitElementPropertiesGfxElement()
+{
+  int i;
+
+  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+  {
+    struct ElementInfo *ei = &element_info[i];
+
+    ei->gfx_element = (ei->use_gfx_element ? ei->gfx_element_initial : i);
+  }
+}
+
 static void InitGlobal()
 {
+  int graphic;
+  int i;
+
+  for (i = 0; i < MAX_NUM_ELEMENTS + 1; i++)
+  {
+    /* check if element_name_info entry defined for each element in "main.h" */
+    if (i < MAX_NUM_ELEMENTS && element_name_info[i].token_name == NULL)
+      Error(ERR_EXIT, "undefined 'element_name_info' entry for element %d", i);
+
+    element_info[i].token_name = element_name_info[i].token_name;
+    element_info[i].class_name = element_name_info[i].class_name;
+    element_info[i].editor_description= element_name_info[i].editor_description;
+
+#if 0
+    printf("%04d: %s\n", i, element_name_info[i].token_name);
+#endif
+  }
+
+  /* create hash from image config list */
+  image_config_hash = newSetupFileHash();
+  for (i = 0; image_config[i].token != NULL; i++)
+    setHashEntry(image_config_hash,
+                image_config[i].token,
+                image_config[i].value);
+
+  /* create hash from element token list */
+  element_token_hash = newSetupFileHash();
+  for (i = 0; element_name_info[i].token_name != NULL; i++)
+    setHashEntry(element_token_hash,
+                element_name_info[i].token_name,
+                int2str(i, 0));
+
+  /* create hash from graphic token list */
+  graphic_token_hash = newSetupFileHash();
+  for (graphic = 0, i = 0; image_config[i].token != NULL; i++)
+    if (strSuffix(image_config[i].value, ".pcx") ||
+       strSuffix(image_config[i].value, ".wav") ||
+       strEqual(image_config[i].value, UNDEFINED_FILENAME))
+      setHashEntry(graphic_token_hash,
+                  image_config[i].token,
+                  int2str(graphic++, 0));
+
+  /* create hash from font token list */
+  font_token_hash = newSetupFileHash();
+  for (i = 0; font_info[i].token_name != NULL; i++)
+    setHashEntry(font_token_hash,
+                font_info[i].token_name,
+                int2str(i, 0));
+
+  /* always start with reliable default values (all elements) */
+  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+    ActiveElement[i] = i;
+
+  /* now add all entries that have an active state (active elements) */
+  for (i = 0; element_with_active_state[i].element != -1; i++)
+  {
+    int element = element_with_active_state[i].element;
+    int element_active = element_with_active_state[i].element_active;
+
+    ActiveElement[element] = element_active;
+  }
+
+  /* always start with reliable default values (all buttons) */
+  for (i = 0; i < NUM_IMAGE_FILES; i++)
+    ActiveButton[i] = i;
+
+  /* now add all entries that have an active state (active buttons) */
+  for (i = 0; button_with_active_state[i].button != -1; i++)
+  {
+    int button = button_with_active_state[i].button;
+    int button_active = button_with_active_state[i].button_active;
+
+    ActiveButton[button] = button_active;
+  }
+
+  /* always start with reliable default values (all fonts) */
+  for (i = 0; i < NUM_FONTS; i++)
+    ActiveFont[i] = i;
+
+  /* now add all entries that have an active state (active fonts) */
+  for (i = 0; font_with_active_state[i].font_nr != -1; i++)
+  {
+    int font = font_with_active_state[i].font_nr;
+    int font_active = font_with_active_state[i].font_nr_active;
+
+    ActiveFont[font] = font_active;
+  }
+
   global.autoplay_leveldir = NULL;
+  global.convert_leveldir = NULL;
+  global.create_images_dir = NULL;
 
   global.frames_per_second = 0;
   global.fps_slowdown = FALSE;
   global.fps_slowdown_factor = 1;
+
+  global.border_status = GAME_MODE_MAIN;
+#if 0
+  global.fading_status = GAME_MODE_MAIN;
+  global.fading_type = TYPE_ENTER_MENU;
+#endif
+
+  global.use_envelope_request = FALSE; /* !!! MOVE TO ARTWORK CONFIG !!! */
 }
 
 void Execute_Command(char *command)
 {
-  if (strcmp(command, "print graphicsinfo.conf") == 0)
-  {
-    int i;
+  int i;
 
+  if (strEqual(command, "print graphicsinfo.conf"))
+  {
     printf("# You can configure additional/alternative image files here.\n");
-    printf("# (The images below are default and therefore commented out.)\n");
+    printf("# (The entries below are default and therefore commented out.)\n");
     printf("\n");
     printf("%s\n", getFormattedSetupEntry("name", "Classic Graphics"));
     printf("\n");
     printf("%s\n", getFormattedSetupEntry("sort_priority", "100"));
     printf("\n");
 
-    for (i=0; image_config[i].token != NULL; i++)
-      printf("# %s\n",
-            getFormattedSetupEntry(image_config[i].token,
-                                   image_config[i].value));
+    for (i = 0; image_config[i].token != NULL; i++)
+      printf("# %s\n", getFormattedSetupEntry(image_config[i].token,
+                                             image_config[i].value));
 
     exit(0);
   }
-  else if (strcmp(command, "print soundsinfo.conf") == 0)
+  else if (strEqual(command, "print soundsinfo.conf"))
   {
-    int i;
-
     printf("# You can configure additional/alternative sound files here.\n");
-    printf("# (The sounds below are default and therefore commented out.)\n");
+    printf("# (The entries below are default and therefore commented out.)\n");
     printf("\n");
     printf("%s\n", getFormattedSetupEntry("name", "Classic Sounds"));
     printf("\n");
     printf("%s\n", getFormattedSetupEntry("sort_priority", "100"));
     printf("\n");
 
-    for (i=0; sound_config[i].token != NULL; i++)
-      printf("# %s\n",
-            getFormattedSetupEntry(sound_config[i].token,
-                                   sound_config[i].value));
+    for (i = 0; sound_config[i].token != NULL; i++)
+      printf("# %s\n", getFormattedSetupEntry(sound_config[i].token,
+                                             sound_config[i].value));
 
     exit(0);
   }
-  else if (strcmp(command, "print musicinfo.conf") == 0)
+  else if (strEqual(command, "print musicinfo.conf"))
   {
-    printf("# (Currently only \"name\" and \"sort_priority\" recognized.)\n");
+    printf("# You can configure additional/alternative music files here.\n");
+    printf("# (The entries below are default and therefore commented out.)\n");
     printf("\n");
     printf("%s\n", getFormattedSetupEntry("name", "Classic Music"));
     printf("\n");
     printf("%s\n", getFormattedSetupEntry("sort_priority", "100"));
+    printf("\n");
+
+    for (i = 0; music_config[i].token != NULL; i++)
+      printf("# %s\n", getFormattedSetupEntry(music_config[i].token,
+                                             music_config[i].value));
+
+    exit(0);
+  }
+  else if (strEqual(command, "print editorsetup.conf"))
+  {
+    printf("# You can configure your personal editor element list here.\n");
+    printf("# (The entries below are default and therefore commented out.)\n");
+    printf("\n");
+
+    /* this is needed to be able to check element list for cascade elements */
+    InitElementPropertiesStatic();
+    InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
+
+    PrintEditorElementList();
 
     exit(0);
   }
-  else if (strncmp(command, "dump level ", 11) == 0)
+  else if (strEqual(command, "print helpanim.conf"))
+  {
+    printf("# You can configure different element help animations here.\n");
+    printf("# (The entries below are default and therefore commented out.)\n");
+    printf("\n");
+
+    for (i = 0; helpanim_config[i].token != NULL; i++)
+    {
+      printf("# %s\n", getFormattedSetupEntry(helpanim_config[i].token,
+                                             helpanim_config[i].value));
+
+      if (strEqual(helpanim_config[i].token, "end"))
+       printf("#\n");
+    }
+
+    exit(0);
+  }
+  else if (strEqual(command, "print helptext.conf"))
+  {
+    printf("# You can configure different element help text here.\n");
+    printf("# (The entries below are default and therefore commented out.)\n");
+    printf("\n");
+
+    for (i = 0; helptext_config[i].token != NULL; i++)
+      printf("# %s\n", getFormattedSetupEntry(helptext_config[i].token,
+                                             helptext_config[i].value));
+
+    exit(0);
+  }
+  else if (strPrefix(command, "dump level "))
   {
     char *filename = &command[11];
 
-    if (access(filename, F_OK) != 0)
+    if (!fileExists(filename))
       Error(ERR_EXIT, "cannot open file '%s'", filename);
 
-    LoadLevelFromFilename(filename);
+    LoadLevelFromFilename(&level, filename);
     DumpLevel(&level);
 
     exit(0);
   }
-  else if (strncmp(command, "dump tape ", 10) == 0)
+  else if (strPrefix(command, "dump tape "))
   {
     char *filename = &command[10];
 
-    if (access(filename, F_OK) != 0)
+    if (!fileExists(filename))
       Error(ERR_EXIT, "cannot open file '%s'", filename);
 
     LoadTapeFromFilename(filename);
@@ -2729,20 +5248,109 @@ void Execute_Command(char *command)
 
     exit(0);
   }
-  else if (strncmp(command, "autoplay ", 9) == 0)
+  else if (strPrefix(command, "autoplay "))
   {
-    char *str_copy = getStringCopy(&command[9]);
+    char *str_ptr = getStringCopy(&command[9]);        /* read command parameters */
+
+    while (*str_ptr != '\0')                   /* continue parsing string */
+    {
+      /* cut leading whitespace from string, replace it by string terminator */
+      while (*str_ptr == ' ' || *str_ptr == '\t')
+       *str_ptr++ = '\0';
+
+      if (*str_ptr == '\0')                    /* end of string reached */
+       break;
+
+      if (global.autoplay_leveldir == NULL)    /* read level set string */
+      {
+       global.autoplay_leveldir = str_ptr;
+       global.autoplay_all = TRUE;             /* default: play all tapes */
+
+       for (i = 0; i < MAX_TAPES_PER_SET; i++)
+         global.autoplay_level[i] = FALSE;
+      }
+      else                                     /* read level number string */
+      {
+       int level_nr = atoi(str_ptr);           /* get level_nr value */
+
+       if (level_nr >= 0 && level_nr < MAX_TAPES_PER_SET)
+         global.autoplay_level[level_nr] = TRUE;
+
+       global.autoplay_all = FALSE;
+      }
+
+      /* advance string pointer to the next whitespace (or end of string) */
+      while (*str_ptr != ' ' && *str_ptr != '\t' && *str_ptr != '\0')
+       str_ptr++;
+    }
+  }
+  else if (strPrefix(command, "convert "))
+  {
+    char *str_copy = getStringCopy(strchr(command, ' ') + 1);
     char *str_ptr = strchr(str_copy, ' ');
 
-    global.autoplay_leveldir = str_copy;
-    global.autoplay_level_nr = -1;
+    global.convert_leveldir = str_copy;
+    global.convert_level_nr = -1;
 
-    if (str_ptr != NULL)
+    if (str_ptr != NULL)                       /* level number follows */
     {
       *str_ptr++ = '\0';                       /* terminate leveldir string */
-      global.autoplay_level_nr = atoi(str_ptr);        /* get level_nr value */
+      global.convert_level_nr = atoi(str_ptr); /* get level_nr value */
     }
   }
+  else if (strPrefix(command, "create images "))
+  {
+#if defined(TARGET_SDL)
+    global.create_images_dir = getStringCopy(&command[14]);
+
+    if (access(global.create_images_dir, W_OK) != 0)
+      Error(ERR_EXIT, "image target directory '%s' not found or not writable",
+           global.create_images_dir);
+#else
+    Error(ERR_EXIT, "command only available for SDL target");
+#endif
+  }
+
+#if DEBUG
+#if defined(TARGET_SDL)
+#if !defined(TARGET_SDL2)
+  else if (strEqual(command, "SDL_ListModes"))
+  {
+    SDL_Rect **modes;
+    int i;
+
+    SDL_Init(SDL_INIT_VIDEO);
+
+    /* get available fullscreen/hardware modes */
+    modes = SDL_ListModes(NULL, SDL_FULLSCREEN | SDL_HWSURFACE);
+
+    /* check if there are any modes available */
+    if (modes == NULL)
+    {
+      printf("No modes available!\n");
+
+      exit(-1);
+    }
+
+    /* check if our resolution is restricted */
+    if (modes == (SDL_Rect **)-1)
+    {
+      printf("All resolutions available.\n");
+    }
+    else
+    {
+      printf("Available Modes:\n");
+
+      for(i = 0; modes[i]; i++)
+       printf("  %d x %d\n", modes[i]->w, modes[i]->h);
+    }
+
+    exit(0);
+  }
+#endif
+#endif
+#endif
+
   else
   {
     Error(ERR_EXIT_HELP, "unrecognized command '%s'", command);
@@ -2759,6 +5367,11 @@ static void InitSetup()
     options.verbose = TRUE;
 }
 
+static void InitGameInfo()
+{
+  game.restart_level = FALSE;
+}
+
 static void InitPlayerInfo()
 {
   int i;
@@ -2766,7 +5379,7 @@ static void InitPlayerInfo()
   /* choose default local player */
   local_player = &stored_player[0];
 
-  for (i=0; i<MAX_PLAYERS; i++)
+  for (i = 0; i < MAX_PLAYERS; i++)
     stored_player[i].connected = FALSE;
 
   local_player->connected = TRUE;
@@ -2777,6 +5390,28 @@ static void InitArtworkInfo()
   LoadArtworkInfo();
 }
 
+static char *get_string_in_brackets(char *string)
+{
+  char *string_in_brackets = checked_malloc(strlen(string) + 3);
+
+  sprintf(string_in_brackets, "[%s]", string);
+
+  return string_in_brackets;
+}
+
+static char *get_level_id_suffix(int id_nr)
+{
+  char *id_suffix = checked_malloc(1 + 3 + 1);
+
+  if (id_nr < 0 || id_nr > 999)
+    id_nr = 0;
+
+  sprintf(id_suffix, ".%03d", id_nr);
+
+  return id_suffix;
+}
+
+#if 0
 static char *get_element_class_token(int element)
 {
   char *element_class_name = element_info[element].class_name;
@@ -2787,13 +5422,26 @@ static char *get_element_class_token(int element)
   return element_class_token;
 }
 
+static char *get_action_class_token(int action)
+{
+  char *action_class_name = &element_action_info[action].suffix[1];
+  char *action_class_token = checked_malloc(strlen(action_class_name) + 3);
+
+  sprintf(action_class_token, "[%s]", action_class_name);
+
+  return action_class_token;
+}
+#endif
+
 static void InitArtworkConfig()
 {
   static char *image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS + 1];
-  static char *sound_id_prefix[MAX_NUM_ELEMENTS + MAX_NUM_ELEMENTS + 1];
+  static char *sound_id_prefix[2 * MAX_NUM_ELEMENTS + 1];
+  static char *music_id_prefix[NUM_MUSIC_PREFIXES + 1];
   static char *action_id_suffix[NUM_ACTIONS + 1];
-  static char *direction_id_suffix[NUM_DIRECTIONS + 1];
+  static char *direction_id_suffix[NUM_DIRECTIONS_FULL + 1];
   static char *special_id_suffix[NUM_SPECIAL_GFX_ARGS + 1];
+  static char *level_id_suffix[MAX_LEVELS + 1];
   static char *dummy[1] = { NULL };
   static char *ignore_generic_tokens[] =
   {
@@ -2801,25 +5449,29 @@ static void InitArtworkConfig()
     "sort_priority",
     NULL
   };
-  static char **ignore_image_tokens, **ignore_sound_tokens;
+  static char **ignore_image_tokens;
+  static char **ignore_sound_tokens;
+  static char **ignore_music_tokens;
   int num_ignore_generic_tokens;
-  int num_ignore_image_tokens, num_ignore_sound_tokens;
+  int num_ignore_image_tokens;
+  int num_ignore_sound_tokens;
+  int num_ignore_music_tokens;
   int i;
 
   /* dynamically determine list of generic tokens to be ignored */
   num_ignore_generic_tokens = 0;
-  for (i=0; ignore_generic_tokens[i] != NULL; i++)
+  for (i = 0; ignore_generic_tokens[i] != NULL; i++)
     num_ignore_generic_tokens++;
 
   /* dynamically determine list of image tokens to be ignored */
   num_ignore_image_tokens = num_ignore_generic_tokens;
-  for (i=0; image_config_vars[i].token != NULL; i++)
+  for (i = 0; image_config_vars[i].token != NULL; i++)
     num_ignore_image_tokens++;
   ignore_image_tokens =
     checked_malloc((num_ignore_image_tokens + 1) * sizeof(char *));
-  for (i=0; i < num_ignore_generic_tokens; i++)
+  for (i = 0; i < num_ignore_generic_tokens; i++)
     ignore_image_tokens[i] = ignore_generic_tokens[i];
-  for (i=0; i < num_ignore_image_tokens - num_ignore_generic_tokens; i++)
+  for (i = 0; i < num_ignore_image_tokens - num_ignore_generic_tokens; i++)
     ignore_image_tokens[num_ignore_generic_tokens + i] =
       image_config_vars[i].token;
   ignore_image_tokens[num_ignore_image_tokens] = NULL;
@@ -2828,58 +5480,105 @@ static void InitArtworkConfig()
   num_ignore_sound_tokens = num_ignore_generic_tokens;
   ignore_sound_tokens =
     checked_malloc((num_ignore_sound_tokens + 1) * sizeof(char *));
-  for (i=0; i < num_ignore_generic_tokens; i++)
+  for (i = 0; i < num_ignore_generic_tokens; i++)
     ignore_sound_tokens[i] = ignore_generic_tokens[i];
   ignore_sound_tokens[num_ignore_sound_tokens] = NULL;
 
-  for (i=0; i<MAX_NUM_ELEMENTS; i++)
+  /* dynamically determine list of music tokens to be ignored */
+  num_ignore_music_tokens = num_ignore_generic_tokens;
+  ignore_music_tokens =
+    checked_malloc((num_ignore_music_tokens + 1) * sizeof(char *));
+  for (i = 0; i < num_ignore_generic_tokens; i++)
+    ignore_music_tokens[i] = ignore_generic_tokens[i];
+  ignore_music_tokens[num_ignore_music_tokens] = NULL;
+
+  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
     image_id_prefix[i] = element_info[i].token_name;
-  for (i=0; i<NUM_FONTS; i++)
+  for (i = 0; i < NUM_FONTS; i++)
     image_id_prefix[MAX_NUM_ELEMENTS + i] = font_info[i].token_name;
   image_id_prefix[MAX_NUM_ELEMENTS + NUM_FONTS] = NULL;
 
-  for (i=0; i<MAX_NUM_ELEMENTS; i++)
+  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
     sound_id_prefix[i] = element_info[i].token_name;
-  for (i=0; i<MAX_NUM_ELEMENTS; i++)
-    sound_id_prefix[MAX_NUM_ELEMENTS + i] = get_element_class_token(i);
-  sound_id_prefix[MAX_NUM_ELEMENTS + MAX_NUM_ELEMENTS] = NULL;
+  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+    sound_id_prefix[MAX_NUM_ELEMENTS + i] =
+      get_string_in_brackets(element_info[i].class_name);
+  sound_id_prefix[2 * MAX_NUM_ELEMENTS] = NULL;
 
-  for (i=0; i<NUM_ACTIONS; i++)
+  for (i = 0; i < NUM_MUSIC_PREFIXES; i++)
+    music_id_prefix[i] = music_prefix_info[i].prefix;
+  music_id_prefix[NUM_MUSIC_PREFIXES] = NULL;
+
+  for (i = 0; i < NUM_ACTIONS; i++)
     action_id_suffix[i] = element_action_info[i].suffix;
   action_id_suffix[NUM_ACTIONS] = NULL;
 
-  for (i=0; i<NUM_DIRECTIONS; i++)
+  for (i = 0; i < NUM_DIRECTIONS_FULL; i++)
     direction_id_suffix[i] = element_direction_info[i].suffix;
-  direction_id_suffix[NUM_DIRECTIONS] = NULL;
+  direction_id_suffix[NUM_DIRECTIONS_FULL] = NULL;
 
-  for (i=0; i<NUM_SPECIAL_GFX_ARGS; i++)
+  for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
     special_id_suffix[i] = special_suffix_info[i].suffix;
   special_id_suffix[NUM_SPECIAL_GFX_ARGS] = NULL;
 
+  for (i = 0; i < MAX_LEVELS; i++)
+    level_id_suffix[i] = get_level_id_suffix(i);
+  level_id_suffix[MAX_LEVELS] = NULL;
+
   InitImageList(image_config, NUM_IMAGE_FILES, image_config_suffix,
                image_id_prefix, action_id_suffix, direction_id_suffix,
                special_id_suffix, ignore_image_tokens);
   InitSoundList(sound_config, NUM_SOUND_FILES, sound_config_suffix,
                sound_id_prefix, action_id_suffix, dummy,
                special_id_suffix, ignore_sound_tokens);
+  InitMusicList(music_config, NUM_MUSIC_FILES, music_config_suffix,
+               music_id_prefix, special_id_suffix, level_id_suffix,
+               dummy, ignore_music_tokens);
 }
 
 static void InitMixer()
 {
   OpenAudio();
+
   StartMixer();
 }
 
+void InitGfxBuffers()
+{
+  ReCreateBitmap(&bitmap_db_store, WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
+  ReCreateBitmap(&bitmap_db_cross, WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
+  ReCreateBitmap(&bitmap_db_field, FXSIZE, FYSIZE, DEFAULT_DEPTH);
+  ReCreateBitmap(&bitmap_db_panel, DXSIZE, DYSIZE, DEFAULT_DEPTH);
+  ReCreateBitmap(&bitmap_db_door, 3 * DXSIZE, DYSIZE + VYSIZE, DEFAULT_DEPTH);
+  ReCreateBitmap(&bitmap_db_toons, FULL_SXSIZE, FULL_SYSIZE, DEFAULT_DEPTH);
+
+  /* initialize screen properties */
+  InitGfxFieldInfo(SX, SY, SXSIZE, SYSIZE,
+                  REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
+                  bitmap_db_field);
+  InitGfxDoor1Info(DX, DY, DXSIZE, DYSIZE);
+  InitGfxDoor2Info(VX, VY, VXSIZE, VYSIZE);
+  InitGfxWindowInfo(WIN_XSIZE, WIN_YSIZE);
+  InitGfxScrollbufferInfo(FXSIZE, FYSIZE);
+  InitGfxClipRegion(FALSE, -1, -1, -1, -1);
+
+  InitGfxBuffers_EM();
+  InitGfxBuffers_SP();
+}
+
 void InitGfx()
 {
+  struct GraphicInfo *graphic_info_last = graphic_info;
   char *filename_font_initial = NULL;
+  char *filename_anim_initial = NULL;
   Bitmap *bitmap_font_initial = NULL;
+  int font_height;
   int i, j;
 
   /* determine settings for initial font (for displaying startup messages) */
-  for (i=0; image_config[i].token != NULL; i++)
+  for (i = 0; image_config[i].token != NULL; i++)
   {
-    for (j=0; j < NUM_INITIAL_FONTS; j++)
+    for (j = 0; j < NUM_INITIAL_FONTS; j++)
     {
       char font_token[128];
       int len_font_token;
@@ -2887,24 +5586,24 @@ void InitGfx()
       sprintf(font_token, "%s_%d", CONFIG_TOKEN_FONT_INITIAL, j + 1);
       len_font_token = strlen(font_token);
 
-      if (strcmp(image_config[i].token, font_token) == 0)
+      if (strEqual(image_config[i].token, font_token))
        filename_font_initial = image_config[i].value;
       else if (strlen(image_config[i].token) > len_font_token &&
               strncmp(image_config[i].token, font_token, len_font_token) == 0)
       {
-       if (strcmp(&image_config[i].token[len_font_token], ".x") == 0)
+       if (strEqual(&image_config[i].token[len_font_token], ".x"))
          font_initial[j].src_x = atoi(image_config[i].value);
-       else if (strcmp(&image_config[i].token[len_font_token], ".y") == 0)
+       else if (strEqual(&image_config[i].token[len_font_token], ".y"))
          font_initial[j].src_y = atoi(image_config[i].value);
-       else if (strcmp(&image_config[i].token[len_font_token], ".width") == 0)
+       else if (strEqual(&image_config[i].token[len_font_token], ".width"))
          font_initial[j].width = atoi(image_config[i].value);
-       else if (strcmp(&image_config[i].token[len_font_token],".height") == 0)
+       else if (strEqual(&image_config[i].token[len_font_token], ".height"))
          font_initial[j].height = atoi(image_config[i].value);
       }
     }
   }
 
-  for (j=0; j < NUM_INITIAL_FONTS; j++)
+  for (j = 0; j < NUM_INITIAL_FONTS; j++)
   {
     font_initial[j].num_chars = DEFAULT_NUM_CHARS_PER_FONT;
     font_initial[j].num_chars_per_line = DEFAULT_NUM_CHARS_PER_LINE;
@@ -2913,9 +5612,16 @@ void InitGfx()
   if (filename_font_initial == NULL)   /* should not happen */
     Error(ERR_EXIT, "cannot get filename for '%s'", CONFIG_TOKEN_FONT_INITIAL);
 
-  /* create additional image buffers for double-buffering */
+#if 1
+  InitGfxBuffers();
+#else
+  /* create additional image buffers for double-buffering and cross-fading */
+  bitmap_db_store = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
+  bitmap_db_cross = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
   bitmap_db_field = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
+  bitmap_db_panel = CreateBitmap(DXSIZE, DYSIZE, DEFAULT_DEPTH);
   bitmap_db_door  = CreateBitmap(3 * DXSIZE, DYSIZE + VYSIZE, DEFAULT_DEPTH);
+  bitmap_db_toons = CreateBitmap(FULL_SXSIZE, FULL_SYSIZE, DEFAULT_DEPTH);
 
   /* initialize screen properties */
   InitGfxFieldInfo(SX, SY, SXSIZE, SYSIZE,
@@ -2923,38 +5629,155 @@ void InitGfx()
                   bitmap_db_field);
   InitGfxDoor1Info(DX, DY, DXSIZE, DYSIZE);
   InitGfxDoor2Info(VX, VY, VXSIZE, VYSIZE);
+  InitGfxWindowInfo(WIN_XSIZE, WIN_YSIZE);
   InitGfxScrollbufferInfo(FXSIZE, FYSIZE);
+#endif
+
+  InitGfxCustomArtworkInfo();
 
   bitmap_font_initial = LoadCustomImage(filename_font_initial);
 
-  for (j=0; j < NUM_INITIAL_FONTS; j++)
+  for (j = 0; j < NUM_INITIAL_FONTS; j++)
     font_initial[j].bitmap = bitmap_font_initial;
 
   InitFontGraphicInfo();
 
-  DrawInitText(WINDOW_TITLE_STRING, 20, FC_YELLOW);
-  DrawInitText(WINDOW_SUBTITLE_STRING, 50, FC_RED);
+  font_height = getFontHeight(FC_RED);
+
+#if 1
+  DrawInitText(getWindowTitleString(), 20, FC_YELLOW);
+#else
+  DrawInitText(getProgramInitString(), 20, FC_YELLOW);
+#endif
+  DrawInitText(PROGRAM_COPYRIGHT_STRING, 50, FC_RED);
+  DrawInitText(PROGRAM_WEBSITE_STRING, WIN_YSIZE - 20 - font_height, FC_RED);
+
+  DrawInitText("Loading graphics", 120, FC_GREEN);
+
+#if 1
+#if 1
+  /* initialize busy animation with default values */
+  int parameter[NUM_GFX_ARGS];
+  for (i = 0; i < NUM_GFX_ARGS; i++)
+    parameter[i] = get_graphic_parameter_value(image_config_suffix[i].value,
+                                               image_config_suffix[i].token,
+                                               image_config_suffix[i].type);
+#if 0
+  for (i = 0; i < NUM_GFX_ARGS; i++)
+    printf("::: '%s' => %d\n", image_config_suffix[i].token, parameter[i]);
+#endif
+#endif
 
-  DrawInitText("Loading graphics:", 120, FC_GREEN);
+  /* determine settings for busy animation (when displaying startup messages) */
+  for (i = 0; image_config[i].token != NULL; i++)
+  {
+    char *anim_token = CONFIG_TOKEN_GLOBAL_BUSY;
+    int len_anim_token = strlen(anim_token);
+
+    if (strEqual(image_config[i].token, anim_token))
+      filename_anim_initial = image_config[i].value;
+    else if (strlen(image_config[i].token) > len_anim_token &&
+            strncmp(image_config[i].token, anim_token, len_anim_token) == 0)
+    {
+#if 1
+      for (j = 0; image_config_suffix[j].token != NULL; j++)
+      {
+       if (strEqual(&image_config[i].token[len_anim_token],
+                    image_config_suffix[j].token))
+         parameter[j] =
+           get_graphic_parameter_value(image_config[i].value,
+                                       image_config_suffix[j].token,
+                                       image_config_suffix[j].type);
+      }
+#else
+      if (strEqual(&image_config[i].token[len_anim_token], ".x"))
+       anim_initial.src_x = atoi(image_config[i].value);
+      else if (strEqual(&image_config[i].token[len_anim_token], ".y"))
+       anim_initial.src_y = atoi(image_config[i].value);
+      else if (strEqual(&image_config[i].token[len_anim_token], ".width"))
+       anim_initial.width = atoi(image_config[i].value);
+      else if (strEqual(&image_config[i].token[len_anim_token], ".height"))
+       anim_initial.height = atoi(image_config[i].value);
+      else if (strEqual(&image_config[i].token[len_anim_token], ".frames"))
+       anim_initial.anim_frames = atoi(image_config[i].value);
+      else if (strEqual(&image_config[i].token[len_anim_token],
+                       ".frames_per_line"))
+       anim_initial.anim_frames_per_line = atoi(image_config[i].value);
+      else if (strEqual(&image_config[i].token[len_anim_token], ".delay"))
+       anim_initial.anim_delay = atoi(image_config[i].value);
+#endif
+    }
+  }
+
+#if defined(CREATE_SPECIAL_EDITION_RND_JUE)
+  filename_anim_initial = "loading.pcx";
+
+  parameter[GFX_ARG_X] = 0;
+  parameter[GFX_ARG_Y] = 0;
+  parameter[GFX_ARG_WIDTH] = 128;
+  parameter[GFX_ARG_HEIGHT] = 40;
+  parameter[GFX_ARG_FRAMES] = 32;
+  parameter[GFX_ARG_DELAY] = 4;
+  parameter[GFX_ARG_FRAMES_PER_LINE] = ARG_UNDEFINED_VALUE;
+#endif
+
+  if (filename_anim_initial == NULL)   /* should not happen */
+    Error(ERR_EXIT, "cannot get filename for '%s'", CONFIG_TOKEN_GLOBAL_BUSY);
+
+  anim_initial.bitmap = LoadCustomImage(filename_anim_initial);
+
+  graphic_info = &anim_initial;                /* graphic == 0 => anim_initial */
+
+  set_graphic_parameters_ext(0, parameter, anim_initial.bitmap);
+
+#if 0
+  printf("::: INIT_GFX: anim_frames_per_line == %d [%d / %d] [%d, %d]\n",
+        graphic_info[0].anim_frames_per_line,
+        get_scaled_graphic_width(0),
+        graphic_info[0].width,
+        getOriginalImageWidthFromImageID(0),
+        graphic_info[0].scale_up_factor);
+#endif
+
+  graphic_info = graphic_info_last;
+
+  init.busy.width  = anim_initial.width;
+  init.busy.height = anim_initial.height;
+
+  InitMenuDesignSettings_Static();
+  InitGfxDrawBusyAnimFunction(DrawInitAnim);
+
+  /* use copy of busy animation to prevent change while reloading artwork */
+  init_last = init;
+#endif
+}
+
+void RedrawBackground()
+{
+  BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, backbuffer,
+            0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
 
-  InitTileClipmasks();
+  redraw_mask = REDRAW_ALL;
 }
 
 void InitGfxBackground()
 {
   int x, y;
 
-  drawto = backbuffer;
   fieldbuffer = bitmap_db_field;
   SetDrawtoField(DRAW_BACKBUFFER);
 
-  BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, backbuffer,
-            0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
+#if 1
+  ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
+#else
+  RedrawBackground();
+
   ClearRectangle(backbuffer, REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
   ClearRectangle(bitmap_db_door, 0, 0, 3 * DXSIZE, DYSIZE + VYSIZE);
+#endif
 
-  for (x=0; x<MAX_BUF_XSIZE; x++)
-    for (y=0; y<MAX_BUF_YSIZE; y++)
+  for (x = 0; x < MAX_BUF_XSIZE; x++)
+    for (y = 0; y < MAX_BUF_YSIZE; y++)
       redraw[x][y] = 0;
   redraw_tiles = 0;
   redraw_mask = REDRAW_ALL;
@@ -2967,43 +5790,121 @@ static void InitLevelInfo()
   LoadLevelSetup_SeriesInfo();                 /* last played level info */
 }
 
-void InitLevelArtworkInfo()
+static void InitLevelArtworkInfo()
 {
   LoadLevelArtworkInfo();
 }
 
 static void InitImages()
 {
+  print_timestamp_init("InitImages");
+
+#if 0
+  printf("::: leveldir_current->identifier == '%s'\n",
+        leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
+  printf("::: leveldir_current->graphics_path == '%s'\n",
+        leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
+  printf("::: leveldir_current->graphics_set == '%s'\n",
+        leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
+  printf("::: getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'\n",
+        leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
+#endif
+
+  setLevelArtworkDir(artwork.gfx_first);
+
+#if 0
+  printf("::: leveldir_current->identifier == '%s'\n",
+        leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
+  printf("::: leveldir_current->graphics_path == '%s'\n",
+        leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
+  printf("::: leveldir_current->graphics_set == '%s'\n",
+        leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
+  printf("::: getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'\n",
+        leveldir_current == NULL ? "[NULL]" : LEVELDIR_ARTWORK_SET(leveldir_current, ARTWORK_TYPE_GRAPHICS));
+#endif
+
+#if 0
+  printf("::: InitImages for '%s' ['%s', '%s'] ['%s', '%s']\n",
+        leveldir_current->identifier,
+        artwork.gfx_current_identifier,
+        artwork.gfx_current->identifier,
+        leveldir_current->graphics_set,
+        leveldir_current->graphics_path);
+#endif
+
+  UPDATE_BUSY_STATE();
+
   ReloadCustomImages();
+  print_timestamp_time("ReloadCustomImages");
+
+  UPDATE_BUSY_STATE();
 
   LoadCustomElementDescriptions();
-  LoadSpecialMenuDesignSettings();
+  print_timestamp_time("LoadCustomElementDescriptions");
+
+  UPDATE_BUSY_STATE();
+
+  LoadMenuDesignSettings();
+  print_timestamp_time("LoadMenuDesignSettings");
+
+  UPDATE_BUSY_STATE();
 
   ReinitializeGraphics();
+  print_timestamp_time("ReinitializeGraphics");
+
+  UPDATE_BUSY_STATE();
+
+  print_timestamp_done("InitImages");
 }
 
-static void InitSound()
+static void InitSound(char *identifier)
 {
-  InitReloadCustomSounds(artwork.snd_current->identifier);
+  print_timestamp_init("InitSound");
+
+  if (identifier == NULL)
+    identifier = artwork.snd_current->identifier;
+
+  /* set artwork path to send it to the sound server process */
+  setLevelArtworkDir(artwork.snd_first);
+
+  InitReloadCustomSounds(identifier);
+  print_timestamp_time("InitReloadCustomSounds");
+
   ReinitializeSounds();
+  print_timestamp_time("ReinitializeSounds");
+
+  print_timestamp_done("InitSound");
 }
 
-static void InitMusic()
+static void InitMusic(char *identifier)
 {
-  InitReloadCustomMusic(artwork.mus_current->identifier);
+  print_timestamp_init("InitMusic");
+
+  if (identifier == NULL)
+    identifier = artwork.mus_current->identifier;
+
+  /* set artwork path to send it to the sound server process */
+  setLevelArtworkDir(artwork.mus_first);
+
+  InitReloadCustomMusic(identifier);
+  print_timestamp_time("InitReloadCustomMusic");
+
   ReinitializeMusic();
+  print_timestamp_time("ReinitializeMusic");
+
+  print_timestamp_done("InitMusic");
 }
 
 void InitNetworkServer()
 {
-#if defined(PLATFORM_UNIX)
+#if defined(NETWORK_AVALIABLE)
   int nr_wanted;
 #endif
 
   if (!options.network)
     return;
 
-#if defined(PLATFORM_UNIX)
+#if defined(NETWORK_AVALIABLE)
   nr_wanted = Request("Choose player", REQ_PLAYER | REQ_STAY_CLOSED);
 
   if (!ConnectToServer(options.server_host, options.server_port))
@@ -3017,141 +5918,356 @@ void InitNetworkServer()
 #endif
 }
 
-void ReloadCustomArtwork()
+static boolean CheckArtworkConfigForCustomElements(char *filename)
 {
-  static char *leveldir_current_identifier = NULL;
-  static boolean last_override_level_graphics = FALSE;
-  static boolean last_override_level_sounds = FALSE;
-  static boolean last_override_level_music = FALSE;
-  /* identifier for new artwork; default: artwork configured in setup */
-  char *gfx_new_identifier = artwork.gfx_current->identifier;
-  char *snd_new_identifier = artwork.snd_current->identifier;
-  char *mus_new_identifier = artwork.mus_current->identifier;
-  boolean redraw_screen = FALSE;
+  SetupFileHash *setup_file_hash;
+  boolean redefined_ce_found = FALSE;
+
+  /* !!! CACHE THIS BY USING HASH 'filename' => 'true/false' !!! */
+
+  if ((setup_file_hash = loadSetupFileHash(filename)) != NULL)
+  {
+    BEGIN_HASH_ITERATION(setup_file_hash, itr)
+    {
+      char *token = HASH_ITERATION_TOKEN(itr);
+
+      if (strPrefix(token, "custom_"))
+      {
+       redefined_ce_found = TRUE;
+
+       break;
+      }
+    }
+    END_HASH_ITERATION(setup_file_hash, itr)
+
+    freeSetupFileHash(setup_file_hash);
+  }
+
+  return redefined_ce_found;
+}
+
+static boolean CheckArtworkTypeForRedefinedCustomElements(int type)
+{
+  char *filename_base, *filename_local;
+  boolean redefined_ce_found = FALSE;
+
+  setLevelArtworkDir(ARTWORK_FIRST_NODE(artwork, type));
+
+#if 0
+  printf("::: leveldir_current->identifier == '%s'\n",
+        leveldir_current == NULL ? "[NULL]" : leveldir_current->identifier);
+  printf("::: leveldir_current->graphics_path == '%s'\n",
+        leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_path);
+  printf("::: leveldir_current->graphics_set == '%s'\n",
+        leveldir_current == NULL ? "[NULL]" : leveldir_current->graphics_set);
+  printf("::: getLevelArtworkSet(ARTWORK_TYPE_GRAPHICS) == '%s'\n",
+        leveldir_current == NULL ? "[NULL]" :
+        LEVELDIR_ARTWORK_SET(leveldir_current, type));
+#endif
+
+  /* first look for special artwork configured in level series config */
+  filename_base = getCustomArtworkLevelConfigFilename(type);
 
-  if (leveldir_current_identifier == NULL)
-    leveldir_current_identifier = leveldir_current->identifier;
+#if 0
+  printf("::: filename_base == '%s'\n", filename_base);
+#endif
+
+  if (fileExists(filename_base))
+    redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_base);
+
+  filename_local = getCustomArtworkConfigFilename(type);
 
 #if 0
-  printf("CURRENT GFX: '%s' ['%s']\n", artwork.gfx_current->identifier,
-        leveldir_current->graphics_set);
-  printf("CURRENT LEV: '%s' / '%s'\n", leveldir_current_identifier,
-        leveldir_current->identifier);
+  printf("::: filename_local == '%s'\n", filename_local);
 #endif
 
+  if (filename_local != NULL && !strEqual(filename_base, filename_local))
+    redefined_ce_found |= CheckArtworkConfigForCustomElements(filename_local);
+
 #if 0
-  printf("graphics --> '%s' ('%s')\n",
-        artwork.gfx_current_identifier, artwork.gfx_current->filename);
-  printf("sounds   --> '%s' ('%s')\n",
-        artwork.snd_current_identifier, artwork.snd_current->filename);
-  printf("music    --> '%s' ('%s')\n",
-        artwork.mus_current_identifier, artwork.mus_current->filename);
+  printf("::: redefined_ce_found == %d\n", redefined_ce_found);
+#endif
+
+  return redefined_ce_found;
+}
+
+static void InitOverrideArtwork()
+{
+  boolean redefined_ce_found = FALSE;
+
+  /* to check if this level set redefines any CEs, do not use overriding */
+  gfx.override_level_graphics = FALSE;
+  gfx.override_level_sounds   = FALSE;
+  gfx.override_level_music    = FALSE;
+
+  /* now check if this level set has definitions for custom elements */
+  if (setup.override_level_graphics == AUTO ||
+      setup.override_level_sounds   == AUTO ||
+      setup.override_level_music    == AUTO)
+    redefined_ce_found =
+      (CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_GRAPHICS) |
+       CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_SOUNDS) |
+       CheckArtworkTypeForRedefinedCustomElements(ARTWORK_TYPE_MUSIC));
+
+#if 0
+  printf("::: redefined_ce_found == %d\n", redefined_ce_found);
+#endif
+
+  if (redefined_ce_found)
+  {
+    /* this level set has CE definitions: change "AUTO" to "FALSE" */
+    gfx.override_level_graphics = (setup.override_level_graphics == TRUE);
+    gfx.override_level_sounds   = (setup.override_level_sounds   == TRUE);
+    gfx.override_level_music    = (setup.override_level_music    == TRUE);
+  }
+  else
+  {
+    /* this level set has no CE definitions: change "AUTO" to "TRUE" */
+    gfx.override_level_graphics = (setup.override_level_graphics != FALSE);
+    gfx.override_level_sounds   = (setup.override_level_sounds   != FALSE);
+    gfx.override_level_music    = (setup.override_level_music    != FALSE);
+  }
+
+#if 0
+  printf("::: => %d, %d, %d\n",
+        gfx.override_level_graphics,
+        gfx.override_level_sounds,
+        gfx.override_level_music);
+#endif
+}
+
+static char *getNewArtworkIdentifier(int type)
+{
+  static char *leveldir_current_identifier[3] = { NULL, NULL, NULL };
+  static boolean last_override_level_artwork[3] = { FALSE, FALSE, FALSE };
+  static boolean last_has_level_artwork_set[3] = { FALSE, FALSE, FALSE };
+  static boolean initialized[3] = { FALSE, FALSE, FALSE };
+  TreeInfo *artwork_first_node = ARTWORK_FIRST_NODE(artwork, type);
+#if 1
+  boolean setup_override_artwork = GFX_OVERRIDE_ARTWORK(type);
+#else
+  boolean setup_override_artwork = SETUP_OVERRIDE_ARTWORK(setup, type);
+#endif
+  char *setup_artwork_set = SETUP_ARTWORK_SET(setup, type);
+  char *leveldir_identifier = leveldir_current->identifier;
+#if 1
+  /* !!! setLevelArtworkDir() should be moved to an earlier stage !!! */
+  char *leveldir_artwork_set = setLevelArtworkDir(artwork_first_node);
+#else
+  char *leveldir_artwork_set = LEVELDIR_ARTWORK_SET(leveldir_current, type);
 #endif
+  boolean has_level_artwork_set = (leveldir_artwork_set != NULL);
+  char *artwork_current_identifier;
+  char *artwork_new_identifier = NULL; /* default: nothing has changed */
 
   /* leveldir_current may be invalid (level group, parent link) */
   if (!validLevelSeries(leveldir_current))
-    return;
+    return NULL;
+
+  /* 1st step: determine artwork set to be activated in descending order:
+     --------------------------------------------------------------------
+     1. setup artwork (when configured to override everything else)
+     2. artwork set configured in "levelinfo.conf" of current level set
+        (artwork in level directory will have priority when loading later)
+     3. artwork in level directory (stored in artwork sub-directory)
+     4. setup artwork (currently configured in setup menu) */
+
+  if (setup_override_artwork)
+    artwork_current_identifier = setup_artwork_set;
+  else if (leveldir_artwork_set != NULL)
+    artwork_current_identifier = leveldir_artwork_set;
+  else if (getTreeInfoFromIdentifier(artwork_first_node, leveldir_identifier))
+    artwork_current_identifier = leveldir_identifier;
+  else
+    artwork_current_identifier = setup_artwork_set;
 
-  /* when a new level series was selected, check if there was a change
-     in custom artwork stored in level series directory */
-  if (leveldir_current_identifier != leveldir_current->identifier)
-  {
-    char *identifier_old = leveldir_current_identifier;
-    char *identifier_new = leveldir_current->identifier;
 
-    if (getTreeInfoFromIdentifier(artwork.gfx_first, identifier_old) !=
-       getTreeInfoFromIdentifier(artwork.gfx_first, identifier_new))
-      gfx_new_identifier = identifier_new;
-    if (getTreeInfoFromIdentifier(artwork.snd_first, identifier_old) !=
-       getTreeInfoFromIdentifier(artwork.snd_first, identifier_new))
-      snd_new_identifier = identifier_new;
-    if (getTreeInfoFromIdentifier(artwork.mus_first, identifier_new) !=
-       getTreeInfoFromIdentifier(artwork.mus_first, identifier_new))
-      mus_new_identifier = identifier_new;
+  /* 2nd step: check if it is really needed to reload artwork set
+     ------------------------------------------------------------ */
 
-    leveldir_current_identifier = leveldir_current->identifier;
-  }
+#if 0
+  if (type == ARTWORK_TYPE_GRAPHICS)
+    printf("::: 0: '%s' ['%s', '%s'] ['%s' ('%s')]\n",
+          artwork_new_identifier,
+          ARTWORK_CURRENT_IDENTIFIER(artwork, type),
+          artwork_current_identifier,
+          leveldir_current->graphics_set,
+          leveldir_current->identifier);
+#endif
 
-  /* custom level artwork configured in level series configuration file
-     always overrides custom level artwork stored in level series directory
-     and (level independant) custom artwork configured in setup menue */
-  if (leveldir_current->graphics_set != NULL)
-    gfx_new_identifier = leveldir_current->graphics_set;
-  if (leveldir_current->sounds_set != NULL)
-    snd_new_identifier = leveldir_current->sounds_set;
-  if (leveldir_current->music_set != NULL)
-    mus_new_identifier = leveldir_current->music_set;
+  /* ---------- reload if level set and also artwork set has changed ------- */
+  if (leveldir_current_identifier[type] != leveldir_identifier &&
+      (last_has_level_artwork_set[type] || has_level_artwork_set))
+    artwork_new_identifier = artwork_current_identifier;
+
+  leveldir_current_identifier[type] = leveldir_identifier;
+  last_has_level_artwork_set[type] = has_level_artwork_set;
 
-  if (strcmp(artwork.gfx_current_identifier, gfx_new_identifier) != 0 ||
-      last_override_level_graphics != setup.override_level_graphics)
-  {
 #if 0
-    printf("RELOADING GRAPHICS '%s' -> '%s' ('%s')\n",
-          artwork.gfx_current_identifier,
-          artwork.gfx_current->identifier,
-          gfx_new_identifier);
+  if (type == ARTWORK_TYPE_GRAPHICS)
+    printf("::: 1: '%s'\n", artwork_new_identifier);
 #endif
 
-    setLevelArtworkDir(artwork.gfx_first);
+  /* ---------- reload if "override artwork" setting has changed ----------- */
+  if (last_override_level_artwork[type] != setup_override_artwork)
+    artwork_new_identifier = artwork_current_identifier;
 
-    ClearRectangle(window, 0, 0, WIN_XSIZE, WIN_YSIZE);
+  last_override_level_artwork[type] = setup_override_artwork;
 
-    InitImages();
+#if 0
+  if (type == ARTWORK_TYPE_GRAPHICS)
+    printf("::: 2: '%s'\n", artwork_new_identifier);
+#endif
 
-    FreeTileClipmasks();
-    InitTileClipmasks();
+  /* ---------- reload if current artwork identifier has changed ----------- */
+  if (!strEqual(ARTWORK_CURRENT_IDENTIFIER(artwork, type),
+               artwork_current_identifier))
+    artwork_new_identifier = artwork_current_identifier;
 
-    artwork.gfx_current_identifier = artwork.gfx_current->identifier;
-    last_override_level_graphics = setup.override_level_graphics;
+  *(ARTWORK_CURRENT_IDENTIFIER_PTR(artwork, type))= artwork_current_identifier;
 
-    redraw_screen = TRUE;
-  }
+#if 0
+  if (type == ARTWORK_TYPE_GRAPHICS)
+    printf("::: 3: '%s'\n", artwork_new_identifier);
+#endif
+
+  /* ---------- do not reload directly after starting ---------------------- */
+  if (!initialized[type])
+    artwork_new_identifier = NULL;
+
+  initialized[type] = TRUE;
+
+#if 0
+  if (type == ARTWORK_TYPE_GRAPHICS)
+    printf("::: 4: '%s'\n", artwork_new_identifier);
+#endif
 
-  if (strcmp(artwork.snd_current_identifier, snd_new_identifier) != 0 ||
-      last_override_level_sounds != setup.override_level_sounds)
-  {
 #if 0
-    printf("RELOADING SOUNDS '%s' -> '%s' ('%s')\n",
-          artwork.snd_current_identifier,
-          artwork.snd_current->identifier,
-          snd_new_identifier);
+  if (type == ARTWORK_TYPE_GRAPHICS)
+    printf("CHECKING OLD/NEW GFX:\n- OLD: %s\n- NEW: %s ['%s', '%s'] ['%s']\n",
+          artwork.gfx_current_identifier, artwork_current_identifier,
+          artwork.gfx_current->identifier, leveldir_current->graphics_set,
+          artwork_new_identifier);
 #endif
 
-    /* set artwork path to send it to the sound server process */
-    setLevelArtworkDir(artwork.snd_first);
+  return artwork_new_identifier;
+}
 
-    InitReloadCustomSounds(snd_new_identifier);
-    ReinitializeSounds();
+void ReloadCustomArtwork(int force_reload)
+{
+  int last_game_status = game_status;  /* save current game status */
+  char *gfx_new_identifier;
+  char *snd_new_identifier;
+  char *mus_new_identifier;
+  boolean force_reload_gfx = (force_reload & (1 << ARTWORK_TYPE_GRAPHICS));
+  boolean force_reload_snd = (force_reload & (1 << ARTWORK_TYPE_SOUNDS));
+  boolean force_reload_mus = (force_reload & (1 << ARTWORK_TYPE_MUSIC));
+  boolean reload_needed;
 
-    artwork.snd_current_identifier = artwork.snd_current->identifier;
-    last_override_level_sounds = setup.override_level_sounds;
+  InitOverrideArtwork();
 
-    redraw_screen = TRUE;
-  }
+  force_reload_gfx |= AdjustGraphicsForEMC();
 
-  if (strcmp(artwork.mus_current_identifier, mus_new_identifier) != 0 ||
-      last_override_level_music != setup.override_level_music)
-  {
-    /* set artwork path to send it to the sound server process */
-    setLevelArtworkDir(artwork.mus_first);
+  gfx_new_identifier = getNewArtworkIdentifier(ARTWORK_TYPE_GRAPHICS);
+  snd_new_identifier = getNewArtworkIdentifier(ARTWORK_TYPE_SOUNDS);
+  mus_new_identifier = getNewArtworkIdentifier(ARTWORK_TYPE_MUSIC);
+
+  reload_needed = (gfx_new_identifier != NULL || force_reload_gfx ||
+                  snd_new_identifier != NULL || force_reload_snd ||
+                  mus_new_identifier != NULL || force_reload_mus);
 
-    InitReloadCustomMusic(mus_new_identifier);
-    ReinitializeMusic();
+  if (!reload_needed)
+    return;
 
-    artwork.mus_current_identifier = artwork.mus_current->identifier;
-    last_override_level_music = setup.override_level_music;
+  print_timestamp_init("ReloadCustomArtwork");
 
-    redraw_screen = TRUE;
+  game_status = GAME_MODE_LOADING;
+
+  FadeOut(REDRAW_ALL);
+
+#if 1
+  ClearRectangle(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
+#else
+  ClearRectangle(window, 0, 0, WIN_XSIZE, WIN_YSIZE);
+#endif
+  print_timestamp_time("ClearRectangle");
+
+#if 0
+  printf("::: fading in ... %d\n", fading.fade_mode);
+#endif
+  FadeIn(REDRAW_ALL);
+#if 0
+  printf("::: done\n");
+#endif
+
+  if (gfx_new_identifier != NULL || force_reload_gfx)
+  {
+#if 0
+    printf("RELOADING GRAPHICS '%s' -> '%s' ['%s', '%s']\n",
+          artwork.gfx_current_identifier,
+          gfx_new_identifier,
+          artwork.gfx_current->identifier,
+          leveldir_current->graphics_set);
+#endif
+
+    InitImages();
+    print_timestamp_time("InitImages");
   }
 
-  if (redraw_screen)
+  if (snd_new_identifier != NULL || force_reload_snd)
   {
-    InitGfxBackground();
+    InitSound(snd_new_identifier);
+    print_timestamp_time("InitSound");
+  }
 
-    /* force redraw of (open or closed) door graphics */
-    SetDoorState(DOOR_OPEN_ALL);
-    CloseDoor(DOOR_CLOSE_ALL | DOOR_NO_DELAY);
+  if (mus_new_identifier != NULL || force_reload_mus)
+  {
+    InitMusic(mus_new_identifier);
+    print_timestamp_time("InitMusic");
   }
+
+  game_status = last_game_status;      /* restore current game status */
+
+  init_last = init;                    /* switch to new busy animation */
+
+#if 0
+  printf("::: ----------------DELAY 1 ...\n");
+  Delay(3000);
+#endif
+
+#if 0
+  printf("::: FadeOut @ ReloadCustomArtwork ...\n");
+#endif
+  FadeOut(REDRAW_ALL);
+#if 0
+  printf("::: FadeOut @ ReloadCustomArtwork done\n");
+#endif
+
+  RedrawBackground();
+
+  /* force redraw of (open or closed) door graphics */
+  SetDoorState(DOOR_OPEN_ALL);
+  CloseDoor(DOOR_CLOSE_ALL | DOOR_NO_DELAY);
+
+#if 1
+#if 1
+#if 1
+  FadeSetEnterScreen();
+  FadeSkipNextFadeOut();
+  // FadeSetDisabled();
+#else
+  FadeSkipNext();
+#endif
+#else
+  fading = fading_none;
+#endif
+#endif
+
+#if 0
+  redraw_mask = REDRAW_ALL;
+#endif
+
+  print_timestamp_done("ReloadCustomArtwork");
 }
 
 void KeyboardAutoRepeatOffUnlessAutoplay()
@@ -3160,6 +6276,63 @@ void KeyboardAutoRepeatOffUnlessAutoplay()
     KeyboardAutoRepeatOff();
 }
 
+void DisplayExitMessage(char *format, va_list ap)
+{
+  // check if draw buffer and fonts for exit message are already available
+  if (drawto == NULL || font_initial[NUM_INITIAL_FONTS - 1].bitmap == NULL)
+    return;
+
+  int font_1 = FC_RED;
+  int font_2 = FC_YELLOW;
+  int font_3 = FC_BLUE;
+  int font_width = getFontWidth(font_2);
+  int font_height = getFontHeight(font_2);
+  int sx = SX;
+  int sy = SY;
+  int sxsize = WIN_XSIZE - 2 * sx;
+  int sysize = WIN_YSIZE - 2 * sy;
+  int line_length = sxsize / font_width;
+  int max_lines = sysize / font_height;
+  int num_lines_printed;
+
+  gfx.sx = sx;
+  gfx.sy = sy;
+  gfx.sxsize = sxsize;
+  gfx.sysize = sysize;
+
+  sy = 20;
+
+  ClearRectangle(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
+
+  DrawTextSCentered(sy, font_1, "Fatal error:");
+  sy += 3 * font_height;;
+
+  num_lines_printed =
+    DrawTextBufferVA(sx, sy, format, ap, font_2,
+                    line_length, line_length, max_lines,
+                    0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
+  sy += (num_lines_printed + 3) * font_height;
+
+  DrawTextSCentered(sy, font_1, "For details, see the following error file:");
+  sy += 3 * font_height;
+
+  num_lines_printed =
+    DrawTextBuffer(sx, sy, program.error_filename, font_2,
+                  line_length, line_length, max_lines,
+                  0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
+
+  DrawTextSCentered(SYSIZE - 20, font_3, "Press any key or button to exit");
+
+  redraw_mask = REDRAW_ALL;
+
+  BackToFront();
+
+  /* deactivate toons on error message screen */
+  setup.toons = FALSE;
+
+  WaitForEventToContinue();
+}
+
 
 /* ========================================================================= */
 /* OpenAll()                                                                 */
@@ -3167,7 +6340,17 @@ void KeyboardAutoRepeatOffUnlessAutoplay()
 
 void OpenAll()
 {
-  InitGlobal();                /* initialize some global variables */
+  print_timestamp_init("OpenAll");
+
+  game_status = GAME_MODE_LOADING;
+
+#if 1
+  InitCounter();
+#endif
+
+  InitGlobal();                        /* initialize some global variables */
+
+  print_timestamp_time("[init global stuff]");
 
   if (options.execute_command)
     Execute_Command(options.execute_command);
@@ -3179,50 +6362,112 @@ void OpenAll()
 #else
     Error(ERR_WARN, "networking only supported in Unix version");
 #endif
-    exit(0);   /* never reached */
+
+    exit(0);                   /* never reached, server loops forever */
   }
 
   InitSetup();
 
+  print_timestamp_time("[init setup/config stuff (1)]");
+
+  InitGameInfo();
+  print_timestamp_time("[init setup/config stuff (2)]");
   InitPlayerInfo();
+  print_timestamp_time("[init setup/config stuff (3)]");
   InitArtworkInfo();           /* needed before loading gfx, sound & music */
+  print_timestamp_time("[init setup/config stuff (4)]");
   InitArtworkConfig();         /* needed before forking sound child process */
+  print_timestamp_time("[init setup/config stuff (5)]");
   InitMixer();
+  print_timestamp_time("[init setup/config stuff (6)]");
 
+#if 0
   InitCounter();
+#endif
 
   InitRND(NEW_RANDOMIZE);
-  InitSimpleRND(NEW_RANDOMIZE);
+  InitSimpleRandom(NEW_RANDOMIZE);
 
   InitJoysticks();
 
+  print_timestamp_time("[init setup/config stuff]");
+
   InitVideoDisplay();
-  InitVideoBuffer(&backbuffer, &window, WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH,
-                 setup.fullscreen);
+  InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
+
+  InitEventFilter(FilterEvents);
 
-  InitEventFilter(FilterMouseMotionEvents);
+  print_timestamp_time("[init video stuff]");
 
   InitElementPropertiesStatic();
+  InitElementPropertiesEngine(GAME_VERSION_ACTUAL);
+  InitElementPropertiesGfxElement();
+
+  print_timestamp_time("[init element properties stuff]");
 
   InitGfx();
 
+  print_timestamp_time("InitGfx");
+
   InitLevelInfo();
+  print_timestamp_time("InitLevelInfo");
+
   InitLevelArtworkInfo();
+  print_timestamp_time("InitLevelArtworkInfo");
+
+  InitOverrideArtwork();       /* needs to know current level directory */
+  print_timestamp_time("InitOverrideArtwork");
 
   InitImages();                        /* needs to know current level directory */
-  InitSound();                 /* needs to know current level directory */
-  InitMusic();                 /* needs to know current level directory */
+  print_timestamp_time("InitImages");
+
+  InitSound(NULL);             /* needs to know current level directory */
+  print_timestamp_time("InitSound");
+
+  InitMusic(NULL);             /* needs to know current level directory */
+  print_timestamp_time("InitMusic");
 
   InitGfxBackground();
 
+#if 1
+  em_open_all();
+#endif
+
+#if 1
+  sp_open_all();
+#endif
+
   if (global.autoplay_leveldir)
   {
     AutoPlayTape();
     return;
   }
+  else if (global.convert_leveldir)
+  {
+    ConvertLevels();
+    return;
+  }
+  else if (global.create_images_dir)
+  {
+    CreateLevelSketchImages();
+    return;
+  }
 
   game_status = GAME_MODE_MAIN;
 
+#if 1
+  FadeSetEnterScreen();
+  if (!(fading.fade_mode & FADE_TYPE_TRANSFORM))
+    FadeSkipNextFadeOut();
+  // FadeSetDisabled();
+#else
+  fading = fading_none;
+#endif
+
+  print_timestamp_time("[post-artwork]");
+
+  print_timestamp_done("OpenAll");
+
   DrawMainMenu();
 
   InitNetworkServer();
@@ -3235,11 +6480,39 @@ void CloseAllAndExit(int exit_value)
   FreeAllMusic();
   CloseAudio();                /* called after freeing sounds (needed for SDL) */
 
+#if 1
+  em_close_all();
+#endif
+
+#if 1
+  sp_close_all();
+#endif
+
   FreeAllImages();
-  FreeTileClipmasks();
+
+#if defined(TARGET_SDL)
+#if defined(TARGET_SDL2)
+  // !!! TODO !!!
+  // set a flag to tell the network server thread to quit and wait for it
+  // using SDL_WaitThread()
+#else
+  if (network_server)  /* terminate network server */
+    SDL_KillThread(server_thread);
+#endif
+#endif
 
   CloseVideoDisplay();
-  ClosePlatformDependantStuff();
+  ClosePlatformDependentStuff();
+
+  if (exit_value != 0)
+  {
+    /* fall back to default level set (current set may have caused an error) */
+    SaveLevelSetup_LastSeries_Deactivate();
+
+    /* tell user where to find error log file which may contain more details */
+    // (error notification now directly displayed on screen inside R'n'D
+    // NotifyUserAboutErrorFile();     /* currently only works for Windows */
+  }
 
   exit(exit_value);
 }