fixed bug with even sized playfield screen and levels without border element
[rocksndiamonds.git] / src / tools.c
index 21f062b41cdc21fb2bfc3c257f08ffbefc87d9f7..c2b1ec15c80b9e31719171cb40b7d587f4f33478 100644 (file)
 #include "screens.h"
 
 
-/* select level set with EMC X11 graphics before activating EM GFX debugging */
+// select level set with EMC X11 graphics before activating EM GFX debugging
 #define DEBUG_EM_GFX           FALSE
 #define DEBUG_FRAME_TIME       FALSE
 
-/* tool button identifiers */
+// tool button identifiers
 #define TOOL_CTRL_ID_YES       0
 #define TOOL_CTRL_ID_NO                1
 #define TOOL_CTRL_ID_CONFIRM   2
@@ -38,7 +38,7 @@
 
 #define NUM_TOOL_BUTTONS       7
 
-/* constants for number of doors and door parts */
+// constants for number of doors and door parts
 #define NUM_DOORS              2
 #define NUM_PANELS             NUM_DOORS
 // #define NUM_PANELS          0
@@ -165,8 +165,8 @@ static struct DoorPartControlInfo door_part_controls[] =
 };
 
 
-/* forward declaration for internal use */
-static void UnmapToolButtons();
+// forward declaration for internal use
+static void UnmapToolButtons(void);
 static void HandleToolButtons(struct GadgetInfo *);
 static int el_act_dir2crm(int, int, int);
 static int el_act2crm(int, int);
@@ -208,7 +208,7 @@ int correctLevelPosY_EM(int ly)
   return ly;
 }
 
-static int getFieldbufferOffsetX_RND()
+static int getFieldbufferOffsetX_RND(void)
 {
   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
   int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
@@ -217,9 +217,10 @@ static int getFieldbufferOffsetX_RND()
 
   if (EVEN(SCR_FIELDX))
   {
+    int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
     int ffx = (scroll_x - SBX_Left)  * TILEX_VAR + dx_var;
 
-    if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
+    if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
       fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
     else
       fx += (dx_var > 0 ? TILEX_VAR : 0);
@@ -240,7 +241,7 @@ static int getFieldbufferOffsetX_RND()
   return fx;
 }
 
-static int getFieldbufferOffsetY_RND()
+static int getFieldbufferOffsetY_RND(void)
 {
   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
   int dy = (ScreenMovDir & (MV_UP | MV_DOWN)    ? ScreenGfxPos : 0);
@@ -249,9 +250,10 @@ static int getFieldbufferOffsetY_RND()
 
   if (EVEN(SCR_FIELDY))
   {
+    int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
     int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
 
-    if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
+    if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
       fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
     else
       fy += (dy_var > 0 ? TILEY_VAR : 0);
@@ -358,12 +360,40 @@ static int getLevelFromScreenY_SP(int sy)
   return ly;
 }
 
+static int getLevelFromScreenX_MM(int sx)
+{
+  int level_xsize = level.native_mm_level->fieldx;
+  int full_xsize = level_xsize * TILESIZE_VAR;
+
+  sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
+
+  int px = sx - SX;
+  int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
+
+  return lx;
+}
+
+static int getLevelFromScreenY_MM(int sy)
+{
+  int level_ysize = level.native_mm_level->fieldy;
+  int full_ysize = level_ysize * TILESIZE_VAR;
+
+  sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
+
+  int py = sy - SY;
+  int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
+
+  return ly;
+}
+
 int getLevelFromScreenX(int x)
 {
   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
     return getLevelFromScreenX_EM(x);
   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
     return getLevelFromScreenX_SP(x);
+  if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
+    return getLevelFromScreenX_MM(x);
   else
     return getLevelFromScreenX_RND(x);
 }
@@ -374,6 +404,8 @@ int getLevelFromScreenY(int y)
     return getLevelFromScreenY_EM(y);
   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
     return getLevelFromScreenY_SP(y);
+  if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
+    return getLevelFromScreenY_MM(y);
   else
     return getLevelFromScreenY_RND(y);
 }
@@ -382,6 +414,7 @@ void DumpTile(int x, int y)
 {
   int sx = SCREENX(x);
   int sy = SCREENY(y);
+  char *token_name;
 
   printf_line("-", 79);
   printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
@@ -395,8 +428,9 @@ void DumpTile(int x, int y)
     return;
   }
 
-  printf("  Feld:        %d\t['%s']\n", Feld[x][y],
-        element_info[Feld[x][y]].token_name);
+  token_name = element_info[Feld[x][y]].token_name;
+
+  printf("  Feld:        %d\t['%s']\n", Feld[x][y], token_name);
   printf("  Back:        %s\n", print_if_not_empty(Back[x][y]));
   printf("  Store:       %s\n", print_if_not_empty(Store[x][y]));
   printf("  Store2:      %s\n", print_if_not_empty(Store2[x][y]));
@@ -434,7 +468,7 @@ void SetDrawtoField(int mode)
 
     drawto_field = fieldbuffer;
   }
-  else /* DRAW_TO_BACKBUFFER */
+  else // DRAW_TO_BACKBUFFER
   {
     FX = SX;
     FY = SY;
@@ -447,7 +481,7 @@ void SetDrawtoField(int mode)
   }
 }
 
-static void RedrawPlayfield_RND()
+static void RedrawPlayfield_RND(void)
 {
   if (game.envelope_active)
     return;
@@ -456,7 +490,7 @@ static void RedrawPlayfield_RND()
   DrawAllPlayers();
 }
 
-void RedrawPlayfield()
+void RedrawPlayfield(void)
 {
   if (game_status != GAME_MODE_PLAYING)
     return;
@@ -465,6 +499,8 @@ void RedrawPlayfield()
     RedrawPlayfield_EM(TRUE);
   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
     RedrawPlayfield_SP(TRUE);
+  else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
+    RedrawPlayfield_MM();
   else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
     RedrawPlayfield_RND();
 
@@ -525,7 +561,7 @@ static void DrawMaskedBorderExt_DOOR_2(int draw_target)
 
 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
 {
-  /* currently not available */
+  // currently not available
 }
 
 static void DrawMaskedBorderExt_ALL(int draw_target)
@@ -538,7 +574,7 @@ static void DrawMaskedBorderExt_ALL(int draw_target)
 
 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
 {
-  /* never draw masked screen borders on borderless screens */
+  // never draw masked screen borders on borderless screens
   if (global.border_status == GAME_MODE_LOADING ||
       global.border_status == GAME_MODE_TITLE)
     return;
@@ -558,7 +594,7 @@ static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
   }
 }
 
-void DrawMaskedBorder_FIELD()
+void DrawMaskedBorder_FIELD(void)
 {
   DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
 }
@@ -597,6 +633,64 @@ void DrawMaskedBorderToTarget(int draw_target)
   }
 }
 
