added optional button to restart game (door, panel and touch variants)
[rocksndiamonds.git] / src / init.c
index 44b0a5cbb10da6c37d001c37d0818bad679d1abe..62a5d0ff860865ef4d203e8b71f2b30da8b23ff7 100644 (file)
 
 
 #define CONFIG_TOKEN_FONT_INITIAL              "font.initial"
+#define CONFIG_TOKEN_GLOBAL_BUSY_INITIAL       "global.busy_initial"
 #define CONFIG_TOKEN_GLOBAL_BUSY               "global.busy"
+#define CONFIG_TOKEN_GLOBAL_BUSY_PLAYFIELD     "global.busy_playfield"
+#define CONFIG_TOKEN_BACKGROUND                        "background"
+#define CONFIG_TOKEN_BACKGROUND_LOADING_INITIAL        "background.LOADING_INITIAL"
+#define CONFIG_TOKEN_BACKGROUND_LOADING                "background.LOADING"
+
+#define INITIAL_IMG_GLOBAL_BUSY_INITIAL                0
+#define INITIAL_IMG_GLOBAL_BUSY                        1
+#define INITIAL_IMG_GLOBAL_BUSY_PLAYFIELD      2
+
+#define NUM_INITIAL_IMAGES_BUSY                        3
+
+#define INITIAL_IMG_BACKGROUND                 3
+#define INITIAL_IMG_BACKGROUND_LOADING_INITIAL 4
+#define INITIAL_IMG_BACKGROUND_LOADING         5
+
+#define NUM_INITIAL_IMAGES                     6
 
 
 static struct FontBitmapInfo font_initial[NUM_INITIAL_FONTS];
-static struct GraphicInfo    anim_initial;
+static struct GraphicInfo   image_initial[NUM_INITIAL_IMAGES];
 
 static int copy_properties[][5] =
 {
@@ -93,38 +110,75 @@ static int copy_properties[][5] =
 static int get_graphic_parameter_value(char *, char *, int);
 
 
-static void DrawInitAnim(void)
+static int getLoadingBackgroundImage(int graphic)
+{
+  return getImageFromGraphicOrDefault(graphic, INITIAL_IMG_BACKGROUND);
+}
+
+static void SetLoadingWindowBackgroundImage(int graphic)
+{
+  SetBackgroundImage(getLoadingBackgroundImage(graphic), REDRAW_ALL);
+}
+
+static void SetLoadingBackgroundImage(void)
 {
   struct GraphicInfo *graphic_info_last = graphic_info;
-  int graphic = 0;
-  static unsigned int action_delay = 0;
-  unsigned int action_delay_value = GameFrameDelay;
+  int background_image = (game_status_last_screen == -1 ?
+                         INITIAL_IMG_BACKGROUND_LOADING_INITIAL :
+                         INITIAL_IMG_BACKGROUND_LOADING);
+
+  graphic_info = image_initial;
+
+  SetDrawDeactivationMask(REDRAW_NONE);
+  SetDrawBackgroundMask(REDRAW_ALL);
+
+  SetLoadingWindowBackgroundImage(background_image);
+
+  graphic_info = graphic_info_last;
+}
+
+static void DrawInitAnim(boolean only_when_loading)
+{
+  struct GraphicInfo *graphic_info_last = graphic_info;
+  int graphic = (game_status_last_screen == -1 ?
+                INITIAL_IMG_GLOBAL_BUSY_INITIAL :
+                game_status == GAME_MODE_LOADING ?
+                INITIAL_IMG_GLOBAL_BUSY :
+                INITIAL_IMG_GLOBAL_BUSY_PLAYFIELD);
+  struct MenuPosInfo *busy = (game_status_last_screen == -1 ?
+                             &init_last.busy_initial :
+                             game_status == GAME_MODE_LOADING ?
+                             &init_last.busy :
+                             &init_last.busy_playfield);
+  static DelayCounter action_delay = { 0 };
   int sync_frame = FrameCounter;
   int x, y;
 
+  action_delay.value = GameFrameDelay;
+
   // prevent OS (Windows) from complaining about program not responding
   CheckQuitEvent();
 
-  if (game_status != GAME_MODE_LOADING)
+  if (game_status != GAME_MODE_LOADING && only_when_loading)
     return;
 
-  if (anim_initial.bitmap == NULL || window == NULL)
+  if (image_initial[graphic].bitmap == NULL || window == NULL)
     return;
 
-  if (!DelayReached(&action_delay, action_delay_value))
+  if (!DelayReached(&action_delay))
     return;
 
-  if (init_last.busy.x == -1)
-    init_last.busy.x = WIN_XSIZE / 2;
-  if (init_last.busy.y == -1)
-    init_last.busy.y = WIN_YSIZE / 2;
+  if (busy->x == -1)
+    busy->x = (game_status == GAME_MODE_LOADING ? WIN_XSIZE / 2 : SXSIZE / 2);
+  if (busy->y == -1)
+    busy->y = (game_status == GAME_MODE_LOADING ? WIN_YSIZE / 2 : SYSIZE / 2);
 
-  x = ALIGNED_TEXT_XPOS(&init_last.busy);
-  y = ALIGNED_TEXT_YPOS(&init_last.busy);
+  x = (game_status == GAME_MODE_LOADING ? 0 : SX) + ALIGNED_TEXT_XPOS(busy);
+  y = (game_status == GAME_MODE_LOADING ? 0 : SY) + ALIGNED_TEXT_YPOS(busy);
 
-  graphic_info = &anim_initial;                // graphic == 0 => anim_initial
+  graphic_info = image_initial;
 
-  if (sync_frame % anim_initial.anim_delay == 0)
+  if (sync_frame % image_initial[graphic].anim_delay == 0)
   {
     Bitmap *src_bitmap;
     int src_x, src_y;
@@ -132,8 +186,12 @@ static void DrawInitAnim(void)
     int height = graphic_info[graphic].height;
     int frame = getGraphicAnimationFrame(graphic, sync_frame);
 
+    ClearRectangleOnBackground(drawto, x, y, width, height);
+
     getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
-    BlitBitmap(src_bitmap, window, src_x, src_y, width, height, x, y);
+    BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height, x, y);
+
+    BlitBitmap(drawto, window, x, y, width, height, x, y);
   }
 
   graphic_info = graphic_info_last;
@@ -323,7 +381,7 @@ void InitImageTextures(void)
     CreateImageTextures(texture_graphics[i]);
 }
 