+void DrawTileCursor(int draw_target)
+{
+  Bitmap *fade_bitmap;
+  Bitmap *src_bitmap;
+  int src_x, src_y;
+  int dst_x, dst_y;
+  int graphic = IMG_GLOBAL_TILE_CURSOR;
+  int frame = 0;
+  int tilesize = TILESIZE_VAR;
+  int width = tilesize;
+  int height = tilesize;
+
+  if (game_status != GAME_MODE_PLAYING)
+    return;
+
+  if (!tile_cursor.enabled ||
+      !tile_cursor.active)
+    return;
+
+  if (tile_cursor.moving)
+  {
+    int step = TILESIZE_VAR / 4;
+    int dx = tile_cursor.target_x - tile_cursor.x;
+    int dy = tile_cursor.target_y - tile_cursor.y;
+
+    if (ABS(dx) < step)
+      tile_cursor.x = tile_cursor.target_x;
+    else
+      tile_cursor.x += SIGN(dx) * step;
+
+    if (ABS(dy) < step)
+      tile_cursor.y = tile_cursor.target_y;
+    else
+      tile_cursor.y += SIGN(dy) * step;
+
+    if (tile_cursor.x == tile_cursor.target_x &&
+       tile_cursor.y == tile_cursor.target_y)
+      tile_cursor.moving = FALSE;
+  }
+
+  dst_x = tile_cursor.x;
+  dst_y = tile_cursor.y;
+
+  frame = getGraphicAnimationFrame(graphic, -1);
+
+  getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
+
+  fade_bitmap =
+    (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
+     draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
+
+  if (draw_target == DRAW_TO_SCREEN)
+    BlitToScreenMasked(src_bitmap, src_x, src_y, width, height, dst_x, dst_y);
+  else
+    BlitBitmapMasked(src_bitmap, fade_bitmap, src_x, src_y, width, height,
+                    dst_x, dst_y);
+}
+
 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
 {
   int fx = getFieldbufferOffsetX_RND();
@@ -611,13 +705,15 @@ void BlitScreenToBitmap(Bitmap *target_bitmap)
     BlitScreenToBitmap_EM(target_bitmap);
   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
     BlitScreenToBitmap_SP(target_bitmap);
+  else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
+    BlitScreenToBitmap_MM(target_bitmap);
   else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
     BlitScreenToBitmap_RND(target_bitmap);
 
   redraw_mask |= REDRAW_FIELD;
 }
 
-void DrawFramesPerSecond()
+static void DrawFramesPerSecond(void)
 {
   char text[100];
   int font_nr = FONT_TEXT_2;
@@ -625,25 +721,25 @@ void DrawFramesPerSecond()
   int draw_deactivation_mask = GetDrawDeactivationMask();
   boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
 
-  /* draw FPS with leading space (needed if field buffer deactivated) */
+  // draw FPS with leading space (needed if field buffer deactivated)
   sprintf(text, " %04.1f fps", global.frames_per_second);
 
-  /* override draw deactivation mask (required for invisible warp mode) */
+  // override draw deactivation mask (required for invisible warp mode)
   SetDrawDeactivationMask(REDRAW_NONE);
 
-  /* draw opaque FPS if field buffer deactivated, else draw masked FPS */
+  // draw opaque FPS if field buffer deactivated, else draw masked FPS
   DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
              font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
 
-  /* set draw deactivation mask to previous value */
+  // set draw deactivation mask to previous value
   SetDrawDeactivationMask(draw_deactivation_mask);
 
-  /* force full-screen redraw in this frame */
+  // force full-screen redraw in this frame
   redraw_mask = REDRAW_ALL;
 }
 
 #if DEBUG_FRAME_TIME
-static void PrintFrameTimeDebugging()
+static void PrintFrameTimeDebugging(void)
 {
   static unsigned int last_counter = 0;
   unsigned int counter = Counter();
@@ -696,7 +792,7 @@ static boolean equalRedrawMasks(int mask_1, int mask_2)
   return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
 }
 
-void BackToFront()
+void BackToFront(void)
 {
   static int last_redraw_mask = REDRAW_NONE;
 
@@ -812,7 +908,7 @@ static void FadeExt(int fade_mask, int fade_mode, int fade_type)
   {
     if (fade_type_skip != FADE_TYPE_NONE)
     {
-      /* skip all fade operations until specified fade operation */
+      // skip all fade operations until specified fade operation
       if (fade_type & fade_type_skip)
        fade_type_skip = FADE_TYPE_NONE;
 
@@ -837,7 +933,7 @@ static void FadeExt(int fade_mask, int fade_mode, int fade_type)
 
   if (fade_type_skip != FADE_TYPE_NONE)
   {
-    /* skip all fade operations until specified fade operation */
+    // skip all fade operations until specified fade operation
     if (fade_type & fade_type_skip)
       fade_type_skip = FADE_TYPE_NONE;
 
@@ -857,11 +953,11 @@ static void FadeExt(int fade_mask, int fade_mode, int fade_type)
     height = FADE_SYSIZE;
 
     if (border.draw_masked_when_fading)
-      draw_border_function = DrawMaskedBorder_FIELD;   /* update when fading */
+      draw_border_function = DrawMaskedBorder_FIELD;   // update when fading
     else
-      DrawMaskedBorder_FIELD();                                /* draw once */
+      DrawMaskedBorder_FIELD();                                // draw once
   }
-  else         /* REDRAW_ALL */
+  else         // REDRAW_ALL
   {
     x = 0;
     y = 0;
@@ -887,9 +983,11 @@ static void FadeExt(int fade_mask, int fade_mode, int fade_type)
                draw_border_function);
 
   redraw_mask &= ~fade_mask;
+
+  ClearAutoRepeatKeyEvents();
 }
 
-static void SetScreenStates_BeforeFadingIn()
+static void SetScreenStates_BeforeFadingIn(void)
 {
   // temporarily set screen mode for animations to screen after fading in
   global.anim_status = global.anim_status_next;
@@ -902,7 +1000,7 @@ static void SetScreenStates_BeforeFadingIn()
   global.anim_status = GAME_MODE_PSEUDO_FADING;
 }
 
-static void SetScreenStates_AfterFadingIn()
+static void SetScreenStates_AfterFadingIn(void)
 {
   // store new source screen (to use correct masked border for fading)
   gfx.fade_border_source_status = global.border_status;
@@ -910,7 +1008,7 @@ static void SetScreenStates_AfterFadingIn()
   global.anim_status = global.anim_status_next;
 }
 
-static void SetScreenStates_BeforeFadingOut()
+static void SetScreenStates_BeforeFadingOut(void)
 {
   // store new target screen (to use correct masked border for fading)
   gfx.fade_border_target_status = game_status;
@@ -923,7 +1021,7 @@ static void SetScreenStates_BeforeFadingOut()
     PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
 }
 
-static void SetScreenStates_AfterFadingOut()
+static void SetScreenStates_AfterFadingOut(void)
 {
   global.border_status = game_status;
 }
@@ -946,8 +1044,9 @@ void FadeIn(int fade_mask)
   FADE_SXSIZE = FULL_SXSIZE;
   FADE_SYSIZE = FULL_SYSIZE;
 
-  if (game_status == GAME_MODE_PLAYING &&
-      strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
+  // activate virtual buttons depending on upcoming game status
+  if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
+      game_status == GAME_MODE_PLAYING && !tape.playing)
     SetOverlayActive(TRUE);
 
   SetScreenStates_AfterFadingIn();
@@ -965,6 +1064,7 @@ void FadeOut(int fade_mask)
 
   SetScreenStates_BeforeFadingOut();
 
+  SetTileCursorActive(FALSE);
   SetOverlayActive(FALSE);
 
 #if 0
@@ -989,38 +1089,38 @@ static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
     fading = fading_leave_stored;
 }
 
-void FadeSetEnterMenu()
+void FadeSetEnterMenu(void)
 {
   fading = menu.enter_menu;
 
-  FadeSetLeaveNext(fading, TRUE);      /* (keep same fade mode) */
+  FadeSetLeaveNext(fading, TRUE);      // (keep same fade mode)
 }
 
-void FadeSetLeaveMenu()
+void FadeSetLeaveMenu(void)
 {
   fading = menu.leave_menu;
 
-  FadeSetLeaveNext(fading, TRUE);      /* (keep same fade mode) */
+  FadeSetLeaveNext(fading, TRUE);      // (keep same fade mode)
 }
 
-void FadeSetEnterScreen()
+void FadeSetEnterScreen(void)
 {
   fading = menu.enter_screen[game_status];
 
-  FadeSetLeaveNext(menu.leave_screen[game_status], TRUE);      /* store */
+  FadeSetLeaveNext(menu.leave_screen[game_status], TRUE);      // store
 }
 
-void FadeSetNextScreen()
+void FadeSetNextScreen(void)
 {
   fading = menu.next_screen[game_status];
 
   // (do not overwrite fade mode set by FadeSetEnterScreen)
-  // FadeSetLeaveNext(fading, TRUE);   /* (keep same fade mode) */
+  // FadeSetLeaveNext(fading, TRUE);   // (keep same fade mode)
 }
 
-void FadeSetLeaveScreen()
+void FadeSetLeaveScreen(void)
 {
-  FadeSetLeaveNext(menu.leave_screen[game_status], FALSE);     /* recall */
+  FadeSetLeaveNext(menu.leave_screen[game_status], FALSE);     // recall
 }
 
 void FadeSetFromType(int type)
@@ -1033,39 +1133,41 @@ void FadeSetFromType(int type)
     FadeSetLeaveMenu();
 }
 
-void FadeSetDisabled()
+void FadeSetDisabled(void)
 {
   static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
 
   fading = fading_none;
 }
 
-void FadeSkipNextFadeIn()
+void FadeSkipNextFadeIn(void)
 {
   FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
 }
 
-void FadeSkipNextFadeOut()
+void FadeSkipNextFadeOut(void)
 {
   FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
 }
 
-Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
+static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
 {
+  if (graphic == IMG_UNDEFINED)
+    return NULL;
+
   boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
 
-  return (graphic == IMG_UNDEFINED ? NULL :
-         graphic_info[graphic].bitmap != NULL || redefined ?
+  return (graphic_info[graphic].bitmap != NULL || redefined ?
          graphic_info[graphic].bitmap :
          graphic_info[default_graphic].bitmap);
 }
 
-Bitmap *getBackgroundBitmap(int graphic)
+static Bitmap *getBackgroundBitmap(int graphic)
 {
   return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
 }
 
-Bitmap *getGlobalBorderBitmap(int graphic)
+static Bitmap *getGlobalBorderBitmap(int graphic)
 {
   return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
 }
@@ -1116,7 +1218,7 @@ void SetDoorBackgroundImage(int graphic)
   SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
 }
 
-void SetPanelBackground()
+void SetPanelBackground(void)
 {
   struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
 
@@ -1128,7 +1230,7 @@ void SetPanelBackground()
 
 void DrawBackground(int x, int y, int width, int height)
 {
-  /* "drawto" might still point to playfield buffer here (hall of fame) */
+  // "drawto" might still point to playfield buffer here (hall of fame)
   ClearRectangleOnBackground(backbuffer, x, y, width, height);
 
   if (IN_GFX_FIELD_FULL(x, y))
@@ -1170,8 +1272,10 @@ static int dx_last = -1, dy_last = -1;
 static int dxsize_last = -1, dysize_last = -1;
 static int vx_last = -1, vy_last = -1;
 static int vxsize_last = -1, vysize_last = -1;
+static int ex_last = -1, ey_last = -1;
+static int exsize_last = -1, eysize_last = -1;
 
-boolean CheckIfGlobalBorderHasChanged()
+boolean CheckIfGlobalBorderHasChanged(void)
 {
   // if game status has not changed, global border has not changed either
   if (game_status == game_status_last)
@@ -1183,7 +1287,10 @@ boolean CheckIfGlobalBorderHasChanged()
   return (global_border_bitmap_last != global_border_bitmap);
 }
 
-boolean CheckIfGlobalBorderRedrawIsNeeded()
+#define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED            0
+
+#if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
+static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
 {
   // if game status has not changed, nothing has to be redrawn
   if (game_status == game_status_last)
@@ -1212,10 +1319,16 @@ boolean CheckIfGlobalBorderRedrawIsNeeded()
       vxsize_last != VXSIZE || vysize_last != VYSIZE)
     return TRUE;
 
+  // redraw if position or size of editor area has changed
+  if (ex_last != EX || ey_last != EY ||
+      exsize_last != EXSIZE || eysize_last != EYSIZE)
+    return TRUE;
+
   return FALSE;
 }
+#endif
 
-void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
+static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
 {
   if (bitmap)
     BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
@@ -1223,7 +1336,7 @@ void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
     ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
 }
 
-void RedrawGlobalBorder()
+void RedrawGlobalBorder(void)
 {
   Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
 
@@ -1232,20 +1345,27 @@ void RedrawGlobalBorder()
   redraw_mask = REDRAW_ALL;
 }
 
-static void RedrawGlobalBorderIfNeeded()
+static void RedrawGlobalBorderIfNeeded(void)
 {
+#if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
   if (game_status == game_status_last)
     return;
+#endif
 
   // copy current draw buffer to later copy back areas that have not changed
   if (game_status_last != GAME_MODE_TITLE)
     BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
 
+#if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
   if (CheckIfGlobalBorderRedrawIsNeeded())
+#endif
   {
     // redraw global screen border (or clear, if defined to be empty)
     RedrawGlobalBorderFromBitmap(global_border_bitmap);
 
+    if (game_status == GAME_MODE_EDITOR)
+      DrawSpecialEditorDoor();
+
     // copy previous playfield and door areas, if they are defined on both
     // previous and current screen and if they still have the same size
 
@@ -1262,11 +1382,22 @@ static void RedrawGlobalBorderIfNeeded()
       BlitBitmap(bitmap_db_store_1, backbuffer,
                 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
 
-    if (vx_last != -1 && vy_last != -1 &&
-       VX != -1 && VY != -1 &&
-       vxsize_last == VXSIZE && vysize_last == VYSIZE)
-      BlitBitmap(bitmap_db_store_1, backbuffer,
-                vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
+    if (game_status != GAME_MODE_EDITOR)
+    {
+      if (vx_last != -1 && vy_last != -1 &&
+         VX != -1 && VY != -1 &&
+         vxsize_last == VXSIZE && vysize_last == VYSIZE)
+       BlitBitmap(bitmap_db_store_1, backbuffer,
+                  vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
+    }
+    else
+    {
+      if (ex_last != -1 && ey_last != -1 &&
+         EX != -1 && EY != -1 &&
+         exsize_last == EXSIZE && eysize_last == EYSIZE)
+       BlitBitmap(bitmap_db_store_1, backbuffer,
+                  ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
+    }
 
     redraw_mask = REDRAW_ALL;
   }
@@ -1287,17 +1418,21 @@ static void RedrawGlobalBorderIfNeeded()
   vy_last = VY;
   vxsize_last = VXSIZE;
   vysize_last = VYSIZE;
+  ex_last = EX;
+  ey_last = EY;
+  exsize_last = EXSIZE;
+  eysize_last = EYSIZE;
 }
 
-void ClearField()
+void ClearField(void)
 {
   RedrawGlobalBorderIfNeeded();
 
-  /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
-  /* (when entering hall of fame after playing) */
+  // !!! "drawto" might still point to playfield buffer here (see above) !!!
+  // (when entering hall of fame after playing)
   DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
 
-  /* !!! maybe this should be done before clearing the background !!! */
+  // !!! maybe this should be done before clearing the background !!!
   if (game_status == GAME_MODE_PLAYING)
   {
     ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
@@ -1314,12 +1449,16 @@ void MarkTileDirty(int x, int y)
   redraw_mask |= REDRAW_FIELD;
 }
 
-void SetBorderElement()
+void SetBorderElement(void)
 {
   int x, y;
 
   BorderElement = EL_EMPTY;
 
+  // the MM game engine does not use a visible border element
+  if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
+    return;
+
   for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
   {
     for (x = 0; x < lev_fieldx; x++)
@@ -1333,16 +1472,17 @@ void SetBorderElement()
   }
 }
 
-void FloodFillLevel(int from_x, int from_y, int fill_element,
-                   short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
-                   int max_fieldx, int max_fieldy)
+void FloodFillLevelExt(int from_x, int from_y, int fill_element,
+                      int max_array_fieldx, int max_array_fieldy,
+                      short field[max_array_fieldx][max_array_fieldy],
+                      int max_fieldx, int max_fieldy)
 {
   int i,x,y;
   int old_element;
   static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
   static int safety = 0;
 
-  /* check if starting field still has the desired content */
+  // check if starting field still has the desired content
   if (field[from_x][from_y] == fill_element)
     return;
 
@@ -1360,12 +1500,22 @@ void FloodFillLevel(int from_x, int from_y, int fill_element,
     y = from_y + check[i][1];
 
     if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
-      FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
+      FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
+                       field, max_fieldx, max_fieldy);
   }
 
   safety--;
 }
 
+void FloodFillLevel(int from_x, int from_y, int fill_element,
+                   short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
+                   int max_fieldx, int max_fieldy)
+{
+  FloodFillLevelExt(from_x, from_y, fill_element,
+                   MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
+                   max_fieldx, max_fieldy);
+}
+
 void SetRandomAnimationValue(int x, int y)
 {
   gfx.anim_random_frame = GfxRandom[x][y];
@@ -1373,7 +1523,7 @@ void SetRandomAnimationValue(int x, int y)
 
 int getGraphicAnimationFrame(int graphic, int sync_frame)
 {
-  /* animation synchronized with global frame counter, not move position */
+  // animation synchronized with global frame counter, not move position
   if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
     sync_frame = FrameCounter;
 
@@ -1404,7 +1554,7 @@ void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
   int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
   int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
 
-  if (g->offset_y == 0)                /* frames are ordered horizontally */
+  if (g->offset_y == 0)                // frames are ordered horizontally
   {
     int max_width = g->anim_frames_per_line * g->width;
     int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
@@ -1412,7 +1562,7 @@ void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
     *x = pos % max_width;
     *y = src_y % g->height + pos / max_width * g->height;
   }
-  else if (g->offset_x == 0)   /* frames are ordered vertically */
+  else if (g->offset_x == 0)   // frames are ordered vertically
   {
     int max_height = g->anim_frames_per_line * g->height;
     int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
@@ -1420,7 +1570,7 @@ void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
     *x = src_x % g->width + pos / max_height * g->width;
     *y = pos % max_height;
   }
-  else                         /* frames are ordered diagonally */
+  else                         // frames are ordered diagonally
   {
     *x = src_x + frame * g->offset_x;
     *y = src_y + frame * g->offset_y;
@@ -1433,6 +1583,10 @@ void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
 {
   struct GraphicInfo *g = &graphic_info[graphic];
 
+  // if no graphics defined at all, use fallback graphics
+  if (g->bitmaps == NULL)
+    *g = graphic_info[IMG_CHAR_EXCLAM];
+
   // if no in-game graphics defined, always use standard graphic size
   if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
     tilesize = TILESIZE;
@@ -1461,8 +1615,8 @@ void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
   getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
 }
 
-inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
-                                      int *x, int *y, boolean get_backside)
+static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
+                               int *x, int *y, boolean get_backside)
 {
   getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
                           get_backside);
@@ -1591,6 +1745,14 @@ void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
   MarkTileDirty(x / tilesize, y / tilesize);
 }
 
+void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
+                             int tilesize)
+{
+  DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
+                             graphic, frame, tilesize);
+  MarkTileDirty(x / tilesize, y / tilesize);
+}
+
 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
                         int tilesize)
 {
@@ -1601,6 +1763,16 @@ void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
   BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
 }
 
+void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
+                                int frame, int tilesize)
+{
+  Bitmap *src_bitmap;
+  int src_x, src_y;
+
+  getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
+  BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
+}
+
 void DrawMiniGraphic(int x, int y, int graphic)
 {
   DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
@@ -1616,9 +1788,9 @@ void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
   BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
 }
 
-inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
-                                           int graphic, int frame,
-                                           int cut_mode, int mask_mode)
+static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
+                                    int graphic, int frame,
+                                    int cut_mode, int mask_mode)
 {
   Bitmap *src_bitmap;
   int src_x, src_y;
@@ -1626,35 +1798,35 @@ inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
   int width = TILEX, height = TILEY;
   int cx = 0, cy = 0;
 
-  if (dx || dy)                        /* shifted graphic */
+  if (dx || dy)                        // shifted graphic
   {
-    if (x < BX1)               /* object enters playfield from the left */
+    if (x < BX1)               // object enters playfield from the left
     {
       x = BX1;
       width = dx;
       cx = TILEX - dx;
       dx = 0;
     }
-    else if (x > BX2)          /* object enters playfield from the right */
+    else if (x > BX2)          // object enters playfield from the right
     {
       x = BX2;
       width = -dx;
       dx = TILEX + dx;
     }
-    else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
+    else if (x == BX1 && dx < 0) // object leaves playfield to the left
     {
       width += dx;
       cx = -dx;
       dx = 0;
     }
-    else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
+    else if (x == BX2 && dx > 0) // object leaves playfield to the right
       width -= dx;
-    else if (dx)               /* general horizontal movement */
+    else if (dx)               // general horizontal movement
       MarkTileDirty(x + SIGN(dx), y);
 
-    if (y < BY1)               /* object enters playfield from the top */
+    if (y < BY1)               // object enters playfield from the top
     {
-      if (cut_mode == CUT_BELOW) /* object completely above top border */
+      if (cut_mode == CUT_BELOW) // object completely above top border
        return;
 
       y = BY1;
@@ -1662,13 +1834,13 @@ inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
       cy = TILEY - dy;
       dy = 0;
     }
-    else if (y > BY2)          /* object enters playfield from the bottom */
+    else if (y > BY2)          // object enters playfield from the bottom
     {
       y = BY2;
       height = -dy;
       dy = TILEY + dy;
     }
-    else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
+    else if (y == BY1 && dy < 0) // object leaves playfield to the top
     {
       height += dy;
       cy = -dy;
@@ -1676,17 +1848,17 @@ inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
     }
     else if (dy > 0 && cut_mode == CUT_ABOVE)
     {
-      if (y == BY2)            /* object completely above bottom border */
+      if (y == BY2)            // object completely above bottom border
        return;
 
       height = dy;
       cy = TILEY - dy;
       dy = TILEY;
       MarkTileDirty(x, y + 1);
-    }                          /* object leaves playfield to the bottom */
+    }                          // object leaves playfield to the bottom
     else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
       height -= dy;
-    else if (dy)               /* general vertical movement */
+    else if (dy)               // general vertical movement
       MarkTileDirty(x, y + SIGN(dy));
   }
 
@@ -1727,9 +1899,9 @@ inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
   }
 }
 
-inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
-                                           int graphic, int frame,
-                                           int cut_mode, int mask_mode)
+static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
+                                    int graphic, int frame,
+                                    int cut_mode, int mask_mode)
 {
   Bitmap *src_bitmap;
   int src_x, src_y;
@@ -1740,22 +1912,22 @@ inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
   int x2 = x + SIGN(dx);
   int y2 = y + SIGN(dy);
 
-  /* movement with two-tile animations must be sync'ed with movement position,
-     not with current GfxFrame (which can be higher when using slow movement) */
+  // movement with two-tile animations must be sync'ed with movement position,
+  // not with current GfxFrame (which can be higher when using slow movement)
   int anim_pos = (dx ? ABS(dx) : ABS(dy));
   int anim_frames = graphic_info[graphic].anim_frames;
 
-  /* (we also need anim_delay here for movement animations with less frames) */
+  // (we also need anim_delay here for movement animations with less frames)
   int anim_delay = graphic_info[graphic].anim_delay;
   int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
 
-  boolean draw_start_tile = (cut_mode != CUT_ABOVE);   /* only for falling! */
-  boolean draw_end_tile   = (cut_mode != CUT_BELOW);   /* only for falling! */
+  boolean draw_start_tile = (cut_mode != CUT_ABOVE);   // only for falling!
+  boolean draw_end_tile   = (cut_mode != CUT_BELOW);   // only for falling!
 
-  /* re-calculate animation frame for two-tile movement animation */
+  // re-calculate animation frame for two-tile movement animation
   frame = getGraphicAnimationFrame(graphic, sync_frame);
 
-  /* check if movement start graphic inside screen area and should be drawn */
+  // check if movement start graphic inside screen area and should be drawn
   if (draw_start_tile && IN_SCR_FIELD(x1, y1))
   {
     getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
@@ -1773,7 +1945,7 @@ inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
     MarkTileDirty(x1, y1);
   }
 
-  /* check if movement end graphic inside screen area and should be drawn */
+  // check if movement end graphic inside screen area and should be drawn
   if (draw_end_tile && IN_SCR_FIELD(x2, y2))
   {
     getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
@@ -1803,14 +1975,14 @@ static void DrawGraphicShifted(int x, int y, int dx, int dy,
     return;
   }
 
-  if (graphic_info[graphic].double_movement)   /* EM style movement images */
+  if (graphic_info[graphic].double_movement)   // EM style movement images
     DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
   else
     DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
 }
 
-void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
-                               int frame, int cut_mode)
+static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
+                                      int graphic, int frame, int cut_mode)
 {
   DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
 }
@@ -1829,14 +2001,14 @@ void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
     graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
     frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
 
-    /* do not use double (EM style) movement graphic when not moving */
+    // do not use double (EM style) movement graphic when not moving
     if (graphic_info[graphic].double_movement && !dx && !dy)
     {
       graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
       frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
     }
   }
-  else /* border element */
+  else // border element
   {
     graphic = el2img(element);
     frame = getGraphicAnimationFrame(graphic, -1);
@@ -1903,7 +2075,7 @@ void DrawLevelFieldThruMask(int x, int y)
   DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
 }
 
-/* !!! implementation of quicksand is totally broken !!! */
+// !!! implementation of quicksand is totally broken !!!
 #define IS_CRUMBLED_TILE(x, y, e)                                      \
        (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) ||                     \
                             !IS_MOVING(x, y) ||                        \
@@ -1934,17 +2106,17 @@ static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
     int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
                   BorderElement);
 
-    /* check if neighbour field is of same crumble type */
+    // check if neighbour field is of same crumble type
     boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
                    graphic_info[graphic].class ==
                    graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
 
-    /* return if check prevents inner corner */
+    // return if check prevents inner corner
     if (same == (dxx == dx && dyy == dy))
       return;
   }
 
-  /* if we reach this point, we have an inner corner */
+  // if we reach this point, we have an inner corner
 
   getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
 
@@ -1973,7 +2145,7 @@ static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
 
   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
 
-  /* draw simple, sloppy, non-corner-accurate crumbled border */
+  // draw simple, sloppy, non-corner-accurate crumbled border
 
   width  = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
   height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
@@ -1984,12 +2156,12 @@ static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
             FX + sx * TILEX_VAR + cx,
             FY + sy * TILEY_VAR + cy);
 
-  /* (remaining middle border part must be at least as big as corner part) */
+  // (remaining middle border part must be at least as big as corner part)
   if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
       crumbled_border_size_var >= TILESIZE_VAR / 3)
     return;
 
-  /* correct corners of crumbled border, if needed */
+  // correct corners of crumbled border, if needed
 
   for (i = -1; i <= 1; i += 2)
   {
@@ -1998,12 +2170,12 @@ static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
     int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
                   BorderElement);
 
-    /* check if neighbour field is of same crumble type */
+    // check if neighbour field is of same crumble type
     if (IS_CRUMBLED_TILE(xx, yy, element) &&
        graphic_info[graphic].class ==
        graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
     {
-      /* no crumbled corner, but continued crumbled border */
+      // no crumbled corner, but continued crumbled border
 
       int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
       int c2 = (i == 1 ? crumbled_border_pos_var : 0);
@@ -2054,12 +2226,12 @@ static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
 
   element = TILE_GFX_ELEMENT(x, y);
 
-  if (IS_CRUMBLED_TILE(x, y, element))         /* crumble field itself */
+  if (IS_CRUMBLED_TILE(x, y, element))         // crumble field itself
   {
     if (!IN_SCR_FIELD(sx, sy))
       return;
 
-    /* crumble field borders towards direct neighbour fields */
+    // crumble field borders towards direct neighbour fields
     for (i = 0; i < 4; i++)
     {
       int xx = x + xy[i][0];
@@ -2068,7 +2240,7 @@ static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
       element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
                 BorderElement);
 
-      /* check if neighbour field is of same crumble type */
+      // check if neighbour field is of same crumble type
       if (IS_CRUMBLED_TILE(xx, yy, element) &&
          graphic_info[graphic].class ==
          graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
@@ -2077,7 +2249,7 @@ static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
       DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
     }
 
-    /* crumble inner field corners towards corner neighbour fields */
+    // crumble inner field corners towards corner neighbour fields
     if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
        graphic_info[graphic].anim_frames == 2)
     {
@@ -2092,9 +2264,9 @@ static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
 
     MarkTileDirty(sx, sy);
   }
-  else         /* center field is not crumbled -- crumble neighbour fields */
+  else         // center field is not crumbled -- crumble neighbour fields
   {
-    /* crumble field borders of direct neighbour fields */
+    // crumble field borders of direct neighbour fields
     for (i = 0; i < 4; i++)
     {
       int xx = x + xy[i][0];
@@ -2106,7 +2278,9 @@ static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
          !IN_SCR_FIELD(sxx, syy))
        continue;
 
-      if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
+      // do not crumble fields that are being digged or snapped
+      if (Feld[xx][yy] == EL_EMPTY ||
+         Feld[xx][yy] == EL_ELEMENT_SNAPPING)
        continue;
 
       element = TILE_GFX_ELEMENT(xx, yy);
@@ -2121,7 +2295,7 @@ static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
       MarkTileDirty(sxx, syy);
     }
 
-    /* crumble inner field corners of corner neighbour fields */
+    // crumble inner field corners of corner neighbour fields
     for (i = 0; i < 4; i++)
     {
       int dx = (i & 1 ? +1 : -1);
@@ -2200,7 +2374,7 @@ void DrawLevelFieldCrumbledNeighbours(int x, int y)
   };
   int i;
 
-  /* crumble direct neighbour fields (required for field borders) */
+  // crumble direct neighbour fields (required for field borders)
   for (i = 0; i < 4; i++)
   {
     int xx = x + xy[i][0];
@@ -2217,7 +2391,7 @@ void DrawLevelFieldCrumbledNeighbours(int x, int y)
     DrawLevelField(xx, yy);
   }
 
-  /* crumble corner neighbour fields (required for inner field corners) */
+  // crumble corner neighbour fields (required for inner field corners)
   for (i = 0; i < 4; i++)
   {
     int dx = (i & 1 ? +1 : -1);
@@ -2412,12 +2586,90 @@ void DrawLevelField(int x, int y)
   }
 }
 
+static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
+                               int (*el2img_function)(int), boolean masked,
+                               int element_bits_draw)
+{
+  int element_base = map_mm_wall_element(element);
+  int element_bits = (IS_DF_WALL(element) ?
+                     element - EL_DF_WALL_START :
+                     IS_MM_WALL(element) ?
+                     element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
+  int graphic = el2img_function(element_base);
+  int tilesize_draw = tilesize / 2;
+  Bitmap *src_bitmap;
+  int src_x, src_y;
+  int i;
+
+  getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
+
+  for (i = 0; i < 4; i++)
+  {
+    int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
+    int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
+
+    if (!(element_bits_draw & (1 << i)))
+      continue;
+
+    if (element_bits & (1 << i))
+    {
+      if (masked)
+       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
+                        tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
+      else
+       BlitBitmap(src_bitmap, drawto, src_x, src_y,
+                  tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
+    }
+    else
+    {
+      if (!masked)
+       ClearRectangle(drawto, dst_draw_x, dst_draw_y,
+                      tilesize_draw, tilesize_draw);
+    }
+  }
+}
+
+void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
+                          boolean masked, int element_bits_draw)
+{
+  DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
+                     element, tilesize, el2edimg, masked, element_bits_draw);
+}
+
+static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
+                            int (*el2img_function)(int))
+{
+  DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
+                     0x000f);
+}
+
+static void DrawSizedElementExt(int x, int y, int element, int tilesize,
+                               boolean masked)
+{
+  if (IS_MM_WALL(element))
+  {
+    DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
+                       element, tilesize, el2edimg, masked, 0x000f);
+  }
+  else
+  {
+    int graphic = el2edimg(element);
+
+    if (masked)
+      DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
+    else
+      DrawSizedGraphic(x, y, graphic, 0, tilesize);
+  }
+}
+
 void DrawSizedElement(int x, int y, int element, int tilesize)
 {
-  int graphic;
+  DrawSizedElementExt(x, y, element, tilesize, FALSE);
+}
 
-  graphic = el2edimg(element);
-  DrawSizedGraphic(x, y, graphic, 0, tilesize);
+void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
+{
+  DrawSizedElementExt(x, y, element, tilesize, TRUE);
 }
 
 void DrawMiniElement(int x, int y, int element)
@@ -2453,9 +2705,9 @@ void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
     DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
 }
 
-void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
-                                int x, int y, int xsize, int ysize,
-                                int tile_width, int tile_height)
+static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
+                                       int x, int y, int xsize, int ysize,
+                                       int tile_width, int tile_height)
 {
   Bitmap *src_bitmap;
   int src_x, src_y;
@@ -2492,8 +2744,9 @@ void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
               dst_x, dst_y);
 }
 
-void DrawEnvelopeBackground(int graphic, int startx, int starty,
-                           int x, int y, int xsize, int ysize, int font_nr)
+static void DrawEnvelopeBackground(int graphic, int startx, int starty,
+                                  int x, int y, int xsize, int ysize,
+                                  int font_nr)
 {
   int font_width  = getFontWidth(font_nr);
   int font_height = getFontHeight(font_nr);
@@ -2502,7 +2755,7 @@ void DrawEnvelopeBackground(int graphic, int startx, int starty,
                              font_width, font_height);
 }
 
-void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
+static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
 {
   int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
   Bitmap *src_bitmap = graphic_info[graphic].bitmap;
@@ -2559,6 +2812,8 @@ void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
 
     SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
   }
+
+  ClearAutoRepeatKeyEvents();
 }
 
 void ShowEnvelope(int envelope_nr)
@@ -2575,7 +2830,7 @@ void ShowEnvelope(int envelope_nr)
   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
                        anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
 
-  game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
+  game.envelope_active = TRUE; // needed for RedrawPlayfield() events
 
   PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
 
@@ -2672,7 +2927,7 @@ static void setRequestPosition(int *x, int *y, boolean add_border_size)
   setRequestPositionExt(x, y, request.width, request.height, add_border_size);
 }
 
-void DrawEnvelopeRequest(char *text)
+static void DrawEnvelopeRequest(char *text)
 {
   char *text_final = text;
   char *text_door_style = NULL;
@@ -2740,7 +2995,7 @@ void DrawEnvelopeRequest(char *text)
                                  x, y, x_steps, y_steps,
                                  tile_size, tile_size);
 
-  /* force DOOR font inside door area */
+  // force DOOR font inside door area
   SetFontStatus(GAME_MODE_PSEUDO_DOOR);
 
   DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
@@ -2759,7 +3014,7 @@ void DrawEnvelopeRequest(char *text)
     free(text_door_style);
 }
 
-void AnimateEnvelopeRequest(int anim_mode, int action)
+static void AnimateEnvelopeRequest(int anim_mode, int action)
 {
   int graphic = IMG_BACKGROUND_REQUEST;
   boolean draw_masked = graphic_info[graphic].draw_masked;
@@ -2842,15 +3097,17 @@ void AnimateEnvelopeRequest(int anim_mode, int action)
 
     SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
   }
+
+  ClearAutoRepeatKeyEvents();
 }
 
-void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
+static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
 {
   int graphic = IMG_BACKGROUND_REQUEST;
   int sound_opening = SND_REQUEST_OPENING;
   int sound_closing = SND_REQUEST_CLOSING;
-  int anim_mode_1 = request.anim_mode;                 /* (higher priority) */
-  int anim_mode_2 = graphic_info[graphic].anim_mode;   /* (lower priority) */
+  int anim_mode_1 = request.anim_mode;                 // (higher priority)
+  int anim_mode_2 = graphic_info[graphic].anim_mode;   // (lower priority)
   int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
   int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
                        anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
@@ -2886,7 +3143,7 @@ void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
     DrawEnvelopeRequest(text);
   }
 
-  game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
+  game.envelope_active = TRUE; // needed for RedrawPlayfield() events
 
   if (action == ACTION_OPENING)
   {
@@ -2925,14 +3182,22 @@ void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
     SetDrawtoField(DRAW_TO_FIELDBUFFER);
 }
 
-void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
+static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
 {
-  Bitmap *src_bitmap;
-  int src_x, src_y;
-  int graphic = el2preimg(element);
+  if (IS_MM_WALL(element))
+  {
+    DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
+  }
+  else
+  {
+    Bitmap *src_bitmap;
+    int src_x, src_y;
+    int graphic = el2preimg(element);
 
-  getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
-  BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
+    getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
+    BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
+              dst_x, dst_y);
+  }
 }
 
 void DrawLevel(int draw_background_mask)
@@ -3133,7 +3398,7 @@ static void DrawPreviewLevelExt(boolean restart)
     DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
     DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
 
-    /* initialize delay counters */
+    // initialize delay counters
     DelayReached(&scroll_delay, 0);
     DelayReached(&label_delay, 0);
 
@@ -3157,7 +3422,7 @@ static void DrawPreviewLevelExt(boolean restart)
     return;
   }
 
-  /* scroll preview level, if needed */
+  // scroll preview level, if needed
   if (preview.anim_mode != ANIM_NONE &&
       (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
       DelayReached(&scroll_delay, scroll_delay_value))
@@ -3213,8 +3478,8 @@ static void DrawPreviewLevelExt(boolean restart)
     DrawPreviewLevelPlayfield(from_x, from_y);
   }
 
-  /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
-  /* redraw micro level label, if needed */
+  // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
+  // redraw micro level label, if needed
   if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
       !strEqual(level.author, ANONYMOUS_NAME) &&
       !strEqual(level.author, leveldir_current->name) &&
@@ -3255,19 +3520,196 @@ static void DrawPreviewLevelExt(boolean restart)
   }
 }
 
-void DrawPreviewLevelInitial()
+void DrawPreviewPlayers(void)
+{
+  if (game_status != GAME_MODE_MAIN)
+    return;
+
+  // do not draw preview players if level preview redefined, but players aren't
+  if (preview.redefined && !menu.main.preview_players.redefined)
+    return;
+
+  boolean player_found[MAX_PLAYERS];
+  int num_players = 0;
+  int i, x, y;
+
+  for (i = 0; i < MAX_PLAYERS; i++)
+    player_found[i] = FALSE;
+
+  // check which players can be found in the level (simple approach)
+  for (x = 0; x < lev_fieldx; x++)
+  {
+    for (y = 0; y < lev_fieldy; y++)
+    {
+      int element = level.field[x][y];
+
+      if (ELEM_IS_PLAYER(element))
+      {
+       int player_nr = GET_PLAYER_NR(element);
+
+       player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
+
+       if (!player_found[player_nr])
+         num_players++;
+
+       player_found[player_nr] = TRUE;
+      }
+    }
+  }
+
+  struct TextPosInfo *pos = &menu.main.preview_players;
+  int tile_size = pos->tile_size;
+  int border_size = pos->border_size;
+  int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
+  int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
+  int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
+  int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
+  int max_players_width  = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
+  int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
+  int all_players_width  = (num_players - 1) * player_xoffset + tile_size;
+  int all_players_height = (num_players - 1) * player_yoffset + tile_size;
+  int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width,  pos->align);
+  int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
+  int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width,  pos->align);
+  int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
+
+  // clear area in which the players will be drawn
+  ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
+                            max_players_width, max_players_height);
+
+  if (!network.enabled && !setup.team_mode)
+    return;
+
+  // only draw players if level is suited for team mode
+  if (num_players < 2)
+    return;
+
+  // draw all players that were found in the level
+  for (i = 0; i < MAX_PLAYERS; i++)
+  {
+    if (player_found[i])
+    {
+      int graphic = el2img(EL_PLAYER_1 + i);
+
+      DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
+
+      xpos += player_xoffset;
+      ypos += player_yoffset;
+    }
+  }
+}
+
+void DrawPreviewLevelInitial(void)
 {
   DrawPreviewLevelExt(TRUE);
+  DrawPreviewPlayers();
 }
 