-static int getFontBitmapID(int font_nr)
+static int getFontSpecialSuffix(void)
 {
   int special = -1;
 
@@ -333,6 +391,15 @@ static int getFontBitmapID(int font_nr)
     special = game_status;
   else if (game_status == GAME_MODE_PSEUDO_TYPENAME)
     special = GFX_SPECIAL_ARG_MAIN;
+  else if (game_status == GAME_MODE_PSEUDO_TYPENAMES)
+    special = GFX_SPECIAL_ARG_NAMES;
+
+  return special;
+}
+
+static int getFontBitmapID(int font_nr)
+{
+  int special = getFontSpecialSuffix();
 
   if (special != -1)
     return font_info[font_nr].special_bitmap_id[special];
@@ -351,6 +418,22 @@ static int getFontFromToken(char *token)
   return FONT_INITIAL_1;
 }
 
+static char *getTokenFromFont(int font_nr)
+{
+  static char *token = NULL;
+  int special = getFontSpecialSuffix();
+
+  checked_free(token);
+
+  if (special != -1)
+    token = getStringCat2(font_info[font_nr].token_name,
+                          special_suffix_info[special].suffix);
+  else
+    token = getStringCopy(font_info[font_nr].token_name);
+
+  return token;
+}
+
 static void InitFontGraphicInfo(void)
 {
   static struct FontBitmapInfo *font_bitmap_info = NULL;
@@ -362,7 +445,7 @@ static void InitFontGraphicInfo(void)
   if (graphic_info == NULL)            // still at startup phase
   {
     InitFontInfo(font_initial, NUM_INITIAL_FONTS,
-                getFontBitmapID, getFontFromToken);
+                getFontBitmapID, getFontFromToken, getTokenFromFont);
 
     return;
   }
@@ -587,7 +670,7 @@ static void InitFontGraphicInfo(void)
   }
 
   InitFontInfo(font_bitmap_info, num_font_bitmaps,
-              getFontBitmapID, getFontFromToken);
+              getFontBitmapID, getFontFromToken, getTokenFromFont);
 }
 
 static void InitGlobalAnimGraphicInfo(void)
@@ -1254,19 +1337,27 @@ static int get_graphic_parameter_value(char *value_raw, char *suffix, int type)
   return -1;
 }
 
-static int get_scaled_graphic_width(int graphic)
+static int get_scaled_graphic_width(Bitmap *src_bitmap, int graphic)
 {
   int original_width = getOriginalImageWidthFromImageID(graphic);
   int scale_up_factor = graphic_info[graphic].scale_up_factor;
 
+  // only happens when loaded outside artwork system (like "global.busy")
+  if (graphic_info == image_initial && src_bitmap)
+    original_width = src_bitmap->width;
+
   return original_width * scale_up_factor;
 }
 
-static int get_scaled_graphic_height(int graphic)
+static int get_scaled_graphic_height(Bitmap *src_bitmap, int graphic)
 {
   int original_height = getOriginalImageHeightFromImageID(graphic);
   int scale_up_factor = graphic_info[graphic].scale_up_factor;
 
+  // only happens when loaded outside artwork system (like "global.busy")
+  if (graphic_info == image_initial && src_bitmap)
+    original_height = src_bitmap->height;
+
   return original_height * scale_up_factor;
 }
 
@@ -1321,6 +1412,7 @@ static void set_graphic_parameters_ext(int graphic, int *parameter,
   g->sort_priority = 0;                        // default for title screens
   g->class = 0;
   g->style = STYLE_DEFAULT;
+  g->alpha = -1;
 
   g->bitmaps = src_bitmaps;
   g->bitmap = src_bitmap;
@@ -1350,8 +1442,8 @@ static void set_graphic_parameters_ext(int graphic, int *parameter,
   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);
+    g->width  = get_scaled_graphic_width(src_bitmap, graphic);
+    g->height = get_scaled_graphic_height(src_bitmap, graphic);
   }
 
   // optional width and height of each animation frame
@@ -1398,15 +1490,8 @@ static void set_graphic_parameters_ext(int graphic, int *parameter,
   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;
-    }
+    int src_image_width  = get_scaled_graphic_width(src_bitmap, graphic);
+    int src_image_height = get_scaled_graphic_height(src_bitmap, graphic);
 
     if (parameter[GFX_ARG_TILE_SIZE] != ARG_UNDEFINED_VALUE)
     {
@@ -1501,6 +1586,9 @@ static void set_graphic_parameters_ext(int graphic, int *parameter,
   // animation synchronized with global frame counter, not move position
   g->anim_global_sync = parameter[GFX_ARG_GLOBAL_SYNC];
 
+  // animation synchronized with global anim frame counter, not move position
+  g->anim_global_anim_sync = parameter[GFX_ARG_GLOBAL_ANIM_SYNC];
+
   // optional element for cloning crumble graphics
   if (parameter[GFX_ARG_CRUMBLED_LIKE] != ARG_UNDEFINED_VALUE)
     g->crumbled_like = parameter[GFX_ARG_CRUMBLED_LIKE];
@@ -1561,7 +1649,7 @@ static void set_graphic_parameters_ext(int graphic, int *parameter,
   g->draw_yoffset = parameter[GFX_ARG_DRAW_YOFFSET];
 
   // use a different default value for global animations and toons
-  if ((graphic >= IMG_GFX_GLOBAL_ANIM_1 && graphic <= IMG_GFX_GLOBAL_ANIM_8) ||
+  if ((graphic >= IMG_GFX_GLOBAL_ANIM_1 && graphic <= IMG_GFX_GLOBAL_ANIM_32) ||
       (graphic >= IMG_TOON_1            && graphic <= IMG_TOON_20))
     g->draw_masked = TRUE;
 
@@ -1599,12 +1687,20 @@ static void set_graphic_parameters_ext(int graphic, int *parameter,
     g->class = parameter[GFX_ARG_CLASS];
   if (parameter[GFX_ARG_STYLE] != ARG_UNDEFINED_VALUE)
     g->style = parameter[GFX_ARG_STYLE];
+  if (parameter[GFX_ARG_ALPHA] != ARG_UNDEFINED_VALUE)
+    g->alpha = parameter[GFX_ARG_ALPHA];
 
   // 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];
+
+  // this is only used for drawing stacked global animations
+  g->stacked_xfactor = parameter[GFX_ARG_STACKED_XFACTOR];
+  g->stacked_yfactor = parameter[GFX_ARG_STACKED_YFACTOR];
+  g->stacked_xoffset = parameter[GFX_ARG_STACKED_XOFFSET];
+  g->stacked_yoffset = parameter[GFX_ARG_STACKED_YOFFSET];
 }
 
 static void set_graphic_parameters(int graphic)
@@ -1692,12 +1788,16 @@ static void InitGraphicInfo(void)
     IMG_BACKGROUND_REQUEST,
 
     IMG_BACKGROUND,
+    IMG_BACKGROUND_LOADING_INITIAL,
+    IMG_BACKGROUND_LOADING,
     IMG_BACKGROUND_TITLE_INITIAL,
     IMG_BACKGROUND_TITLE,
     IMG_BACKGROUND_MAIN,
+    IMG_BACKGROUND_NAMES,
     IMG_BACKGROUND_LEVELS,
     IMG_BACKGROUND_LEVELNR,
     IMG_BACKGROUND_SCORES,
+    IMG_BACKGROUND_SCOREINFO,
     IMG_BACKGROUND_EDITOR,
     IMG_BACKGROUND_INFO,
     IMG_BACKGROUND_INFO_ELEMENTS,
@@ -1879,6 +1979,16 @@ static void InitGraphicCompatibilityInfo(void)
        // process all images which default to same image as "global.door"
        if (strEqual(fi->default_filename, fi_global_door->default_filename))
        {
+         // skip all images that are cloned from images that default to same
+         // image as "global.door", but that are redefined to something else
+         if (graphic_info[i].clone_from != -1)
+         {
+           int cloned_graphic = graphic_info[i].clone_from;
+
+           if (getImageListEntryFromImageID(cloned_graphic)->redefined)
+             continue;
+         }
+
 #if 0
          Debug("init:InitGraphicCompatibilityInfo",
                "special treatment needed for token '%s'", fi->token);
@@ -1891,6 +2001,70 @@ static void InitGraphicCompatibilityInfo(void)
     }
   }
 
+  // special compatibility handling for "Snake Bite" graphics set
+  if (strPrefix(leveldir_current->identifier, "snake_bite"))
+  {
+    Bitmap *bitmap = graphic_info[IMG_BACKGROUND_SCORES].bitmap;
+
+    BlitBitmap(bitmap, bitmap, 18,  66, 32, 480, 50,  66);
+    BlitBitmap(bitmap, bitmap, 466, 66, 32, 480, 434, 66);
+
+    ClearRectangle(bitmap, 2,   66, 32, 480);
+    ClearRectangle(bitmap, 514, 66, 32, 480);
+  }
+
+  // special compatibility handling for "Jue" graphics sets (2007 and 2019)
+  boolean supports_score_info = (menu.draw_xoffset[GAME_MODE_SCOREINFO] != 0);
+  if (strPrefix(artwork.gfx_current_identifier, "jue") && !supports_score_info)
+  {
+    int font_title[] =
+    {
+      FONT_TITLE_1,
+      FONT_TITLE_2,
+
+      -1
+    };
+    int font_text[] =
+    {
+      FONT_TEXT_1,
+      FONT_TEXT_2,
+      FONT_TEXT_3,
+      FONT_TEXT_4,
+
+      -1
+    };
+    int mode_old = GAME_MODE_SCORES;
+    int mode_new = GAME_MODE_SCOREINFO;
+    int i, j;
+
+    // adjust title screens on score info page
+    for (i = 0; font_title[i] != -1; i++)
+    {
+      struct FontInfo *fi = &font_info[font_title[i]];
+
+      fi->special_graphic[mode_new]   = fi->special_graphic[mode_old];
+      fi->special_bitmap_id[mode_new] = fi->special_bitmap_id[mode_old];
+    }
+
+    // adjust vertical text and button positions on scores page
+    for (i = 0; font_text[i] != -1; i++)
+    {
+      for (j = 0; j < 2; j++)
+      {
+       boolean jue0 = strEqual(artwork.gfx_current_identifier, "jue0");
+       int font_nr = (j == 0 ? font_text[i] : FONT_ACTIVE(font_text[i]));
+       int font_bitmap_id = font_info[font_nr].special_bitmap_id[mode_old];
+       int font_yoffset = (jue0 ? 10 : 5);
+
+       gfx.font_bitmap_info[font_bitmap_id].draw_yoffset = font_yoffset;
+      }
+    }
+
+    // adjust page offsets on score info page
+    menu.draw_xoffset[mode_new] = menu.draw_xoffset[mode_old];
+    menu.draw_yoffset[mode_new] = menu.draw_yoffset[mode_old];
+  }
+
   InitGraphicCompatibilityInfo_Doors();
 }
 
@@ -2109,6 +2283,11 @@ static void InitSoundInfo(void)
     }
 
     set_sound_parameters(i, sound->parameter);
+
+#if 0
+    Debug("init:InitSoundInfo", "loop mode: %d ['%s']",
+         sound_info[i].loop, sound->token);
+#endif
   }
 
   free(sound_effect_properties);
@@ -2233,6 +2412,13 @@ static void InitMusicInfo(void)
   }
 }
 
+
+static void InitGameInfoFromArtworkInfo(void)
+{
+  // special case: store initial value of custom artwork setting
+  game.use_masked_elements_initial = game.use_masked_elements;
+}
+
 static void ReinitializeGraphics(void)
 {
   print_timestamp_init("ReinitializeGraphics");
@@ -2266,16 +2452,13 @@ static void ReinitializeGraphics(void)
   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");
   InitDoors();
   print_timestamp_time("InitDoors");
 
+  InitGameInfoFromArtworkInfo();
+
   print_timestamp_done("ReinitializeGraphics");
 }
 
@@ -2997,6 +3180,22 @@ void InitElementPropertiesStatic(void)
   static int ep_walkable_over[] =
   {
     EL_EMPTY_SPACE,
+    EL_EMPTY_SPACE_1,
+    EL_EMPTY_SPACE_2,
+    EL_EMPTY_SPACE_3,
+    EL_EMPTY_SPACE_4,
+    EL_EMPTY_SPACE_5,
+    EL_EMPTY_SPACE_6,
+    EL_EMPTY_SPACE_7,
+    EL_EMPTY_SPACE_8,
+    EL_EMPTY_SPACE_9,
+    EL_EMPTY_SPACE_10,
+    EL_EMPTY_SPACE_11,
+    EL_EMPTY_SPACE_12,
+    EL_EMPTY_SPACE_13,
+    EL_EMPTY_SPACE_14,
+    EL_EMPTY_SPACE_15,
+    EL_EMPTY_SPACE_16,
     EL_SP_EMPTY_SPACE,
     EL_SOKOBAN_FIELD_EMPTY,
     EL_EXIT_OPEN,
@@ -3294,6 +3493,29 @@ void InitElementPropertiesStatic(void)
     -1
   };
 