-void DrawPreviewLevelAnimation()
+void DrawPreviewLevelAnimation(void)
 {
   DrawPreviewLevelExt(FALSE);
 }
 
-inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
-                                          int graphic, int sync_frame,
-                                          int mask_mode)
+static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
+                             int border_size, int font_nr)
+{
+  int graphic = el2img(EL_PLAYER_1 + player_nr);
+  int font_height = getFontHeight(font_nr);
+  int player_height = MAX(tile_size, font_height);
+  int xoffset_text = tile_size + border_size;
+  int yoffset_text    = (player_height - font_height) / 2;
+  int yoffset_graphic = (player_height - tile_size) / 2;
+  char *player_name = getNetworkPlayerName(player_nr + 1);
+
+  DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
+                             tile_size);
+  DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
+}
+
+static void DrawNetworkPlayersExt(boolean force)
+{
+  if (game_status != GAME_MODE_MAIN)
+    return;
+
+  if (!network.connected && !force)
+    return;
+
+  // do not draw network players if level preview redefined, but players aren't
+  if (preview.redefined && !menu.main.network_players.redefined)
+    return;
+
+  int num_players = 0;
+  int i;
+
+  for (i = 0; i < MAX_PLAYERS; i++)
+    if (stored_player[i].connected_network)
+      num_players++;
+
+  struct TextPosInfo *pos = &menu.main.network_players;
+  int tile_size = pos->tile_size;
+  int border_size = pos->border_size;
+  int xoffset_text = tile_size + border_size;
+  int font_nr = pos->font;
+  int font_width = getFontWidth(font_nr);
+  int font_height = getFontHeight(font_nr);
+  int player_height = MAX(tile_size, font_height);
+  int player_yoffset = player_height + border_size;
+  int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
+  int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
+  int all_players_height = num_players * player_yoffset - border_size;
+  int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width,  pos->align);
+  int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
+  int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
+
+  ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
+                            max_players_width, max_players_height);
+
+  // first draw local network player ...
+  for (i = 0; i < MAX_PLAYERS; i++)
+  {
+    if (stored_player[i].connected_network &&
+       stored_player[i].connected_locally)
+    {
+      char *player_name = getNetworkPlayerName(i + 1);
+      int player_width = xoffset_text + getTextWidth(player_name, font_nr);
+      int xpos = SX + ALIGNED_XPOS(pos->x, player_width,  pos->align);
+
+      DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
+
+      ypos += player_yoffset;
+    }
+  }
+
+  // ... then draw all other network players
+  for (i = 0; i < MAX_PLAYERS; i++)
+  {
+    if (stored_player[i].connected_network &&
+       !stored_player[i].connected_locally)
+    {
+      char *player_name = getNetworkPlayerName(i + 1);
+      int player_width = xoffset_text + getTextWidth(player_name, font_nr);
+      int xpos = SX + ALIGNED_XPOS(pos->x, player_width,  pos->align);
+
+      DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
+
+      ypos += player_yoffset;
+    }
+  }
+}
+
+void DrawNetworkPlayers(void)
+{
+  DrawNetworkPlayersExt(FALSE);
+}
+
+void ClearNetworkPlayers(void)
+{
+  DrawNetworkPlayersExt(TRUE);
+}
+
+static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
+                                   int graphic, int sync_frame,
+                                   int mask_mode)
 {
   int frame = getGraphicAnimationFrame(graphic, sync_frame);
 
@@ -3288,7 +3730,7 @@ void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
     DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
 }
 
-inline static void DrawGraphicAnimation(int x, int y, int graphic)
+static void DrawGraphicAnimation(int x, int y, int graphic)
 {
   int lx = LEVELX(x), ly = LEVELY(y);
 
@@ -3369,14 +3811,14 @@ static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
 {
   if (player->use_murphy)
   {
-    /* this works only because currently only one player can be "murphy" ... */
+    // this works only because currently only one player can be "murphy" ...
     static int last_horizontal_dir = MV_LEFT;
     int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
 
     if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
       last_horizontal_dir = move_dir;
 
-    if (graphic == IMG_SP_MURPHY)      /* undefined => use special graphic */
+    if (graphic == IMG_SP_MURPHY)      // undefined => use special graphic
     {
       int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
 
@@ -3402,87 +3844,122 @@ static boolean equalGraphics(int graphic1, int graphic2)
          g1->anim_mode   == g2->anim_mode);
 }
 
-void DrawAllPlayers()
+#define DRAW_PLAYER_OVER_PUSHED_ELEMENT        1
+
+enum
 {
-  int i;
+  DRAW_PLAYER_STAGE_INIT = 0,
+  DRAW_PLAYER_STAGE_LAST_FIELD,
+  DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
+#if DRAW_PLAYER_OVER_PUSHED_ELEMENT
+  DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
+  DRAW_PLAYER_STAGE_PLAYER,
+#else
+  DRAW_PLAYER_STAGE_PLAYER,
+  DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
+#endif
+  DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
+  DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
 
-  for (i = 0; i < MAX_PLAYERS; i++)
-    if (stored_player[i].active)
-      DrawPlayer(&stored_player[i]);
-}
+  NUM_DRAW_PLAYER_STAGES
+};
 
-void DrawPlayerField(int x, int y)
+static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
 {
-  if (!IS_PLAYER(x, y))
+  static int static_last_player_graphic[MAX_PLAYERS];
+  static int static_last_player_frame[MAX_PLAYERS];
+  static boolean static_player_is_opaque[MAX_PLAYERS];
+  static boolean draw_player[MAX_PLAYERS];
+  int pnr = player->index_nr;
+
+  if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
+  {
+    static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
+    static_last_player_frame[pnr] = player->Frame;
+    static_player_is_opaque[pnr] = FALSE;
+
+    draw_player[pnr] = TRUE;
+  }
+
+  if (!draw_player[pnr])
     return;
 
-  DrawPlayer(PLAYERINFO(x, y));
-}
+#if DEBUG
+  if (!IN_LEV_FIELD(player->jx, player->jy))
+  {
+    printf("DrawPlayerField(): x = %d, y = %d\n", player->jx, player->jy);
+    printf("DrawPlayerField(): This should never happen!\n");
 
-#define DRAW_PLAYER_OVER_PUSHED_ELEMENT        1
+    draw_player[pnr] = FALSE;
+
+    return;
+  }
+#endif
+
+  int last_player_graphic  = static_last_player_graphic[pnr];
+  int last_player_frame    = static_last_player_frame[pnr];
+  boolean player_is_opaque = static_player_is_opaque[pnr];
 
-void DrawPlayer(struct PlayerInfo *player)
-{
   int jx = player->jx;
   int jy = player->jy;
-  int move_dir = player->MovDir;
+  int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
   int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
   int dy = (move_dir == MV_UP   ? -1 : move_dir == MV_DOWN  ? +1 : 0);
   int last_jx = (player->is_moving ? jx - dx : jx);
   int last_jy = (player->is_moving ? jy - dy : jy);
   int next_jx = jx + dx;
   int next_jy = jy + dy;
-  boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
-  boolean player_is_opaque = FALSE;
-  int sx = SCREENX(jx), sy = SCREENY(jy);
-  int sxx = 0, syy = 0;
-  int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
-  int graphic;
-  int action = ACTION_DEFAULT;
-  int last_player_graphic = getPlayerGraphic(player, move_dir);
-  int last_player_frame = player->Frame;
-  int frame = 0;
+  boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
+  int sx = SCREENX(jx);
+  int sy = SCREENY(jy);
+  int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
+  int syy = (move_dir == MV_UP   || move_dir == MV_DOWN  ? player->GfxPos : 0);
+  int element = Feld[jx][jy];
+  int last_element = Feld[last_jx][last_jy];
+  int action = (player->is_pushing    ? ACTION_PUSHING         :
+               player->is_digging    ? ACTION_DIGGING         :
+               player->is_collecting ? ACTION_COLLECTING      :
+               player->is_moving     ? ACTION_MOVING          :
+               player->is_snapping   ? ACTION_SNAPPING        :
+               player->is_dropping   ? ACTION_DROPPING        :
+               player->is_waiting    ? player->action_waiting :
+               ACTION_DEFAULT);
+
+  if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
+  {
+    // ------------------------------------------------------------------------
+    // initialize drawing the player
+    // ------------------------------------------------------------------------
 
-  /* GfxElement[][] is set to the element the player is digging or collecting;
-     remove also for off-screen player if the player is not moving anymore */
-  if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
-    GfxElement[jx][jy] = EL_UNDEFINED;
+    draw_player[pnr] = FALSE;
 
-  if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
-    return;
+    // GfxElement[][] is set to the element the player is digging or collecting;
+    // remove also for off-screen player if the player is not moving anymore
+    if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
+      GfxElement[jx][jy] = EL_UNDEFINED;
 
-#if DEBUG
-  if (!IN_LEV_FIELD(jx, jy))
-  {
-    printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
-    printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
-    printf("DrawPlayerField(): This should never happen!\n");
-    return;
-  }
-#endif
+    if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
+      return;
 
-  if (element == EL_EXPLOSION)
-    return;
+    if (element == EL_EXPLOSION)
+      return;
 
-  action = (player->is_pushing    ? ACTION_PUSHING         :
-           player->is_digging    ? ACTION_DIGGING         :
-           player->is_collecting ? ACTION_COLLECTING      :
-           player->is_moving     ? ACTION_MOVING          :
-           player->is_snapping   ? ACTION_SNAPPING        :
-           player->is_dropping   ? ACTION_DROPPING        :
-           player->is_waiting    ? player->action_waiting : ACTION_DEFAULT);
+    InitPlayerGfxAnimation(player, action, move_dir);
 
-  if (player->is_waiting)
-    move_dir = player->dir_waiting;
+    draw_player[pnr] = TRUE;
+  }
+  else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
+  {
+    // ------------------------------------------------------------------------
+    // draw things in the field the player is leaving, if needed
+    // ------------------------------------------------------------------------
 
-  InitPlayerGfxAnimation(player, action, move_dir);
+    if (!IN_SCR_FIELD(sx, sy))
+      draw_player[pnr] = FALSE;
 
-  /* ----------------------------------------------------------------------- */
-  /* draw things in the field the player is leaving, if needed               */
-  /* ----------------------------------------------------------------------- */
+    if (!player->is_moving)
+      return;
 
-  if (player->is_moving)
-  {
     if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
     {
       DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
@@ -3498,35 +3975,32 @@ void DrawPlayer(struct PlayerInfo *player)
             last_element == EL_EM_DYNAMITE_ACTIVE ||
             last_element == EL_SP_DISK_RED_ACTIVE)
       DrawDynamite(last_jx, last_jy);
-#if 0
-    /* !!! this is not enough to prevent flickering of players which are
-       moving next to each others without a free tile between them -- this
-       can only be solved by drawing all players layer by layer (first the
-       background, then the foreground etc.) !!! => TODO */
-    else if (!IS_PLAYER(last_jx, last_jy))
-      DrawLevelField(last_jx, last_jy);
-#else
     else
       DrawLevelField(last_jx, last_jy);
-#endif
 
-    if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
-      DrawLevelElement(next_jx, next_jy, EL_EMPTY);
-  }
+    if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
+      DrawLevelElement(next_jx, next_jy, EL_EMPTY);
+  }
+  else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER)
+  {
+    // ------------------------------------------------------------------------
+    // draw things behind the player, if needed
+    // ------------------------------------------------------------------------
+
+    if (Back[jx][jy])
+    {
+      DrawLevelElement(jx, jy, Back[jx][jy]);
 
-  if (!IN_SCR_FIELD(sx, sy))
-    return;
+      return;
+    }
+
+    if (IS_ACTIVE_BOMB(element))
+    {
+      DrawLevelElement(jx, jy, EL_EMPTY);
 
-  /* ----------------------------------------------------------------------- */
-  /* draw things behind the player, if needed                                */
-  /* ----------------------------------------------------------------------- */
+      return;
+    }
 
-  if (Back[jx][jy])
-    DrawLevelElement(jx, jy, Back[jx][jy]);
-  else if (IS_ACTIVE_BOMB(element))
-    DrawLevelElement(jx, jy, EL_EMPTY);
-  else
-  {
     if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
     {
       int old_element = GfxElement[jx][jy];
@@ -3539,14 +4013,14 @@ void DrawPlayer(struct PlayerInfo *player)
        DrawGraphic(sx, sy, old_graphic, frame);
 
       if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
-       player_is_opaque = TRUE;
+       static_player_is_opaque[pnr] = TRUE;
     }
     else
     {
       GfxElement[jx][jy] = EL_UNDEFINED;
 
-      /* make sure that pushed elements are drawn with correct frame rate */
-      graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
+      // make sure that pushed elements are drawn with correct frame rate
+      int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
 
       if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
        GfxFrame[jx][jy] = player->StepFrame;
@@ -3554,84 +4028,31 @@ void DrawPlayer(struct PlayerInfo *player)
       DrawLevelField(jx, jy);
     }
   }
-
-#if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
-  /* ----------------------------------------------------------------------- */
-  /* draw player himself                                                     */
-  /* ----------------------------------------------------------------------- */
-
-  graphic = getPlayerGraphic(player, move_dir);
-
-  /* in the case of changed player action or direction, prevent the current
-     animation frame from being restarted for identical animations */
-  if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
-    player->Frame = last_player_frame;
-
-  frame = getGraphicAnimationFrame(graphic, player->Frame);
-
-  if (player->GfxPos)
-  {
-    if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
-      sxx = player->GfxPos;
-    else
-      syy = player->GfxPos;
-  }
-
-  if (player_is_opaque)
-    DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
-  else
-    DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
-
-  if (SHIELD_ON(player))
-  {
-    int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
-                  IMG_SHIELD_NORMAL_ACTIVE);
-    int frame = getGraphicAnimationFrame(graphic, -1);
-
-    DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
-  }
-#endif
-
-#if DRAW_PLAYER_OVER_PUSHED_ELEMENT
-  if (player->GfxPos)
+  else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_PUSHED)
   {
-    if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
-      sxx = player->GfxPos;
-    else
-      syy = player->GfxPos;
-  }
-#endif
+    // ------------------------------------------------------------------------
+    // draw things the player is pushing, if needed
+    // ------------------------------------------------------------------------
 
-  /* ----------------------------------------------------------------------- */
-  /* draw things the player is pushing, if needed                            */
-  /* ----------------------------------------------------------------------- */
+    if (!player->is_pushing || !player->is_moving)
+      return;
 