+  static int ep_empty_space[] =
+  {
+    EL_EMPTY_SPACE,
+    EL_EMPTY_SPACE_1,
+    EL_EMPTY_SPACE_2,
+    EL_EMPTY_SPACE_3,
+    EL_EMPTY_SPACE_4,
+    EL_EMPTY_SPACE_5,
+    EL_EMPTY_SPACE_6,
+    EL_EMPTY_SPACE_7,
+    EL_EMPTY_SPACE_8,
+    EL_EMPTY_SPACE_9,
+    EL_EMPTY_SPACE_10,
+    EL_EMPTY_SPACE_11,
+    EL_EMPTY_SPACE_12,
+    EL_EMPTY_SPACE_13,
+    EL_EMPTY_SPACE_14,
+    EL_EMPTY_SPACE_15,
+    EL_EMPTY_SPACE_16,
+
+    -1
+  };
+
   static int ep_player[] =
   {
     EL_PLAYER_1,
@@ -4056,6 +4278,7 @@ void InitElementPropertiesStatic(void)
     EL_BD_AMOEBA,
     EL_EMC_MAGIC_BALL,
     EL_EMC_ANDROID,
+    EL_MM_GRAY_BALL,
 
     -1
   };
@@ -4096,6 +4319,22 @@ void InitElementPropertiesStatic(void)
   static int ep_inactive[] =
   {
     EL_EMPTY,
+    EL_EMPTY_SPACE_1,
+    EL_EMPTY_SPACE_2,
+    EL_EMPTY_SPACE_3,
+    EL_EMPTY_SPACE_4,
+    EL_EMPTY_SPACE_5,
+    EL_EMPTY_SPACE_6,
+    EL_EMPTY_SPACE_7,
+    EL_EMPTY_SPACE_8,
+    EL_EMPTY_SPACE_9,
+    EL_EMPTY_SPACE_10,
+    EL_EMPTY_SPACE_11,
+    EL_EMPTY_SPACE_12,
+    EL_EMPTY_SPACE_13,
+    EL_EMPTY_SPACE_14,
+    EL_EMPTY_SPACE_15,
+    EL_EMPTY_SPACE_16,
     EL_SAND,
     EL_WALL,
     EL_BD_WALL,
@@ -4345,6 +4584,7 @@ void InitElementPropertiesStatic(void)
     EL_INTERNAL_CASCADE_STEEL_CHARS_ACTIVE,
     EL_INTERNAL_CASCADE_CE_ACTIVE,
     EL_INTERNAL_CASCADE_GE_ACTIVE,
+    EL_INTERNAL_CASCADE_ES_ACTIVE,
     EL_INTERNAL_CASCADE_REF_ACTIVE,
     EL_INTERNAL_CASCADE_USER_ACTIVE,
     EL_INTERNAL_CASCADE_DYNAMIC_ACTIVE,
@@ -4368,6 +4608,7 @@ void InitElementPropertiesStatic(void)
     EL_INTERNAL_CASCADE_STEEL_CHARS,
     EL_INTERNAL_CASCADE_CE,
     EL_INTERNAL_CASCADE_GE,
+    EL_INTERNAL_CASCADE_ES,
     EL_INTERNAL_CASCADE_REF,
     EL_INTERNAL_CASCADE_USER,
     EL_INTERNAL_CASCADE_DYNAMIC,
@@ -4425,6 +4666,7 @@ void InitElementPropertiesStatic(void)
     { ep_can_explode,                  EP_CAN_EXPLODE                  },
     { ep_gravity_reachable,            EP_GRAVITY_REACHABLE            },
 
+    { ep_empty_space,                  EP_EMPTY_SPACE                  },
     { 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       },
@@ -4661,7 +4903,7 @@ void InitElementPropertiesEngine(int engine_version)
                                                  i == EL_BLACK_ORB));
 
     // ---------- COULD_MOVE_INTO_ACID ----------------------------------------
-    SET_PROPERTY(i, EP_COULD_MOVE_INTO_ACID, (ELEM_IS_PLAYER(i) ||
+    SET_PROPERTY(i, EP_COULD_MOVE_INTO_ACID, (IS_PLAYER_ELEMENT(i) ||
                                              CAN_MOVE(i) ||
                                              IS_CUSTOM_ELEMENT(i)));
 
@@ -4686,7 +4928,7 @@ void InitElementPropertiesEngine(int engine_version)
     // ---------- 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_EMPTY(i) &&
                    IS_EQUAL_OR_IN_GROUP(i, level.android_clone_element[j])));
 
     // ---------- CAN_CHANGE --------------------------------------------------
@@ -4799,6 +5041,9 @@ static void InitGlobal(void)
     global_anim_info[i].token_name = global_anim_name_info[i].token_name;
   }
 
+  // create hash to store URLs for global animations
+  anim_url_hash = newSetupFileHash();
+
   // create hash from image config list
   image_config_hash = newSetupFileHash();
   for (i = 0; image_config[i].token != NULL; i++)
@@ -4900,7 +5145,10 @@ static void InitGlobal(void)
   global.autoplay_leveldir = NULL;
   global.patchtapes_leveldir = NULL;
   global.convert_leveldir = NULL;
-  global.create_images_dir = NULL;
+  global.dumplevel_leveldir = NULL;
+  global.dumptape_leveldir = NULL;
+  global.create_sketch_images_dir = NULL;
+  global.create_collect_images_dir = NULL;
 
   global.frames_per_second = 0;
   global.show_frames_per_second = FALSE;
@@ -4909,6 +5157,8 @@ static void InitGlobal(void)
   global.anim_status = global.anim_status_next = GAME_MODE_LOADING;
 
   global.use_envelope_request = FALSE;
+
+  global.user_names = NULL;
 }
 
 static void Execute_Command(char *command)