-  if (player->is_pushing && player->is_moving)
-  {
-    int px = SCREENX(jx), py = SCREENY(jy);
-    int pxx = (TILEX - ABS(sxx)) * dx;
-    int pyy = (TILEY - ABS(syy)) * dy;
     int gfx_frame = GfxFrame[jx][jy];
 
-    int graphic;
-    int sync_frame;
-    int frame;
-
-    if (!IS_MOVING(jx, jy))            /* push movement already finished */
+    if (!IS_MOVING(jx, jy))            // push movement already finished
     {
       element = Feld[next_jx][next_jy];
       gfx_frame = GfxFrame[next_jx][next_jy];
     }
 
-    graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
-
-    sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
-    frame = getGraphicAnimationFrame(graphic, sync_frame);
+    int graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
+    int sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
+    int frame = getGraphicAnimationFrame(graphic, sync_frame);
 
-    /* draw background element under pushed element (like the Sokoban field) */
+    // draw background element under pushed element (like the Sokoban field)
     if (game.use_masked_pushing && IS_MOVING(jx, jy))
     {
-      /* this allows transparent pushing animation over non-black background */
+      // this allows transparent pushing animation over non-black background
 
       if (Back[jx][jy])
        DrawLevelElement(jx, jy, Back[jx][jy]);
@@ -3646,121 +4067,147 @@ void DrawPlayer(struct PlayerInfo *player)
     else if (Back[next_jx][next_jy])
       DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
 
+    int px = SCREENX(jx), py = SCREENY(jy);
+    int pxx = (TILEX - ABS(sxx)) * dx;
+    int pyy = (TILEY - ABS(syy)) * dy;
+
 #if 1
-    /* do not draw (EM style) pushing animation when pushing is finished */
-    /* (two-tile animations usually do not contain start and end frame) */
+    // do not draw (EM style) pushing animation when pushing is finished
+    // (two-tile animations usually do not contain start and end frame)
     if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
       DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
     else
       DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
 #else
-    /* masked drawing is needed for EMC style (double) movement graphics */
-    /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
+    // masked drawing is needed for EMC style (double) movement graphics
+    // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
     DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
 #endif
   }
+  else if (drawing_stage == DRAW_PLAYER_STAGE_PLAYER)
+  {
+    // ------------------------------------------------------------------------
+    // draw player himself
+    // ------------------------------------------------------------------------
 
-#if DRAW_PLAYER_OVER_PUSHED_ELEMENT
-  /* ----------------------------------------------------------------------- */
-  /* draw player himself                                                     */
-  /* ----------------------------------------------------------------------- */
-
-  graphic = getPlayerGraphic(player, move_dir);
+    int graphic = getPlayerGraphic(player, move_dir);
 
-  /* in the case of changed player action or direction, prevent the current
-     animation frame from being restarted for identical animations */
-  if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
-    player->Frame = last_player_frame;
+    // in the case of changed player action or direction, prevent the current
+    // animation frame from being restarted for identical animations
+    if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
+      player->Frame = last_player_frame;
 
-  frame = getGraphicAnimationFrame(graphic, player->Frame);
+    int frame = getGraphicAnimationFrame(graphic, player->Frame);
 
-  if (player->GfxPos)
-  {
-    if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
-      sxx = player->GfxPos;
+    if (player_is_opaque)
+      DrawGraphicShifted(sx,sy, sxx,syy, graphic, frame, NO_CUTTING,NO_MASKING);
     else
-      syy = player->GfxPos;
-  }
+      DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
 
-  if (player_is_opaque)
-    DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
-  else
-    DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
+    if (SHIELD_ON(player))
+    {
+      graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
+                IMG_SHIELD_NORMAL_ACTIVE);
+      frame = getGraphicAnimationFrame(graphic, -1);
 
-  if (SHIELD_ON(player))
+      DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
+    }
+  }
+  else if (drawing_stage == DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER)
   {
-    int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
-                  IMG_SHIELD_NORMAL_ACTIVE);
-    int frame = getGraphicAnimationFrame(graphic, -1);
+    // ------------------------------------------------------------------------
+    // draw things in front of player (active dynamite or dynabombs)
+    // ------------------------------------------------------------------------
 
-    DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
-  }
-#endif
+    if (IS_ACTIVE_BOMB(element))
+    {
+      int graphic = el2img(element);
+      int frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
 
-  /* ----------------------------------------------------------------------- */
-  /* draw things in front of player (active dynamite or dynabombs)           */
-  /* ----------------------------------------------------------------------- */
+      if (game.emulation == EMU_SUPAPLEX)
+       DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
+      else
+       DrawGraphicThruMask(sx, sy, graphic, frame);
+    }
 
-  if (IS_ACTIVE_BOMB(element))
+    if (player_is_moving && last_element == EL_EXPLOSION)
+    {
+      int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
+                    GfxElement[last_jx][last_jy] :  EL_EMPTY);
+      int graphic = el_act2img(element, ACTION_EXPLODING);
+      int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
+      int phase = ExplodePhase[last_jx][last_jy] - 1;
+      int frame = getGraphicAnimationFrame(graphic, phase - delay);
+
+      if (phase >= delay)
+       DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
+    }
+  }
+  else if (drawing_stage == DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER)
   {
-    graphic = el2img(element);
-    frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
+    // ------------------------------------------------------------------------
+    // draw elements the player is just walking/passing through/under
+    // ------------------------------------------------------------------------
 
-    if (game.emulation == EMU_SUPAPLEX)
-      DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
-    else
-      DrawGraphicThruMask(sx, sy, graphic, frame);
-  }
+    if (player_is_moving)
+    {
+      // handle the field the player is leaving ...
+      if (IS_ACCESSIBLE_INSIDE(last_element))
+       DrawLevelField(last_jx, last_jy);
+      else if (IS_ACCESSIBLE_UNDER(last_element))
+       DrawLevelFieldThruMask(last_jx, last_jy);
+    }
 
-  if (player_is_moving && last_element == EL_EXPLOSION)
-  {
-    int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
-                  GfxElement[last_jx][last_jy] :  EL_EMPTY);
-    int graphic = el_act2img(element, ACTION_EXPLODING);
-    int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
-    int phase = ExplodePhase[last_jx][last_jy] - 1;
-    int frame = getGraphicAnimationFrame(graphic, phase - delay);
+    // do not redraw accessible elements if the player is just pushing them
+    if (!player_is_moving || !player->is_pushing)
+    {
+      // ... and the field the player is entering
+      if (IS_ACCESSIBLE_INSIDE(element))
+       DrawLevelField(jx, jy);
+      else if (IS_ACCESSIBLE_UNDER(element))
+       DrawLevelFieldThruMask(jx, jy);
+    }
 
-    if (phase >= delay)
-      DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
+    MarkTileDirty(sx, sy);
   }
+}
 
-  /* ----------------------------------------------------------------------- */
-  /* draw elements the player is just walking/passing through/under          */
-  /* ----------------------------------------------------------------------- */
+void DrawPlayer(struct PlayerInfo *player)
+{
+  int i;
 
-  if (player_is_moving)
-  {
-    /* handle the field the player is leaving ... */
-    if (IS_ACCESSIBLE_INSIDE(last_element))
-      DrawLevelField(last_jx, last_jy);
-    else if (IS_ACCESSIBLE_UNDER(last_element))
-      DrawLevelFieldThruMask(last_jx, last_jy);
-  }
+  for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
+    DrawPlayerExt(player, i);
+}
 
-  /* do not redraw accessible elements if the player is just pushing them */
-  if (!player_is_moving || !player->is_pushing)
-  {
-    /* ... and the field the player is entering */
-    if (IS_ACCESSIBLE_INSIDE(element))
-      DrawLevelField(jx, jy);
-    else if (IS_ACCESSIBLE_UNDER(element))
-      DrawLevelFieldThruMask(jx, jy);
-  }
+void DrawAllPlayers(void)
+{
+  int i, j;
+
+  for (i = 0; i < NUM_DRAW_PLAYER_STAGES; i++)
+    for (j = 0; j < MAX_PLAYERS; j++)
+      if (stored_player[j].active)
+       DrawPlayerExt(&stored_player[j], i);
+}
+
+void DrawPlayerField(int x, int y)
+{
+  if (!IS_PLAYER(x, y))
+    return;
 
-  MarkTileDirty(sx, sy);
+  DrawPlayer(PLAYERINFO(x, y));
 }
 
-/* ------------------------------------------------------------------------- */
+// ----------------------------------------------------------------------------
 
-void WaitForEventToContinue()
+void WaitForEventToContinue(void)
 {
   boolean still_wait = TRUE;
 
   if (program.headless)
     return;
 
-  /* simulate releasing mouse button over last gadget, if still pressed */
+  // simulate releasing mouse button over last gadget, if still pressed
   if (button_status)
     HandleGadgets(-1, -1, 0);
 
@@ -3776,11 +4223,9 @@ void WaitForEventToContinue()
     {
       switch (event.type)
       {
-       case EVENT_BUTTONPRESS:
+       case EVENT_BUTTONRELEASE:
        case EVENT_KEYPRESS:
-#if defined(TARGET_SDL2)
         case SDL_CONTROLLERBUTTONDOWN:
-#endif
         case SDL_JOYBUTTONDOWN:
          still_wait = FALSE;
          break;
@@ -3809,13 +4254,19 @@ void WaitForEventToContinue()
 
 static int RequestHandleEvents(unsigned int req_state)
 {
-  boolean level_solved = (game_status == GAME_MODE_PLAYING &&
-                         local_player->LevelSolved_GameEnd);
+  boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
+                            checkGameEnded());
   int width  = request.width;
   int height = request.height;
   int sx, sy;
   int result;
 
+  // when showing request dialog after game ended, deactivate game panel
+  if (game_just_ended)
+    game.panel.active = FALSE;
+
+  game.request_active = TRUE;
+
   setRequestPosition(&sx, &sy, FALSE);
 
   button_status = MB_RELEASED;
@@ -3825,9 +4276,11 @@ static int RequestHandleEvents(unsigned int req_state)
 
   while (result < 0)
   {
-    if (level_solved)
+    if (game_just_ended)
     {
-      SetDrawtoField(DRAW_TO_FIELDBUFFER);
+      // the MM game engine does not use a special (scrollable) field buffer
+      if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
+       SetDrawtoField(DRAW_TO_FIELDBUFFER);
 
       HandleGameActions();
 
@@ -3835,7 +4288,7 @@ static int RequestHandleEvents(unsigned int req_state)
 
       if (global.use_envelope_request)
       {
-       /* copy current state of request area to middle of playfield area */
+       // copy current state of request area to middle of playfield area
        BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
       }
     }
@@ -3874,7 +4327,7 @@ static int RequestHandleEvents(unsigned int req_state)
                button_status = MB_RELEASED;
            }
 
-           /* this sets 'request_gadget_id' */
+           // this sets 'request_gadget_id'
            HandleGadgets(mx, my, button_status);
 
            switch (request_gadget_id)
@@ -3909,7 +4362,6 @@ static int RequestHandleEvents(unsigned int req_state)
            break;
          }
 
-#if defined(TARGET_SDL2)
          case SDL_WINDOWEVENT:
            HandleWindowEvent((WindowEvent *) &event);
            break;
@@ -3920,7 +4372,6 @@ static int RequestHandleEvents(unsigned int req_state)
          case SDL_APP_DIDENTERFOREGROUND:
            HandlePauseResumeEvent((PauseResumeEvent *) &event);
            break;
-#endif
 
          case EVENT_KEYPRESS:
          {
@@ -3934,33 +4385,68 @@ static int RequestHandleEvents(unsigned int req_state)
                break;
 
              case KSYM_Return:
-#if defined(TARGET_SDL2)
+             case KSYM_y:
+             case KSYM_Y:
              case KSYM_Select:
              case KSYM_Menu:
 #if defined(KSYM_Rewind)
-             case KSYM_Rewind:         /* for Amazon Fire TV remote */
-#endif
+             case KSYM_Rewind:         // for Amazon Fire TV remote
 #endif
                result = 1;
                break;
 
              case KSYM_Escape:
-#if defined(TARGET_SDL2)
+             case KSYM_n:
+             case KSYM_N:
              case KSYM_Back:
 #if defined(KSYM_FastForward)
-             case KSYM_FastForward:    /* for Amazon Fire TV remote */
-#endif
+             case KSYM_FastForward:    // for Amazon Fire TV remote
 #endif
                result = 0;
                break;
 
              default:
-               HandleKeysDebug(key);
+               HandleKeysDebug(key, KEY_PRESSED);
                break;
            }
 
            if (req_state & REQ_PLAYER)
-             result = 0;
+           {
+             int old_player_nr = setup.network_player_nr;
+
+             if (result != -1)
+               result = old_player_nr + 1;
+
+             switch (key)
+             {
+               case KSYM_space:
+                 result = old_player_nr + 1;
+                 break;
+
+               case KSYM_Up:
+               case KSYM_1:
+                 result = 1;
+                 break;
+
+               case KSYM_Right:
+               case KSYM_2:
+                 result = 2;
+                 break;
+
+               case KSYM_Down:
+               case KSYM_3:
+                 result = 3;
+                 break;
+
+               case KSYM_Left:
+               case KSYM_4:
+                 result = 4;
+                 break;
+
+               default:
+                 break;
+             }
+           }
 
            break;
          }
@@ -3969,26 +4455,58 @@ static int RequestHandleEvents(unsigned int req_state)
            ClearPlayerAction();
            break;
 
-#if defined(TARGET_SDL2)
          case SDL_CONTROLLERBUTTONDOWN:
            switch (event.cbutton.button)
            {
              case SDL_CONTROLLER_BUTTON_A:
              case SDL_CONTROLLER_BUTTON_X:
              case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
+             case SDL_CONTROLLER_BUTTON_LEFTSTICK:
                result = 1;
                break;
 
              case SDL_CONTROLLER_BUTTON_B:
              case SDL_CONTROLLER_BUTTON_Y:
              case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
+             case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
              case SDL_CONTROLLER_BUTTON_BACK:
                result = 0;
                break;
            }
 
            if (req_state & REQ_PLAYER)
-             result = 0;
+           {
+             int old_player_nr = setup.network_player_nr;
+
+             if (result != -1)
+               result = old_player_nr + 1;
+
+             switch (event.cbutton.button)
+             {
+               case SDL_CONTROLLER_BUTTON_DPAD_UP:
+               case SDL_CONTROLLER_BUTTON_Y:
+                 result = 1;
+                 break;
+
+               case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
+               case SDL_CONTROLLER_BUTTON_B:
+                 result = 2;
+                 break;
+
+               case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
+               case SDL_CONTROLLER_BUTTON_A:
+                 result = 3;
+                 break;
+
+               case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
+               case SDL_CONTROLLER_BUTTON_X:
+                 result = 4;
+                 break;
+
+               default:
+                 break;
+             }
+           }
 
            break;
 
@@ -3996,7 +4514,6 @@ static int RequestHandleEvents(unsigned int req_state)
            HandleJoystickEvent(&event);
            ClearPlayerAction();
            break;
-#endif
 
          default:
            HandleOtherEvents(&event);
@@ -4013,12 +4530,28 @@ static int RequestHandleEvents(unsigned int req_state)
       else if (joy & JOY_BUTTON_2)
        result = 0;
     }
+    else if (AnyJoystick())
+    {
+      int joy = AnyJoystick();
+
+      if (req_state & REQ_PLAYER)
+      {
+       if (joy & JOY_UP)
+         result = 1;
+       else if (joy & JOY_RIGHT)
+         result = 2;
+       else if (joy & JOY_DOWN)
+         result = 3;
+       else if (joy & JOY_LEFT)
+         result = 4;
+      }
+    }
 