@@ -5010,39 +5260,70 @@ static void Execute_Command(char *command)
   {
     char *filename = &command[11];
 
-    if (!fileExists(filename))
+    if (fileExists(filename))
+    {
+      LoadLevelFromFilename(&level, filename);
+      DumpLevel(&level);
+
+      exit(0);
+    }
+
+    char *leveldir = getStringCopy(filename);  // read command parameters
+    char *level_nr = strchr(leveldir, ' ');
+
+    if (level_nr == NULL)
       Fail("cannot open file '%s'", filename);
 
-    LoadLevelFromFilename(&level, filename);
-    DumpLevel(&level);
+    *level_nr++ = '\0';
 
-    exit(0);
+    global.dumplevel_leveldir = leveldir;
+    global.dumplevel_level_nr = atoi(level_nr);
+
+    program.headless = TRUE;
   }
   else if (strPrefix(command, "dump tape "))
   {
     char *filename = &command[10];
 
-    if (!fileExists(filename))
+    if (fileExists(filename))
+    {
+      LoadTapeFromFilename(filename);
+      DumpTape(&tape);
+
+      exit(0);
+    }
+
+    char *leveldir = getStringCopy(filename);  // read command parameters
+    char *level_nr = strchr(leveldir, ' ');
+
+    if (level_nr == NULL)
       Fail("cannot open file '%s'", filename);
 
-    LoadTapeFromFilename(filename);
-    DumpTape(&tape);
+    *level_nr++ = '\0';
 
-    exit(0);
+    global.dumptape_leveldir = leveldir;
+    global.dumptape_level_nr = atoi(level_nr);
+
+    program.headless = TRUE;
   }
   else if (strPrefix(command, "autoplay ") ||
           strPrefix(command, "autoffwd ") ||
           strPrefix(command, "autowarp ") ||
           strPrefix(command, "autotest ") ||
+          strPrefix(command, "autosave ") ||
+          strPrefix(command, "autoupload ") ||
           strPrefix(command, "autofix "))
   {
-    char *str_ptr = getStringCopy(&command[8]);        // read command parameters
+    char *arg_ptr = strchr(command, ' ');
+    char *str_ptr = getStringCopy(arg_ptr);    // read command parameters
 
     global.autoplay_mode =
       (strPrefix(command, "autoplay") ? AUTOPLAY_MODE_PLAY :
        strPrefix(command, "autoffwd") ? AUTOPLAY_MODE_FFWD :
        strPrefix(command, "autowarp") ? AUTOPLAY_MODE_WARP :
        strPrefix(command, "autotest") ? AUTOPLAY_MODE_TEST :
+       strPrefix(command, "autosave") ? AUTOPLAY_MODE_SAVE :
+       strPrefix(command, "autoupload") ? AUTOPLAY_MODE_UPLOAD :
        strPrefix(command, "autofix")  ? AUTOPLAY_MODE_FIX :
        AUTOPLAY_MODE_NONE);
 
@@ -5156,13 +5437,21 @@ static void Execute_Command(char *command)
 
     program.headless = TRUE;
   }
-  else if (strPrefix(command, "create images "))
+  else if (strPrefix(command, "create sketch images "))
   {
-    global.create_images_dir = getStringCopy(&command[14]);
+    global.create_sketch_images_dir = getStringCopy(&command[21]);
 
-    if (access(global.create_images_dir, W_OK) != 0)
+    if (access(global.create_sketch_images_dir, W_OK) != 0)
       Fail("image target directory '%s' not found or not writable",
-          global.create_images_dir);
+          global.create_sketch_images_dir);
+  }
+  else if (strPrefix(command, "create collect image "))
+  {
+    global.create_collect_images_dir = getStringCopy(&command[21]);
+
+    if (access(global.create_collect_images_dir, W_OK) != 0)
+      Fail("image target directory '%s' not found or not writable",
+          global.create_collect_images_dir);
   }
   else if (strPrefix(command, "create CE image "))
   {
@@ -5181,14 +5470,22 @@ static void Execute_Command(char *command)
 
 static void InitSetup(void)
 {
+  LoadUserNames();                             // global user names
+  LoadUserSetup();                             // global user number
+
   LoadSetup();                                 // global setup info
-  LoadSetup_AutoSetup();                       // global auto setup info
 
   // set some options from setup file
 
   if (setup.options.verbose)
     options.verbose = TRUE;
 
+  if (setup.options.debug)
+    options.debug = TRUE;
+
+  if (!strEqual(setup.options.debug_mode, ARG_UNDEFINED_STRING))
+    options.debug_mode = getStringCopy(setup.options.debug_mode);
+
   if (setup.debug.show_frames_per_second)
     global.show_frames_per_second = TRUE;
 }
@@ -5196,8 +5493,9 @@ static void InitSetup(void)
 static void InitGameInfo(void)
 {
   game.restart_level = FALSE;
-  game.restart_game_message = NULL;
   game.request_active = FALSE;
+
+  game.use_masked_elements_initial = FALSE;
 }
 
 static void InitPlayerInfo(void)
@@ -5398,7 +5696,6 @@ void InitGfxBuffers(void)
   }
 
   ReCreateBitmap(&bitmap_db_field, FXSIZE, FYSIZE);
-  ReCreateBitmap(&bitmap_db_panel, DXSIZE, DYSIZE);
   ReCreateBitmap(&bitmap_db_door_1, 3 * DXSIZE, DYSIZE);
   ReCreateBitmap(&bitmap_db_door_2, 3 * VXSIZE, VYSIZE);
 
@@ -5418,15 +5715,32 @@ void InitGfxBuffers(void)
 
   InitGfxBuffers_EM();
   InitGfxBuffers_SP();