-    if (level_solved)
+    if (game_just_ended)
     {
       if (global.use_envelope_request)
       {
-       /* copy back current state of pressed buttons inside request area */
+       // copy back current state of pressed buttons inside request area
        BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
       }
     }
@@ -4026,6 +4559,8 @@ static int RequestHandleEvents(unsigned int req_state)
     BackToFront();
   }
 
+  game.request_active = FALSE;
+
   return result;
 }
 
@@ -4038,7 +4573,7 @@ static boolean RequestDoor(char *text, unsigned int req_state)
   int result;
   int ty;
 
-  if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
+  if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
   {
     max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
     font_nr = FONT_TEXT_1;
@@ -4047,36 +4582,35 @@ static boolean RequestDoor(char *text, unsigned int req_state)
   if (game_status == GAME_MODE_PLAYING)
     BlitScreenToBitmap(backbuffer);
 
-  /* disable deactivated drawing when quick-loading level tape recording */
+  // disable deactivated drawing when quick-loading level tape recording
   if (tape.playing && tape.deactivate_display)
     TapeDeactivateDisplayOff(TRUE);
 
   SetMouseCursor(CURSOR_DEFAULT);
 
-#if defined(NETWORK_AVALIABLE)
-  /* pause network game while waiting for request to answer */
-  if (options.network &&
+  // pause network game while waiting for request to answer
+  if (network.enabled &&
       game_status == GAME_MODE_PLAYING &&
+      !game.all_players_gone &&
       req_state & REQUEST_WAIT_FOR_INPUT)
     SendToServer_PausePlaying();
-#endif
 
   old_door_state = GetDoorState();
 
-  /* simulate releasing mouse button over last gadget, if still pressed */
+  // simulate releasing mouse button over last gadget, if still pressed
   if (button_status)
     HandleGadgets(-1, -1, 0);
 
   UnmapAllGadgets();
 
-  /* draw released gadget before proceeding */
+  // draw released gadget before proceeding
   // BackToFront();
 
   if (old_door_state & DOOR_OPEN_1)
   {
     CloseDoor(DOOR_CLOSE_1);
 
-    /* save old door content */
+    // save old door content
     BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
               0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
   }
@@ -4084,13 +4618,13 @@ static boolean RequestDoor(char *text, unsigned int req_state)
   SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
 
-  /* clear door drawing field */
+  // clear door drawing field
   DrawBackground(DX, DY, DXSIZE, DYSIZE);
 
-  /* force DOOR font inside door area */
+  // force DOOR font inside door area
   SetFontStatus(GAME_MODE_PSEUDO_DOOR);
 
-  /* write text for request */
+  // write text for request
   for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
   {
     char text_line[max_request_line_len + 1];
@@ -4147,7 +4681,7 @@ static boolean RequestDoor(char *text, unsigned int req_state)
     MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
   }
 
-  /* copy request gadgets to door backbuffer */
+  // copy request gadgets to door backbuffer
   BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
 
   OpenDoor(DOOR_OPEN_1);
@@ -4195,15 +4729,14 @@ static boolean RequestDoor(char *text, unsigned int req_state)
     SetDrawBackgroundMask(REDRAW_FIELD);
   }
 
-#if defined(NETWORK_AVALIABLE)
-  /* continue network game after request */
-  if (options.network &&
+  // continue network game after request
+  if (network.enabled &&
       game_status == GAME_MODE_PLAYING &&
+      !game.all_players_gone &&
       req_state & REQUEST_WAIT_FOR_INPUT)
     SendToServer_ContinuePlaying();
-#endif
 
-  /* restore deactivated drawing when quick-loading level tape recording */
+  // restore deactivated drawing when quick-loading level tape recording
   if (tape.playing && tape.deactivate_display)
     TapeDeactivateDisplayOn();
 
@@ -4217,21 +4750,20 @@ static boolean RequestEnvelope(char *text, unsigned int req_state)
   if (game_status == GAME_MODE_PLAYING)
     BlitScreenToBitmap(backbuffer);
 
-  /* disable deactivated drawing when quick-loading level tape recording */
+  // disable deactivated drawing when quick-loading level tape recording
   if (tape.playing && tape.deactivate_display)
     TapeDeactivateDisplayOff(TRUE);
 
   SetMouseCursor(CURSOR_DEFAULT);
 
-#if defined(NETWORK_AVALIABLE)
-  /* pause network game while waiting for request to answer */
-  if (options.network &&
+  // pause network game while waiting for request to answer
+  if (network.enabled &&
       game_status == GAME_MODE_PLAYING &&
+      !game.all_players_gone &&
       req_state & REQUEST_WAIT_FOR_INPUT)
     SendToServer_PausePlaying();
-#endif
 
-  /* simulate releasing mouse button over last gadget, if still pressed */
+  // simulate releasing mouse button over last gadget, if still pressed
   if (button_status)
     HandleGadgets(-1, -1, 0);
 
@@ -4241,7 +4773,7 @@ static boolean RequestEnvelope(char *text, unsigned int req_state)
   // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
   // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
 
-  /* clear door drawing field */
+  // clear door drawing field
   // DrawBackground(DX, DY, DXSIZE, DYSIZE);
 
   ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
@@ -4282,15 +4814,14 @@ static boolean RequestEnvelope(char *text, unsigned int req_state)
     SetDrawBackgroundMask(REDRAW_FIELD);
   }
 
-#if defined(NETWORK_AVALIABLE)
-  /* continue network game after request */
-  if (options.network &&
+  // continue network game after request
+  if (network.enabled &&
       game_status == GAME_MODE_PLAYING &&
+      !game.all_players_gone &&
       req_state & REQUEST_WAIT_FOR_INPUT)
     SendToServer_ContinuePlaying();
-#endif
 
-  /* restore deactivated drawing when quick-loading level tape recording */
+  // restore deactivated drawing when quick-loading level tape recording
   if (tape.playing && tape.deactivate_display)
     TapeDeactivateDisplayOn();
 
@@ -4299,17 +4830,17 @@ static boolean RequestEnvelope(char *text, unsigned int req_state)
 
 boolean Request(char *text, unsigned int req_state)
 {
-  boolean overlay_active = GetOverlayActive();
+  boolean overlay_enabled = GetOverlayEnabled();
   boolean result;
 
-  SetOverlayActive(FALSE);
+  SetOverlayEnabled(FALSE);
 
   if (global.use_envelope_request)
     result = RequestEnvelope(text, req_state);
   else
     result = RequestDoor(text, req_state);
 
-  SetOverlayActive(overlay_active);
+  SetOverlayEnabled(overlay_enabled);
 
   return result;
 }
@@ -4328,7 +4859,7 @@ static int compareDoorPartOrderInfo(const void *object1, const void *object2)
   return compare_result;
 }
 
-void InitGraphicCompatibilityInfo_Doors()
+void InitGraphicCompatibilityInfo_Doors(void)
 {
   struct
   {
@@ -4362,7 +4893,7 @@ void InitGraphicCompatibilityInfo_Doors()
     struct Rect *door_rect = &door_rect_list[door_index];
     boolean door_gfx_redefined = FALSE;
 
-    /* check if any door part graphic definitions have been redefined */
+    // check if any door part graphic definitions have been redefined
 
     for (j = 0; door_part_controls[j].door_token != -1; j++)
     {
@@ -4373,7 +4904,7 @@ void InitGraphicCompatibilityInfo_Doors()
        door_gfx_redefined = TRUE;
     }
 
-    /* check for old-style door graphic/animation modifications */
+    // check for old-style door graphic/animation modifications
 
     if (!door_gfx_redefined)
     {
@@ -4389,7 +4920,7 @@ void InitGraphicCompatibilityInfo_Doors()
        struct GraphicInfo *g_part_2 = &graphic_info[part_2];
        int num_door_steps, num_panel_steps;
 
-       /* remove door part graphics other than the two default wings */
+       // remove door part graphics other than the two default wings
 
        for (j = 0; door_part_controls[j].door_token != -1; j++)
        {
@@ -4401,7 +4932,7 @@ void InitGraphicCompatibilityInfo_Doors()
            g->bitmap = NULL;
        }
 
-       /* set graphics and screen positions of the default wings */
+       // set graphics and screen positions of the default wings
 
        g_part_1->width  = door_rect->width;
        g_part_1->height = door_rect->height;
@@ -4433,18 +4964,18 @@ void InitGraphicCompatibilityInfo_Doors()
          door->part_2.y  += door_rect->height - door->height;
        }
 
-       /* set animation delays for the default wings and panels */
+       // set animation delays for the default wings and panels
 
        door->part_1.step_delay = door->step_delay;
        door->part_2.step_delay = door->step_delay;
        door->panel.step_delay  = door->step_delay;
 
-       /* set animation draw order for the default wings */
+       // set animation draw order for the default wings
 
-       door->part_1.sort_priority = 2; /* draw left wing over ... */
-       door->part_2.sort_priority = 1; /*          ... right wing */
+       door->part_1.sort_priority = 2; // draw left wing over ...
+       door->part_2.sort_priority = 1; //          ... right wing
 
-       /* set animation draw offset for the default wings */
+       // set animation draw offset for the default wings
 
        if (door->anim_mode & ANIM_HORIZONTAL)
        {
@@ -4465,7 +4996,7 @@ void InitGraphicCompatibilityInfo_Doors()
          num_door_steps = g_part_1->height / door->step_offset;
        }
 
-       /* set animation draw offset for the default panels */
+       // set animation draw offset for the default panels
 
        if (door->step_offset > 1)
        {
@@ -4485,7 +5016,7 @@ void InitGraphicCompatibilityInfo_Doors()
   }
 }
 
-void InitDoors()
+void InitDoors(void)
 {
   int i;
 
@@ -4494,7 +5025,7 @@ void InitDoors()
     struct DoorPartControlInfo *dpc = &door_part_controls[i];
     struct DoorPartOrderInfo *dpo = &door_part_order[i];
 
-    /* initialize "start_step_opening" and "start_step_closing", if needed */
+    // initialize "start_step_opening" and "start_step_closing", if needed
     if (dpc->pos->start_step_opening == 0 &&
        dpc->pos->start_step_closing == 0)
     {
@@ -4502,12 +5033,12 @@ void InitDoors()
       dpc->pos->start_step_closing = dpc->pos->start_step;
     }
 
-    /* fill structure for door part draw order (sorted below) */
+    // fill structure for door part draw order (sorted below)
     dpo->nr = i;
     dpo->sort_priority = dpc->pos->sort_priority;
   }
 
-  /* sort door part controls according to sort_priority and graphic number */
+  // sort door part controls according to sort_priority and graphic number
   qsort(door_part_order, MAX_DOOR_PARTS,
         sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
 }
@@ -4550,7 +5081,7 @@ unsigned int CloseDoor(unsigned int door_state)
   return MoveDoor(door_state);
 }
 
-unsigned int GetDoorState()
+unsigned int GetDoorState(void)
 {
   return MoveDoor(DOOR_GET_STATE);
 }
@@ -4560,7 +5091,7 @@ unsigned int SetDoorState(unsigned int door_state)
   return MoveDoor(door_state | DOOR_SET_STATE);
 }
 
-int euclid(int a, int b)
+static int euclid(int a, int b)
 {
   return (b ? euclid(b, a % b) : a);
 }
@@ -4609,7 +5140,7 @@ unsigned int MoveDoor(unsigned int door_state)
     door_state &= ~DOOR_CLOSE_ALL;
   }
 
-  if (game_status == GAME_MODE_EDITOR)
+  if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
     door_state |= DOOR_NO_DELAY;
 
   if (door_state & DOOR_ACTION)
@@ -4699,11 +5230,25 @@ unsigned int MoveDoor(unsigned int door_state)
     }
     else
     {
-      /* opening door sound has priority over simultaneously closing door */
+      // opening door sound has priority over simultaneously closing door
       if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
+      {
         PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
+
+       if (door_state & DOOR_OPEN_1)
+         PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
+       if (door_state & DOOR_OPEN_2)
+         PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
+      }
       else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
+      {
         PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
+
+       if (door_state & DOOR_CLOSE_1)
+         PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
+       if (door_state & DOOR_CLOSE_2)
+         PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
+      }
     }
 
     for (k = start; k < num_move_steps; k++)
@@ -4873,13 +5418,27 @@ unsigned int MoveDoor(unsigned int door_state)
 
        current_move_delay += max_step_delay;
 
-       /* prevent OS (Windows) from complaining about program not responding */
+       // prevent OS (Windows) from complaining about program not responding
        CheckQuitEvent();
       }
 
       if (door_part_done_all)
        break;
     }
+
+    if (!(door_state & DOOR_NO_DELAY))
+    {
+      // wait for specified door action post delay
+      if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
+       door_delay_value = MAX(door_1.post_delay, door_2.post_delay);
+      else if (door_state & DOOR_ACTION_1)
+       door_delay_value = door_1.post_delay;
+      else if (door_state & DOOR_ACTION_2)
+       door_delay_value = door_2.post_delay;
+
+      while (!DelayReached(&door_delay, door_delay_value))
+       BackToFront();
+    }
   }
 
   if (door_state & DOOR_ACTION_1)
@@ -4891,10 +5450,12 @@ unsigned int MoveDoor(unsigned int door_state)
   DrawMaskedBorder(REDRAW_DOOR_1);
   DrawMaskedBorder(REDRAW_DOOR_2);
 
+  ClearAutoRepeatKeyEvents();
+
   return (door1 | door2);
 }
 
-static boolean useSpecialEditorDoor()
+static boolean useSpecialEditorDoor(void)
 {
   int graphic = IMG_GLOBAL_BORDER_EDITOR;
   boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
@@ -4917,7 +5478,7 @@ static boolean useSpecialEditorDoor()
   return TRUE;
 }
 
-void DrawSpecialEditorDoor()
+void DrawSpecialEditorDoor(void)
 {
   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
   int top_border_width = gfx1->width;
@@ -4931,7 +5492,7 @@ void DrawSpecialEditorDoor()
   if (!useSpecialEditorDoor())
     return;
 
-  /* draw bigger level editor toolbox window */
+  // draw bigger level editor toolbox window
   BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
             top_border_width, top_border_height, ex, ey - top_border_height);
   BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
@@ -4940,7 +5501,7 @@ void DrawSpecialEditorDoor()
   redraw_mask |= REDRAW_ALL;
 }
 
-void UndrawSpecialEditorDoor()
+void UndrawSpecialEditorDoor(void)
 {
   struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
   int top_border_width = gfx1->width;
@@ -4955,7 +5516,7 @@ void UndrawSpecialEditorDoor()
   if (!useSpecialEditorDoor())
     return;
 
-  /* draw normal tape recorder window */
+  // draw normal tape recorder window
   if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
   {
     BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
@@ -4975,7 +5536,7 @@ void UndrawSpecialEditorDoor()
 }
 
 
-/* ---------- new tool button stuff ---------------------------------------- */
+// ---------- new tool button stuff -------------------------------------------
 
 static struct
 {
@@ -5015,13 +5576,14 @@ static struct
   }
 };
 
-void CreateToolButtons()
+void CreateToolButtons(void)
 {
   int i;
 
   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
   {
-    struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
+    int graphic = toolbutton_info[i].graphic;
+    struct GraphicInfo *gfx = &graphic_info[graphic];
     struct TextPosInfo *pos = toolbutton_info[i].pos;
     struct GadgetInfo *gi;
     Bitmap *deco_bitmap = None;
@@ -5033,11 +5595,46 @@ void CreateToolButtons()
     int gd_y = gfx->src_y;
     int gd_xp = gfx->src_x + gfx->pressed_xoffset;
     int gd_yp = gfx->src_y + gfx->pressed_yoffset;
+    int x = pos->x;
+    int y = pos->y;
     int id = i;
 
     if (global.use_envelope_request)
+    {
       setRequestPosition(&dx, &dy, TRUE);
 
+      // check if request buttons are outside of envelope and fix, if needed
+      if (x < 0 || x + gfx->width  > request.width ||
+         y < 0 || y + gfx->height > request.height)
+      {
+       if (id == TOOL_CTRL_ID_YES)
+       {
+         x = 0;
+         y = request.height - 2 * request.border_size - gfx->height;
+       }
+       else if (id == TOOL_CTRL_ID_NO)
+       {
+         x = request.width  - 2 * request.border_size - gfx->width;
+         y = request.height - 2 * request.border_size - gfx->height;
+       }
+       else if (id == TOOL_CTRL_ID_CONFIRM)
+       {
+         x = (request.width - 2 * request.border_size - gfx->width) / 2;
+         y = request.height - 2 * request.border_size - gfx->height;
+       }
+       else if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
+       {
+         int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
+
+         x = (request.width - 2 * request.border_size - gfx->width) / 2;
+         y = request.height - 2 * request.border_size - gfx->height * 2;
+
+         x += (player_nr == 3 ? -1 : player_nr == 1 ? +1 : 0) * gfx->width;
+         y += (player_nr == 0 ? -1 : player_nr == 2 ? +1 : 0) * gfx->height;
+       }
+      }
+    }
+
     if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
     {
       int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
@@ -5049,9 +5646,10 @@ void CreateToolButtons()
     }
 
     gi = CreateGadget(GDI_CUSTOM_ID, id,
+                     GDI_IMAGE_ID, graphic,
                      GDI_INFO_TEXT, toolbutton_info[i].infotext,
-                     GDI_X, dx + GDI_ACTIVE_POS(pos->x),
-                     GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
+                     GDI_X, dx + x,
+                     GDI_Y, dy + y,
                      GDI_WIDTH, gfx->width,
                      GDI_HEIGHT, gfx->height,
                      GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
@@ -5074,7 +5672,7 @@ void CreateToolButtons()
   }
 }
 
-void FreeToolButtons()
+void FreeToolButtons(void)
 {
   int i;
 
@@ -5082,7 +5680,7 @@ void FreeToolButtons()
     FreeGadget(tool_gadget[i]);
 }
 
-static void UnmapToolButtons()
+static void UnmapToolButtons(void)
 {
   int i;
 
@@ -5098,8 +5696,8 @@ static void HandleToolButtons(struct GadgetInfo *gi)
 static struct Mapping_EM_to_RND_object
 {
   int element_em;
-  boolean is_rnd_to_em_mapping;                /* unique mapping EM <-> RND */
-  boolean is_backside;                 /* backside of moving element */
+  boolean is_rnd_to_em_mapping;                // unique mapping EM <-> RND
+  boolean is_backside;                 // backside of moving element
 
   int element_rnd;
   int action;
@@ -5169,7 +5767,7 @@ em_object_mapping_list[] =
     Xbomb_force_w,                     FALSE,  FALSE,
     EL_BOMB,                           -1, MV_BIT_LEFT
   },
-#endif /* EM_ENGINE_BAD_ROLL */
+#endif // EM_ENGINE_BAD_ROLL
 
   {
     Xstone,                            TRUE,   FALSE,
@@ -7022,7 +7620,7 @@ int map_element_RND_to_EM(int element_rnd)
   {
     int i;
 
-    /* return "Xalpha_quest" for all undefined elements in mapping array */
+    // return "Xalpha_quest" for all undefined elements in mapping array
     for (i = 0; i < NUM_FILE_ELEMENTS; i++)
       mapping_RND_to_EM[i] = Xalpha_quest;
 
@@ -7051,7 +7649,7 @@ int map_element_EM_to_RND(int element_em)
   {
     int i;
 
-    /* return "EL_UNKNOWN" for all undefined elements in mapping array */
+    // return "EL_UNKNOWN" for all undefined elements in mapping array
     for (i = 0; i < TILE_MAX; i++)
       mapping_EM_to_RND[i] = EL_UNKNOWN;
 
@@ -7150,7 +7748,7 @@ int map_direction_EM_to_RND(int direction)
 
 int map_element_RND_to_SP(int element_rnd)
 {
-  int element_sp = 0x20;       /* map unknown elements to yellow "hardware" */
+  int element_sp = 0x20;       // map unknown elements to yellow "hardware"
 
   if (element_rnd >= EL_SP_START &&
       element_rnd <= EL_SP_END)
@@ -7194,6 +7792,125 @@ int map_action_SP_to_RND(int action_sp)
   }
 }
 
+int map_element_RND_to_MM(int element_rnd)
+{
+  return (element_rnd >= EL_MM_START_1 &&
+         element_rnd <= EL_MM_END_1 ?
+         EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
+
+         element_rnd >= EL_MM_START_2 &&
+         element_rnd <= EL_MM_END_2 ?
+         EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
+
+         element_rnd >= EL_CHAR_START &&
+         element_rnd <= EL_CHAR_END ?
+         EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
+
+         element_rnd >= EL_MM_RUNTIME_START &&
+         element_rnd <= EL_MM_RUNTIME_END ?
+         EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
+
+         element_rnd >= EL_MM_DUMMY_START &&
+         element_rnd <= EL_MM_DUMMY_END ?
+         EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
+
+         EL_MM_EMPTY_NATIVE);
+}
+
+int map_element_MM_to_RND(int element_mm)
+{
+  return (element_mm == EL_MM_EMPTY_NATIVE ||
+         element_mm == EL_DF_EMPTY_NATIVE ?
+         EL_EMPTY :
+
+         element_mm >= EL_MM_START_1_NATIVE &&
+         element_mm <= EL_MM_END_1_NATIVE ?
+         EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
+
+         element_mm >= EL_MM_START_2_NATIVE &&
+         element_mm <= EL_MM_END_2_NATIVE ?
+         EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
+
+         element_mm >= EL_MM_CHAR_START_NATIVE &&
+         element_mm <= EL_MM_CHAR_END_NATIVE ?
+         EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
+
+         element_mm >= EL_MM_RUNTIME_START_NATIVE &&
+         element_mm <= EL_MM_RUNTIME_END_NATIVE ?
+         EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
+
+         element_mm >= EL_MM_DUMMY_START_NATIVE &&
+         element_mm <= EL_MM_DUMMY_END_NATIVE ?
+         EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
+
+         EL_EMPTY);
+}
+
+int map_action_MM_to_RND(int action_mm)
+{
+  // all MM actions are defined to exactly match their RND counterparts
+  return action_mm;
+}
+
+int map_sound_MM_to_RND(int sound_mm)
+{
+  switch (sound_mm)
+  {
+    case SND_MM_GAME_LEVELTIME_CHARGING:
+      return SND_GAME_LEVELTIME_CHARGING;
+
+    case SND_MM_GAME_HEALTH_CHARGING:
+      return SND_GAME_HEALTH_CHARGING;
+
+    default:
+      return SND_UNDEFINED;
+  }
+}
+
+int map_mm_wall_element(int element)
+{
+  return (element >= EL_MM_STEEL_WALL_START &&
+         element <= EL_MM_STEEL_WALL_END ?
+         EL_MM_STEEL_WALL :
+
+         element >= EL_MM_WOODEN_WALL_START &&
+         element <= EL_MM_WOODEN_WALL_END ?
+         EL_MM_WOODEN_WALL :
+
+         element >= EL_MM_ICE_WALL_START &&
+         element <= EL_MM_ICE_WALL_END ?
+         EL_MM_ICE_WALL :
+
+         element >= EL_MM_AMOEBA_WALL_START &&
+         element <= EL_MM_AMOEBA_WALL_END ?
+         EL_MM_AMOEBA_WALL :
+
+         element >= EL_DF_STEEL_WALL_START &&
+         element <= EL_DF_STEEL_WALL_END ?
+         EL_DF_STEEL_WALL :
+
+         element >= EL_DF_WOODEN_WALL_START &&
+         element <= EL_DF_WOODEN_WALL_END ?
+         EL_DF_WOODEN_WALL :
+
+         element);
+}
+
+int map_mm_wall_element_editor(int element)
+{
+  switch (element)
+  {
+    case EL_MM_STEEL_WALL:     return EL_MM_STEEL_WALL_START;
+    case EL_MM_WOODEN_WALL:    return EL_MM_WOODEN_WALL_START;
+    case EL_MM_ICE_WALL:       return EL_MM_ICE_WALL_START;
+    case EL_MM_AMOEBA_WALL:    return EL_MM_AMOEBA_WALL_START;
+    case EL_DF_STEEL_WALL:     return EL_DF_STEEL_WALL_START;
+    case EL_DF_WOODEN_WALL:    return EL_DF_WOODEN_WALL_START;
+
+    default:                   return element;
+  }
+}
+
 int get_next_element(int element)
 {
   switch (element)
@@ -7214,21 +7931,26 @@ int get_next_element(int element)
   }
 }
 
+int el2img_mm(int element_mm)
+{
+  return el2img(map_element_MM_to_RND(element_mm));
+}
+
 int el_act_dir2img(int element, int action, int direction)
 {
   element = GFX_ELEMENT(element);
-  direction = MV_DIR_TO_BIT(direction);        /* default: MV_NONE => MV_DOWN */
+  direction = MV_DIR_TO_BIT(direction);        // default: MV_NONE => MV_DOWN
 
-  /* direction_graphic[][] == graphic[] for undefined direction graphics */
+  // direction_graphic[][] == graphic[] for undefined direction graphics
   return element_info[element].direction_graphic[action][direction];
 }
 
 static int el_act_dir2crm(int element, int action, int direction)
 {
   element = GFX_ELEMENT(element);
-  direction = MV_DIR_TO_BIT(direction);        /* default: MV_NONE => MV_DOWN */
+  direction = MV_DIR_TO_BIT(direction);        // default: MV_NONE => MV_DOWN
 
-  /* direction_graphic[][] == graphic[] for undefined direction graphics */
+  // direction_graphic[][] == graphic[] for undefined direction graphics
   return element_info[element].direction_crumbled[action][direction];
 }
 
@@ -7412,9 +8134,9 @@ int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
   return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
 }
 
-boolean getTeamMode_EM()
+boolean getTeamMode_EM(void)
 {
-  return game.team_mode;
+  return game.team_mode || network_playing;
 }
 
 int getGameFrameDelay_EM(int native_em_game_frame_delay)
@@ -7438,6 +8160,8 @@ unsigned int InitRND(int seed)
     return InitEngineRandom_EM(seed);
   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
     return InitEngineRandom_SP(seed);
+  else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
+    return InitEngineRandom_MM(seed);
   else
     return InitEngineRandom_RND(seed);
 }
@@ -7445,7 +8169,7 @@ unsigned int InitRND(int seed)
 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
 
-inline static int get_effective_element_EM(int tile, int frame_em)
+static int get_effective_element_EM(int tile, int frame_em)
 {
   int element             = object_mapping[tile].element_rnd;
   int action              = object_mapping[tile].action;
@@ -7466,7 +8190,7 @@ inline static int get_effective_element_EM(int tile, int frame_em)
        return element;
     }
   }
-  else /* frame_em == 7 */
+  else // frame_em == 7
   {
     switch (tile)
     {
@@ -7512,7 +8236,7 @@ inline static int get_effective_element_EM(int tile, int frame_em)
   }
 }
 
-inline static boolean check_linear_animation_EM(int tile)
+static boolean check_linear_animation_EM(int tile)
 {
   switch (tile)
   {
@@ -7548,13 +8272,13 @@ inline static boolean check_linear_animation_EM(int tile)
   return FALSE;
 }
 
-inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
-                                           boolean has_crumbled_graphics,
-                                           int crumbled, int sync_frame)
+static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
+                                    boolean has_crumbled_graphics,
+                                    int crumbled, int sync_frame)
 {
-  /* if element can be crumbled, but certain action graphics are just empty
-     space (like instantly snapping sand to empty space in 1 frame), do not
-     treat these empty space graphics as crumbled graphics in EMC engine */
+  // if element can be crumbled, but certain action graphics are just empty
+  // space (like instantly snapping sand to empty space in 1 frame), do not
+  // treat these empty space graphics as crumbled graphics in EMC engine
   if (crumbled == IMG_EMPTY_SPACE)
     has_crumbled_graphics = FALSE;
 
@@ -7587,10 +8311,12 @@ inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
   }
 }
 
+#if 0
 void ResetGfxAnimation_EM(int x, int y, int tile)
 {
   GfxFrame[x][y] = 0;
 }
+#endif
 
 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
                        int tile, int frame_em, int x, int y)
@@ -7616,12 +8342,12 @@ void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
                             action == ACTION_FILLING ||
                             action == ACTION_EMPTYING);
 
-  /* special case: graphic uses "2nd movement tile" and has defined
-     7 frames for movement animation (or less) => use default graphic
-     for last (8th) frame which ends the movement animation */
+  // special case: graphic uses "2nd movement tile" and has defined
+  // 7 frames for movement animation (or less) => use default graphic
+  // for last (8th) frame which ends the movement animation
   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
   {
-    action = ACTION_DEFAULT;   /* (keep action_* unchanged for now) */
+    action = ACTION_DEFAULT;   // (keep action_* unchanged for now)
     graphic = (direction == MV_NONE ?
               el_act2img(effective_element, action) :
               el_act_dir2img(effective_element, action, direction));
@@ -7645,7 +8371,7 @@ void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
       GfxFrame[x][y]++;
 
 #if 1
-      /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
+      // !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!!
       if (g->double_movement && frame_em == 0)
        GfxFrame[x][y] = 0;
 #endif
@@ -7664,7 +8390,7 @@ void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
   {
     GfxFrame[x][y]++;
 
-    /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
+    // special case: animation for Xsand_stonesand_quickout_1/2 twice as fast
     if (tile == Xsand_stonesand_quickout_1 ||
        tile == Xsand_stonesand_quickout_2)
       GfxFrame[x][y]++;
@@ -7675,7 +8401,7 @@ void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
     sync_frame = GfxFrame[x][y];
   else
-    sync_frame = 0;    /* playfield border (pseudo steel) */
+    sync_frame = 0;    // playfield border (pseudo steel)
 
   SetRandomAnimationValue(x, y);
 
@@ -7711,9 +8437,9 @@ void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
   struct GraphicInfo *g = &graphic_info[graphic];
   int sync_frame;
 
-  /* special case: graphic uses "2nd movement tile" and has defined
-     7 frames for movement animation (or less) => use default graphic
-     for last (8th) frame which ends the movement animation */
+  // special case: graphic uses "2nd movement tile" and has defined
+  // 7 frames for movement animation (or less) => use default graphic
+  // for last (8th) frame which ends the movement animation
   if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
   {
     effective_action = ACTION_DEFAULT;
@@ -7734,7 +8460,7 @@ void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
   else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
     sync_frame = GfxFrame[x][y];
   else
-    sync_frame = 0;    /* playfield border (pseudo steel) */
+    sync_frame = 0;    // playfield border (pseudo steel)
 
   SetRandomAnimationValue(x, y);
 
@@ -7747,8 +8473,8 @@ void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
   getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
                      g->double_movement && is_backside);
 
-  /* (updating the "crumbled" graphic definitions is probably not really needed,
-     as animations for crumbled graphics can't be longer than one EMC cycle) */
+  // (updating the "crumbled" graphic definitions is probably not really needed,
+  // as animations for crumbled graphics can't be longer than one EMC cycle)
   set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
                           sync_frame);
 }