+  InitGfxBuffers_MM();
 }
 
 static void InitGfx(void)
 {
   struct GraphicInfo *graphic_info_last = graphic_info;
   char *filename_font_initial = NULL;
-  char *filename_anim_initial = NULL;
+  char *filename_image_initial[NUM_INITIAL_IMAGES] = { NULL };
+  char *image_token[NUM_INITIAL_IMAGES] =
+  {
+    CONFIG_TOKEN_GLOBAL_BUSY_INITIAL,
+    CONFIG_TOKEN_GLOBAL_BUSY,
+    CONFIG_TOKEN_GLOBAL_BUSY_PLAYFIELD,
+    CONFIG_TOKEN_BACKGROUND,
+    CONFIG_TOKEN_BACKGROUND_LOADING_INITIAL,
+    CONFIG_TOKEN_BACKGROUND_LOADING
+  };
+  struct MenuPosInfo *init_busy[NUM_INITIAL_IMAGES_BUSY] =
+  {
+    &init.busy_initial,
+    &init.busy,
+    &init.busy_playfield
+  };
   Bitmap *bitmap_font_initial = NULL;
-  int i, j;
+  int parameter[NUM_INITIAL_IMAGES][NUM_GFX_ARGS];
+  int i, j, k;
 
   // determine settings for initial font (for displaying startup messages)
   for (i = 0; image_config[i].token != NULL; i++)
@@ -5440,7 +5754,9 @@ static void InitGfx(void)
       len_font_token = strlen(font_token);
 
       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)
       {
@@ -5469,6 +5785,8 @@ static void InitGfx(void)
   InitGfxCustomArtworkInfo();
   InitGfxOtherSettings();
 
+  InitGfxTileSizeInfo(TILESIZE, TILESIZE);
+
   bitmap_font_initial = LoadCustomImage(filename_font_initial);
 
   for (j = 0; j < NUM_INITIAL_FONTS; j++)
@@ -5476,21 +5794,17 @@ static void InitGfx(void)
 
   InitFontGraphicInfo();
 
-  DrawProgramInfo();
-
-  DrawInitText("Loading graphics", 120, FC_GREEN);
-
-  // initialize settings for 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);
+  InitMenuDesignSettings_Static();
 
-  char *anim_token = CONFIG_TOKEN_GLOBAL_BUSY;
-  int len_anim_token = strlen(anim_token);
+  // initialize settings for initial images with default values
+  for (i = 0; i < NUM_INITIAL_IMAGES; i++)
+    for (j = 0; j < NUM_GFX_ARGS; j++)
+      parameter[i][j] =
+       get_graphic_parameter_value(image_config_suffix[j].value,
+                                   image_config_suffix[j].token,
+                                   image_config_suffix[j].type);
 
-  // read settings for busy animation from default custom artwork config
+  // read settings for initial images from default custom artwork config
   char *gfx_config_filename = getPath3(options.graphics_directory,
                                       GFX_DEFAULT_SUBDIR,
                                       GRAPHICSINFO_FILENAME);
@@ -5501,79 +5815,110 @@ static void InitGfx(void)
 
     if (setup_file_hash)
     {
-      char *filename = getHashEntry(setup_file_hash, anim_token);
-
-      if (filename)
+      for (i = 0; i < NUM_INITIAL_IMAGES; i++)
       {
-       filename_anim_initial = getStringCopy(filename);
+       char *filename = getHashEntry(setup_file_hash, image_token[i]);
 
-       for (j = 0; image_config_suffix[j].token != NULL; j++)
+       if (filename)
        {
-         int type = image_config_suffix[j].type;
-         char *suffix = image_config_suffix[j].token;
-         char *token = getStringCat2(anim_token, suffix);
-         char *value = getHashEntry(setup_file_hash, token);
+         filename_image_initial[i] = getStringCopy(filename);
+
+         for (j = 0; image_config_suffix[j].token != NULL; j++)
+         {
+           int type = image_config_suffix[j].type;
+           char *suffix = image_config_suffix[j].token;
+           char *token = getStringCat2(image_token[i], suffix);
+           char *value = getHashEntry(setup_file_hash, token);
 
-         checked_free(token);
+           checked_free(token);
 
-         if (value)
-           parameter[j] = get_graphic_parameter_value(value, suffix, type);
+           if (value)
+             parameter[i][j] =
+               get_graphic_parameter_value(value, suffix, type);
+         }
        }
       }
 
+      // read values from custom graphics config file
+      InitMenuDesignSettings_FromHash(setup_file_hash, FALSE);
+
       freeSetupFileHash(setup_file_hash);
     }
   }
 
-  if (filename_anim_initial == NULL)
+  for (i = 0; i < NUM_INITIAL_IMAGES; i++)
   {
-    // read settings for busy animation from static default artwork config
-    for (i = 0; image_config[i].token != NULL; i++)
+    if (filename_image_initial[i] == NULL)
     {
-      if (strEqual(image_config[i].token, anim_token))
-       filename_anim_initial = getStringCopy(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)
+      int len_token = strlen(image_token[i]);
+
+      // read settings for initial images from static default artwork config
+      for (j = 0; image_config[j].token != NULL; j++)
       {
-       for (j = 0; image_config_suffix[j].token != NULL; j++)
+       if (strEqual(image_config[j].token, image_token[i]))
        {
-         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);
+         filename_image_initial[i] = getStringCopy(image_config[j].value);
+       }
+       else if (strlen(image_config[j].token) > len_token &&
+                strncmp(image_config[j].token, image_token[i], len_token) == 0)
+       {
+         for (k = 0; image_config_suffix[k].token != NULL; k++)
+         {
+           if (strEqual(&image_config[j].token[len_token],
+                        image_config_suffix[k].token))
+             parameter[i][k] =
+               get_graphic_parameter_value(image_config[j].value,
+                                           image_config_suffix[k].token,
+                                           image_config_suffix[k].type);
+         }
        }
       }
     }
   }
 
-  if (filename_anim_initial == NULL)   // should not happen
-    Fail("cannot get filename for '%s'", CONFIG_TOKEN_GLOBAL_BUSY);
+  for (i = 0; i < NUM_INITIAL_IMAGES; i++)
+  {
+    if (filename_image_initial[i] == NULL)     // should not happen
+      Fail("cannot get filename for '%s'", image_token[i]);
+
+    image_initial[i].bitmaps =
+      checked_calloc(sizeof(Bitmap *) * NUM_IMG_BITMAP_POINTERS);
 
-  anim_initial.bitmaps =
-    checked_calloc(sizeof(Bitmap *) * NUM_IMG_BITMAP_POINTERS);
+    if (!strEqual(filename_image_initial[i], UNDEFINED_FILENAME))
+      image_initial[i].bitmaps[IMG_BITMAP_STANDARD] =
+       LoadCustomImage(filename_image_initial[i]);
 
-  anim_initial.bitmaps[IMG_BITMAP_STANDARD] =
-    LoadCustomImage(filename_anim_initial);
+    checked_free(filename_image_initial[i]);
+  }
 
-  checked_free(filename_anim_initial);
+  for (i = 0; i < NUM_INITIAL_IMAGES; i++)
+    image_initial[i].use_image_size = TRUE;
 
-  graphic_info = &anim_initial;                // graphic == 0 => anim_initial
+  graphic_info = image_initial;                // graphic == 0 => image_initial
 
-  set_graphic_parameters_ext(0, parameter, anim_initial.bitmaps);
+  for (i = 0; i < NUM_INITIAL_IMAGES; i++)
+    set_graphic_parameters_ext(i, parameter[i], image_initial[i].bitmaps);
 
   graphic_info = graphic_info_last;
 