@@ -7790,7 +8516,7 @@ void InitGraphicInfo_EM(void)
 
   if (graphic_info_em_object[0][0].bitmap == NULL)
   {
-    /* EM graphics not yet initialized in em_open_all() */
+    // EM graphics not yet initialized in em_open_all()
 
     return;
   }
@@ -7798,7 +8524,7 @@ void InitGraphicInfo_EM(void)
   printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
 #endif
 
-  /* always start with reliable default values */
+  // always start with reliable default values
   for (i = 0; i < TILE_MAX; i++)
   {
     object_mapping[i].element_rnd = EL_UNKNOWN;
@@ -7807,7 +8533,7 @@ void InitGraphicInfo_EM(void)
     object_mapping[i].direction = MV_NONE;
   }
 
-  /* always start with reliable default values */
+  // always start with reliable default values
   for (p = 0; p < MAX_PLAYERS; p++)
   {
     for (i = 0; i < SPR_MAX; i++)
@@ -7908,7 +8634,7 @@ void InitGraphicInfo_EM(void)
       struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
       Bitmap *src_bitmap;
       int src_x, src_y;
-      /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
+      // ensure to get symmetric 3-frame, 2-delay animations as used in EM
       boolean special_animation = (action != ACTION_DEFAULT &&
                                   g->anim_frames == 3 &&
                                   g->anim_delay == 2 &&
@@ -8079,7 +8805,7 @@ void InitGraphicInfo_EM(void)
                          i == Xsand_stoneout_2 ? j + 8 : j) + 1;
        int step = (is_backside ? step_frame : num_steps - step_frame);
 
-       if (is_backside)        /* tile where movement starts */
+       if (is_backside)        // tile where movement starts
        {
          if (dx < 0 || dy < 0)
          {
@@ -8092,7 +8818,7 @@ void InitGraphicInfo_EM(void)
            g_em->dst_offset_y = cy * step;
          }
        }
-       else                    /* tile where movement ends */
+       else                    // tile where movement ends
        {
          if (dx < 0 || dy < 0)
          {
@@ -8110,7 +8836,7 @@ void InitGraphicInfo_EM(void)
        g_em->height = TILEY - cy * step;
       }
 
-      /* create unique graphic identifier to decide if tile must be redrawn */
+      // create unique graphic identifier to decide if tile must be redrawn
       /* bit 31 - 16 (16 bit): EM style graphic
         bit 15 - 12 ( 4 bit): EM style frame
         bit 11 -  6 ( 6 bit): graphic width
@@ -8120,7 +8846,7 @@ void InitGraphicInfo_EM(void)
 
 #if DEBUG_EM_GFX
 
-      /* skip check for EMC elements not contained in original EMC artwork */
+      // skip check for EMC elements not contained in original EMC artwork
       if (element == EL_EMC_FAKE_ACID)
        continue;
 
@@ -8210,7 +8936,7 @@ void InitGraphicInfo_EM(void)
                 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
                 Xspring);
 
-       /* no separate animation for "smashed by rock" -- use rock instead */
+       // no separate animation for "smashed by rock" -- use rock instead
        struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
        struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
 
@@ -8279,7 +9005,7 @@ void InitGraphicInfo_EM(void)
 
 #if DEBUG_EM_GFX
 
-       /* skip check for EMC elements not contained in original EMC artwork */
+       // skip check for EMC elements not contained in original EMC artwork
        if (element == EL_PLAYER_3 ||
            element == EL_PLAYER_4)
          continue;
@@ -8336,10 +9062,10 @@ void InitGraphicInfo_EM(void)
 #endif
 }
 
-void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
-                               boolean any_player_moving,
-                               boolean any_player_snapping,
-                               boolean any_player_dropping)
+static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
+                                      boolean any_player_moving,
+                                      boolean any_player_snapping,
+                                      boolean any_player_dropping)
 {
   if (frame == 0 && !any_player_dropping)
   {
@@ -8357,8 +9083,8 @@ void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
   }
 }
 
-void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
-                               boolean murphy_is_dropping)
+static void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
+                                      boolean murphy_is_dropping)
 {
   if (murphy_is_waiting)
   {
@@ -8376,6 +9102,23 @@ void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
   }
 }
 
+static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
+                                      boolean button_released)
+{
+  if (button_released)
+  {
+    if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE)
+      CheckSaveEngineSnapshotToList();
+  }
+  else if (element_clicked)
+  {
+    if (game.snapshot.mode != SNAPSHOT_MODE_EVERY_MOVE)
+      CheckSaveEngineSnapshotToList();
+
+    game.snapshot.changed_action = TRUE;
+  }
+}
+
 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
                            boolean any_player_moving,
                            boolean any_player_snapping,
@@ -8406,6 +9149,16 @@ void CheckSingleStepMode_SP(boolean murphy_is_waiting,
   CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
 }
 
+void CheckSingleStepMode_MM(boolean element_clicked,
+                           boolean button_released)
+{
+  if (tape.single_step && tape.recording && !tape.pausing)
+    if (button_released)
+      TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+
+  CheckSaveEngineSnapshot_MM(element_clicked, button_released);
+}
+
 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
                         int graphic, int sync_frame, int x, int y)
 {
@@ -8439,7 +9192,7 @@ void PlayMenuSoundExt(int sound)
     PlaySound(sound);
 }
 
-void PlayMenuSound()
+void PlayMenuSound(void)
 {
   PlayMenuSoundExt(menu.sound[game_status]);
 }
@@ -8472,7 +9225,7 @@ void PlayMenuSoundIfLoopExt(int sound)
     PlaySoundLoop(sound);
 }
 
-void PlayMenuSoundIfLoop()
+void PlayMenuSoundIfLoop(void)
 {
   PlayMenuSoundIfLoopExt(menu.sound[game_status]);
 }
@@ -8485,10 +9238,13 @@ void PlayMenuMusicExt(int music)
   if (!setup.sound_music)
     return;
 
-  PlayMusic(music);
+  if (IS_LOOP_MUSIC(music))
+    PlayMusicLoop(music);
+  else
+    PlayMusic(music);
 }
 
-void PlayMenuMusic()
+void PlayMenuMusic(void)
 {
   char *curr_music = getCurrentlyPlayingMusicFilename();
   char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
@@ -8497,18 +9253,18 @@ void PlayMenuMusic()
     PlayMenuMusicExt(menu.music[game_status]);
 }
 
-void PlayMenuSoundsAndMusic()
+void PlayMenuSoundsAndMusic(void)
 {
   PlayMenuSound();
   PlayMenuMusic();
 }
 
-static void FadeMenuSounds()
+static void FadeMenuSounds(void)
 {
   FadeSounds();
 }
 
-static void FadeMenuMusic()
+static void FadeMenuMusic(void)
 {
   char *curr_music = getCurrentlyPlayingMusicFilename();
   char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
@@ -8517,27 +9273,27 @@ static void FadeMenuMusic()
     FadeMusic();
 }
 
-void FadeMenuSoundsAndMusic()
+void FadeMenuSoundsAndMusic(void)
 {
   FadeMenuSounds();
   FadeMenuMusic();
 }
 
-void PlaySoundActivating()
+void PlaySoundActivating(void)
 {
 #if 0
   PlaySound(SND_MENU_ITEM_ACTIVATING);
 #endif
 }
 
-void PlaySoundSelecting()
+void PlaySoundSelecting(void)
 {
 #if 0
   PlaySound(SND_MENU_ITEM_SELECTING);
 #endif
 }
 
-void ToggleFullscreenOrChangeWindowScalingIfNeeded()
+void ToggleFullscreenOrChangeWindowScalingIfNeeded(void)
 {
   boolean change_fullscreen = (setup.fullscreen !=
                               video.fullscreen_enabled);
@@ -8551,7 +9307,6 @@ void ToggleFullscreenOrChangeWindowScalingIfNeeded()
   if (!change_window_scaling_percent && !video.fullscreen_available)
     return;
 
-#if defined(TARGET_SDL2)
   if (change_window_scaling_percent)
   {
     SDLSetWindowScaling(setup.window_scaling_percent);
@@ -8562,45 +9317,44 @@ void ToggleFullscreenOrChangeWindowScalingIfNeeded()
   {
     SDLSetWindowFullscreen(setup.fullscreen);
 
-    /* set setup value according to successfully changed fullscreen mode */
+    // set setup value according to successfully changed fullscreen mode
     setup.fullscreen = video.fullscreen_enabled;
 
     return;
   }
-#endif
 
   if (change_fullscreen ||
       change_window_scaling_percent)
   {
     Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
 
-    /* save backbuffer content which gets lost when toggling fullscreen mode */
+    // save backbuffer content which gets lost when toggling fullscreen mode
     BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
 
     if (change_window_scaling_percent)
     {
-      /* keep window mode, but change window scaling */
-      video.fullscreen_enabled = TRUE;         /* force new window scaling */
+      // keep window mode, but change window scaling
+      video.fullscreen_enabled = TRUE;         // force new window scaling
     }
 
-    /* toggle fullscreen */
+    // toggle fullscreen
     ChangeVideoModeIfNeeded(setup.fullscreen);
 
-    /* set setup value according to successfully changed fullscreen mode */
+    // set setup value according to successfully changed fullscreen mode
     setup.fullscreen = video.fullscreen_enabled;
 
-    /* restore backbuffer content from temporary backbuffer backup bitmap */
+    // restore backbuffer content from temporary backbuffer backup bitmap
     BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
 
     FreeBitmap(tmp_backbuffer);
 
-    /* update visible window/screen */
+    // update visible window/screen
     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
   }
 }
 
-void JoinRectangles(int *x, int *y, int *width, int *height,
-                   int x2, int y2, int width2, int height2)
+static void JoinRectangles(int *x, int *y, int *width, int *height,
+                          int x2, int y2, int width2, int height2)
 {
   // do not join with "off-screen" rectangle
   if (x2 == -1 || y2 == -1)
@@ -8656,13 +9410,76 @@ void SetFontStatus(int game_status_new)
   }
 }
 
-void ResetFontStatus()
+void ResetFontStatus(void)
 {
   SetFontStatus(-1);
 }
 
-void ChangeViewportPropertiesIfNeeded()
+void SetLevelSetInfo(char *identifier, int level_nr)
 {
+  setString(&levelset.identifier, identifier);
+
+  levelset.level_nr = level_nr;
+}
+
+boolean CheckIfAllViewportsHaveChanged(void)
+{
+  // if game status has not changed, viewports have not changed either
+  if (game_status == game_status_last)
+    return FALSE;
+
+  // check if all viewports have changed with current game status
+
+  struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
+  struct RectWithBorder *vp_door_1    = &viewport.door_1[game_status];
+  struct RectWithBorder *vp_door_2    = &viewport.door_2[game_status];
+  int new_real_sx      = vp_playfield->x;
+  int new_real_sy      = vp_playfield->y;
+  int new_full_sxsize  = vp_playfield->width;
+  int new_full_sysize  = vp_playfield->height;
+  int new_dx           = vp_door_1->x;
+  int new_dy           = vp_door_1->y;
+  int new_dxsize       = vp_door_1->width;
+  int new_dysize       = vp_door_1->height;
+  int new_vx           = vp_door_2->x;
+  int new_vy           = vp_door_2->y;
+  int new_vxsize       = vp_door_2->width;
+  int new_vysize       = vp_door_2->height;
+
+  boolean playfield_viewport_has_changed =
+    (new_real_sx != REAL_SX ||
+     new_real_sy != REAL_SY ||
+     new_full_sxsize != FULL_SXSIZE ||
+     new_full_sysize != FULL_SYSIZE);
+
+  boolean door_1_viewport_has_changed =
+    (new_dx != DX ||
+     new_dy != DY ||
+     new_dxsize != DXSIZE ||
+     new_dysize != DYSIZE);
+
+  boolean door_2_viewport_has_changed =
+    (new_vx != VX ||
+     new_vy != VY ||
+     new_vxsize != VXSIZE ||
+     new_vysize != VYSIZE ||
+     game_status_last == GAME_MODE_EDITOR);
+
+  return (playfield_viewport_has_changed &&
+         door_1_viewport_has_changed &&
+         door_2_viewport_has_changed);
+}
+
+boolean CheckFadeAll(void)
+{
+  return (CheckIfGlobalBorderHasChanged() ||
+         CheckIfAllViewportsHaveChanged());
+}
+
+void ChangeViewportPropertiesIfNeeded(void)
+{
+  boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
+                              FALSE : setup.small_game_graphics);
   int gfx_game_mode = game_status;
   int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
                        game_status);
@@ -8673,11 +9490,14 @@ void ChangeViewportPropertiesIfNeeded()
   struct RectWithBorder *vp_door_3    = &viewport.door_2[GAME_MODE_EDITOR];
   int new_win_xsize    = vp_window->width;
   int new_win_ysize    = vp_window->height;
-  int border_size      = vp_playfield->border_size;
-  int new_sx           = vp_playfield->x + border_size;
-  int new_sy           = vp_playfield->y + border_size;
-  int new_sxsize       = vp_playfield->width  - 2 * border_size;
-  int new_sysize       = vp_playfield->height - 2 * border_size;
+  int border_left      = vp_playfield->border_left;
+  int border_right     = vp_playfield->border_right;
+  int border_top       = vp_playfield->border_top;
+  int border_bottom    = vp_playfield->border_bottom;
+  int new_sx           = vp_playfield->x      + border_left;
+  int new_sy           = vp_playfield->y      + border_top;
+  int new_sxsize       = vp_playfield->width  - border_left - border_right;
+  int new_sysize       = vp_playfield->height - border_top  - border_bottom;
   int new_real_sx      = vp_playfield->x;
   int new_real_sy      = vp_playfield->y;
   int new_full_sxsize  = vp_playfield->width;
@@ -8694,9 +9514,7 @@ void ChangeViewportPropertiesIfNeeded()
   int new_ey           = vp_door_3->y;
   int new_exsize       = vp_door_3->width;
   int new_eysize       = vp_door_3->height;
-  int new_tilesize_var =
-    (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
-
+  int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
   int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
                  gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
   int new_scr_fieldx = new_sxsize / tilesize;
@@ -8724,7 +9542,7 @@ void ChangeViewportPropertiesIfNeeded()
   if (new_scr_fieldx != SCR_FIELDX ||
       new_scr_fieldy != SCR_FIELDY)
   {
-    /* this always toggles between MAIN and GAME when using small tile size */
+    // this always toggles between MAIN and GAME when using small tile size
 
     SCR_FIELDX = new_scr_fieldx;
     SCR_FIELDY = new_scr_fieldy;
@@ -8784,8 +9602,8 @@ void ChangeViewportPropertiesIfNeeded()
     }
 
     // add current and new door 2 area if position or size has changed
-    if (new_dx != VX || new_dy != VY ||
-       new_dxsize != VXSIZE || new_dysize != VYSIZE)
+    if (new_vx != VX || new_vy != VY ||
+       new_vxsize != VXSIZE || new_vysize != VYSIZE)
     {
       JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
                     VX, VY, VXSIZE, VYSIZE);
@@ -8857,6 +9675,8 @@ void ChangeViewportPropertiesIfNeeded()
   {
     // printf("::: init_video_buffer\n");
 
+    FreeAllImageTextures();    // needs old renderer to free the textures
+
     InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
     InitImageTextures();
   }