-  init.busy.width  = anim_initial.width;
-  init.busy.height = anim_initial.height;
+  for (i = 0; i < NUM_INITIAL_IMAGES_BUSY; i++)
+  {
+    // set image size for busy animations
+    init_busy[i]->width  = image_initial[i].width;
+    init_busy[i]->height = image_initial[i].height;
+  }
+
+  SetLoadingBackgroundImage();
 
-  InitMenuDesignSettings_Static();
+  ClearRectangleOnBackground(window, 0, 0, WIN_XSIZE, WIN_YSIZE);
+
+  DrawProgramInfo();
 
   InitGfxDrawBusyAnimFunction(DrawInitAnim);
   InitGfxDrawGlobalAnimFunction(DrawGlobalAnimations);
   InitGfxDrawGlobalBorderFunction(DrawMaskedBorderToTarget);
   InitGfxDrawTileCursorFunction(DrawTileCursor);
+  InitGfxDrawEnvelopeRequestFunction(DrawEnvelopeRequestToScreen);
 
   gfx.fade_border_source_status = global.border_status;
   gfx.fade_border_target_status = global.border_status;
@@ -5681,17 +6026,14 @@ static void InitImages(void)
   print_timestamp_done("InitImages");
 }
 
-static void InitSound(char *identifier)
+static void InitSound(void)
 {
   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);
+  InitReloadCustomSounds();
   print_timestamp_time("InitReloadCustomSounds");
 
   ReinitializeSounds();
@@ -5700,17 +6042,14 @@ static void InitSound(char *identifier)
   print_timestamp_done("InitSound");
 }
 
-static void InitMusic(char *identifier)
+static void InitMusic(void)
 {
   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);
+  InitReloadCustomMusic();
   print_timestamp_time("InitReloadCustomMusic");
 
   ReinitializeMusic();
@@ -5899,11 +6238,12 @@ static void InitOverrideArtwork(void)
 #endif
 }
 
-static char *getNewArtworkIdentifier(int type)
+static char *setNewArtworkIdentifier(int type)
 {
-  static char *leveldir_current_identifier[3] = { NULL, NULL, NULL };
+  static char *last_leveldir_identifier[3] = { NULL, NULL, NULL };
+  static char *last_artwork_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 last_has_custom_artwork_set[3] = { FALSE, FALSE, FALSE };
   static boolean initialized[3] = { FALSE, FALSE, FALSE };
   TreeInfo *artwork_first_node = ARTWORK_FIRST_NODE(artwork, type);
   boolean setup_override_artwork = GFX_OVERRIDE_ARTWORK(type);
@@ -5912,6 +6252,9 @@ static char *getNewArtworkIdentifier(int type)
   // !!! setLevelArtworkDir() should be moved to an earlier stage !!!
   char *leveldir_artwork_set = setLevelArtworkDir(artwork_first_node);
   boolean has_level_artwork_set = (leveldir_artwork_set != NULL);
+  TreeInfo *custom_artwork_set =
+    getTreeInfoFromIdentifier(artwork_first_node, leveldir_identifier);
+  boolean has_custom_artwork_set = (custom_artwork_set != NULL);
   char *artwork_current_identifier;
   char *artwork_new_identifier = NULL; // default: nothing has changed
 
@@ -5929,24 +6272,23 @@ static char *getNewArtworkIdentifier(int type)
 
   if (setup_override_artwork)
     artwork_current_identifier = setup_artwork_set;
-  else if (leveldir_artwork_set != NULL)
+  else if (has_level_artwork_set)
     artwork_current_identifier = leveldir_artwork_set;
-  else if (getTreeInfoFromIdentifier(artwork_first_node, leveldir_identifier))
+  else if (has_custom_artwork_set)
     artwork_current_identifier = leveldir_identifier;
   else
     artwork_current_identifier = setup_artwork_set;
 
-
   /* 2nd step: check if it is really needed to reload artwork 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))
+  if (last_leveldir_identifier[type] != leveldir_identifier &&
+      (last_has_custom_artwork_set[type] || has_custom_artwork_set))
     artwork_new_identifier = artwork_current_identifier;
 
-  leveldir_current_identifier[type] = leveldir_identifier;
-  last_has_level_artwork_set[type] = has_level_artwork_set;
+  last_leveldir_identifier[type] = leveldir_identifier;
+  last_has_custom_artwork_set[type] = has_custom_artwork_set;
 
   // ---------- reload if "override artwork" setting has changed --------------
   if (last_override_level_artwork[type] != setup_override_artwork)
@@ -5955,11 +6297,14 @@ static char *getNewArtworkIdentifier(int type)
   last_override_level_artwork[type] = setup_override_artwork;
 
   // ---------- reload if current artwork identifier has changed --------------
-  if (!strEqual(ARTWORK_CURRENT_IDENTIFIER(artwork, type),
-               artwork_current_identifier))
+  if (!strEqual(last_artwork_identifier[type], artwork_current_identifier))
     artwork_new_identifier = artwork_current_identifier;
 
-  *(ARTWORK_CURRENT_IDENTIFIER_PTR(artwork, type))= artwork_current_identifier;
+  // (we cannot compare string pointers here, so copy string content itself)
+  setString(&last_artwork_identifier[type], artwork_current_identifier);
+
+  // ---------- set new artwork identifier ----------
+  *(ARTWORK_CURRENT_IDENTIFIER_PTR(artwork, type)) = artwork_current_identifier;
 
   // ---------- do not reload directly after starting -------------------------
   if (!initialized[type])
@@ -5970,6 +6315,13 @@ static char *getNewArtworkIdentifier(int type)
   return artwork_new_identifier;
 }
 
+static void InitArtworkIdentifier(void)
+{
+  setNewArtworkIdentifier(ARTWORK_TYPE_GRAPHICS);
+  setNewArtworkIdentifier(ARTWORK_TYPE_SOUNDS);
+  setNewArtworkIdentifier(ARTWORK_TYPE_MUSIC);
+}
+
 void ReloadCustomArtwork(int force_reload)
 {
   int last_game_status = game_status;  // save current game status
@@ -5983,12 +6335,12 @@ void ReloadCustomArtwork(int force_reload)
 
   InitOverrideArtwork();
 
-  force_reload_gfx |= AdjustGraphicsForEMC();
-  force_reload_snd |= AdjustSoundsForEMC();
+  AdjustGraphicsForEMC();
+  AdjustSoundsForEMC();
 
-  gfx_new_identifier = getNewArtworkIdentifier(ARTWORK_TYPE_GRAPHICS);
-  snd_new_identifier = getNewArtworkIdentifier(ARTWORK_TYPE_SOUNDS);
-  mus_new_identifier = getNewArtworkIdentifier(ARTWORK_TYPE_MUSIC);
+  gfx_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_GRAPHICS);
+  snd_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_SOUNDS);
+  mus_new_identifier = setNewArtworkIdentifier(ARTWORK_TYPE_MUSIC);
 
   reload_needed = (gfx_new_identifier != NULL || force_reload_gfx ||
                   snd_new_identifier != NULL || force_reload_snd ||
@@ -6003,11 +6355,17 @@ void ReloadCustomArtwork(int force_reload)
 
   FadeOut(REDRAW_ALL);
 
-  ClearRectangle(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
-  print_timestamp_time("ClearRectangle");
+  SetLoadingBackgroundImage();
+
+  ClearRectangleOnBackground(drawto, 0, 0, WIN_XSIZE, WIN_YSIZE);
+  print_timestamp_time("ClearRectangleOnBackground");
 
   FadeIn(REDRAW_ALL);
 
+  UPDATE_BUSY_STATE();
+
+  InitMissingFileHash();
+
   if (gfx_new_identifier != NULL || force_reload_gfx)
   {
 #if 0
@@ -6025,13 +6383,13 @@ void ReloadCustomArtwork(int force_reload)
 
   if (snd_new_identifier != NULL || force_reload_snd)
   {
-    InitSound(snd_new_identifier);
+    InitSound();
     print_timestamp_time("InitSound");
   }
 
   if (mus_new_identifier != NULL || force_reload_mus)
   {
-    InitMusic(mus_new_identifier);
+    InitMusic();
     print_timestamp_time("InitMusic");
   }
 
@@ -6039,8 +6397,6 @@ void ReloadCustomArtwork(int force_reload)
 
   SetGameStatus(last_game_status);     // restore current game status
 
-  init_last = init;                    // switch to new busy animation
-
   FadeOut(REDRAW_ALL);
 
   RedrawGlobalBorder();
@@ -6108,7 +6464,7 @@ void DisplayExitMessage(char *format, va_list ap)
   sy += 3 * font_height;
 
   num_lines_printed =
-    DrawTextBuffer(sx, sy, program.log_filename[LOG_ERR_ID], font_2,
+    DrawTextBuffer(sx, sy, program.log_filename, font_2,
                   line_length, line_length, max_lines,
                   0, BLIT_ON_BACKGROUND, TRUE, TRUE, FALSE);
 
@@ -6121,8 +6477,8 @@ void DisplayExitMessage(char *format, va_list ap)
 
   BackToFront();
 
-  // deactivate toons on error message screen
-  setup.toons = FALSE;
+  // deactivate toons and global animations on error message screen
+  setup.global_animations = FALSE;
 
   WaitForEventToContinue();
 }
@@ -6142,14 +6498,18 @@ void OpenAll(void)
 
   InitGlobal();                        // initialize some global variables
 
+  InitRND(NEW_RANDOMIZE);
+  InitSimpleRandom(NEW_RANDOMIZE);
+  InitBetterRandom(NEW_RANDOMIZE);
+
+  InitMissingFileHash();
+
   print_timestamp_time("[init global stuff]");
 
   InitSetup();
 
   print_timestamp_time("[init setup/config stuff (1)]");
 
-  InitScoresInfo();
-
   if (options.execute_command)
     Execute_Command(options.execute_command);
 
@@ -6179,9 +6539,6 @@ void OpenAll(void)
   InitMixer();
   print_timestamp_time("[init setup/config stuff (6)]");
 
-  InitRND(NEW_RANDOMIZE);
-  InitSimpleRandom(NEW_RANDOMIZE);
-
   InitJoysticks();
 
   print_timestamp_time("[init setup/config stuff]");
@@ -6214,13 +6571,16 @@ void OpenAll(void)
   InitOverrideArtwork();       // needs to know current level directory
   print_timestamp_time("InitOverrideArtwork");
 
+  InitArtworkIdentifier();     // needs to know current level directory
+  print_timestamp_time("InitArtworkIdentifier");
+
   InitImages();                        // needs to know current level directory
   print_timestamp_time("InitImages");
 
-  InitSound(NULL);             // needs to know current level directory
+  InitSound();                 // needs to know current level directory
   print_timestamp_time("InitSound");
 
-  InitMusic(NULL);             // needs to know current level directory
+  InitMusic();                 // needs to know current level directory
   print_timestamp_time("InitMusic");
 
   InitArtworkDone();
@@ -6246,11 +6606,26 @@ void OpenAll(void)
     ConvertLevels();
     return;
   }
-  else if (global.create_images_dir)
+  else if (global.dumplevel_leveldir)
+  {
+    DumpLevels();
+    return;
+  }
+  else if (global.dumptape_leveldir)
+  {
+    DumpTapes();
+    return;
+  }
+  else if (global.create_sketch_images_dir)
   {
     CreateLevelSketchImages();
     return;
   }
+  else if (global.create_collect_images_dir)
+  {
+    CreateCollectElementImages();
+    return;
+  }
 
   InitNetworkServer();
 
@@ -6264,6 +6639,9 @@ void OpenAll(void)
 
   print_timestamp_done("OpenAll");
 
+  if (setup.ask_for_remaining_tapes)
+    setup.ask_for_uploading_tapes = TRUE;
+
   DrawMainMenu();
 
 #if 0
@@ -6285,8 +6663,43 @@ void OpenAll(void)
 #endif
 }
 
+static boolean WaitForApiThreads(void)
+{
+  DelayCounter thread_delay = { 10000 };
+
+  if (program.api_thread_count == 0)
+    return TRUE;
+
+  // deactivate global animations (not accessible in game state "loading")
+  setup.global_animations = FALSE;
+
+  // set game state to "loading" to be able to show busy animation
+  SetGameStatus(GAME_MODE_LOADING);
+
+  ResetDelayCounter(&thread_delay);
+
+  // wait for threads to finish (and fail on timeout)
+  while (program.api_thread_count > 0)
+  {
+    if (DelayReached(&thread_delay))
+    {
+      Error("failed waiting for threads - TIMEOUT");
+
+      return FALSE;
+    }
+
+    UPDATE_BUSY_STATE();
+
+    Delay(20);
+  }
+
+  return TRUE;
+}
+
 void CloseAllAndExit(int exit_value)
 {
+  WaitForApiThreads();
+
   StopSounds();
   FreeAllSounds();
   FreeAllMusic();