rnd-20070927-1-src
[rocksndiamonds.git] / src / tools.c
index 3a2b962a3469435f1d8f00d6acf497f5be894d87..3d3930dfb736e3f68b4ab09f0471c30541b85935 100644 (file)
@@ -1,7 +1,7 @@
 /***********************************************************
 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
 *----------------------------------------------------------*
-* (c) 1995-2002 Artsoft Entertainment                      *
+* (c) 1995-2006 Artsoft Entertainment                      *
 *               Holger Schemel                             *
 *               Detmolder Strasse 189                      *
 *               33604 Bielefeld                            *
@@ -19,6 +19,7 @@
 #include "cartoons.h"
 #include "network.h"
 #include "tape.h"
+#include "screens.h"
 
 
 /* select level set with EMC X11 graphics before activating EM GFX debugging */
@@ -117,7 +118,7 @@ void SetDrawtoField(int mode)
 
     drawto_field = fieldbuffer;
   }
-  else /* DRAW_DIRECT, DRAW_BACKBUFFER */
+  else /* DRAW_BACKBUFFER */
   {
     FX = SX;
     FY = SY;
@@ -128,7 +129,7 @@ void SetDrawtoField(int mode)
     redraw_x1 = 0;
     redraw_y1 = 0;
 
-    drawto_field = (mode == DRAW_DIRECT ? window :  backbuffer);
+    drawto_field = backbuffer;
   }
 }
 
@@ -138,8 +139,10 @@ void RedrawPlayfield(boolean force_redraw, int x, int y, int width, int height)
       level.game_engine_type == GAME_ENGINE_TYPE_EM)
   {
     /* currently there is no partial redraw -- always redraw whole playfield */
-
     RedrawPlayfield_EM(TRUE);
+
+    /* blit playfield from scroll buffer to normal back buffer for fading in */
+    BlitScreenToBitmap_EM(backbuffer);
   }
   else if (game_status == GAME_MODE_PLAYING && !game.envelope_active)
   {
@@ -151,23 +154,17 @@ void RedrawPlayfield(boolean force_redraw, int x, int y, int width, int height)
       height = gfx.sysize + 2 * TILEY;
     }
 
-    if (force_redraw || setup.direct_draw)
+    if (force_redraw)
     {
       int xx, yy;
       int x1 = (x - SX) / TILEX, y1 = (y - SY) / TILEY;
       int x2 = (x - SX + width) / TILEX, y2 = (y - SY + height) / TILEY;
 
-      if (setup.direct_draw)
-       SetDrawtoField(DRAW_BACKBUFFER);
-
       for (xx = BX1; xx <= BX2; xx++)
        for (yy = BY1; yy <= BY2; yy++)
          if (xx >= x1 && xx <= x2 && yy >= y1 && yy <= y2)
            DrawScreenField(xx, yy);
       DrawAllPlayers();
-
-      if (setup.direct_draw)
-       SetDrawtoField(DRAW_DIRECT);
     }
 
     if (setup.soft_scrolling)
@@ -181,17 +178,88 @@ void RedrawPlayfield(boolean force_redraw, int x, int y, int width, int height)
     }
   }
 
+  if (force_redraw)
+  {
+    x = gfx.sx;
+    y = gfx.sy;
+    width = gfx.sxsize;
+    height = gfx.sysize;
+  }
+
   BlitBitmap(drawto, window, x, y, width, height, x, y);
 }
 
+void DrawMaskedBorder_Rect(int x, int y, int width, int height)
+{
+  Bitmap *bitmap = graphic_info[IMG_GLOBAL_BORDER].bitmap;
+
+  SetClipOrigin(bitmap, bitmap->stored_clip_gc, 0, 0);
+  BlitBitmapMasked(bitmap, backbuffer, x, y, width, height, x, y);
+}
+
+void DrawMaskedBorder_FIELD()
+{
+  if (global.border_status >= GAME_MODE_TITLE &&
+      global.border_status <= GAME_MODE_PLAYING &&
+      border.draw_masked[global.border_status])
+    DrawMaskedBorder_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
+}
+
+void DrawMaskedBorder_DOOR_1()
+{
+  if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
+      (global.border_status != GAME_MODE_EDITOR ||
+       border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
+    DrawMaskedBorder_Rect(DX, DY, DXSIZE, DYSIZE);
+}
+
+void DrawMaskedBorder_DOOR_2()
+{
+  if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
+      global.border_status != GAME_MODE_EDITOR)
+    DrawMaskedBorder_Rect(VX, VY, VXSIZE, VYSIZE);
+}
+
+void DrawMaskedBorder_DOOR_3()
+{
+  /* currently not available */
+}
+
+void DrawMaskedBorder_ALL()
+{
+  DrawMaskedBorder_FIELD();
+  DrawMaskedBorder_DOOR_1();
+  DrawMaskedBorder_DOOR_2();
+  DrawMaskedBorder_DOOR_3();
+}
+
+void DrawMaskedBorder(int redraw_mask)
+{
+  /* never draw masked screen borders on borderless screens */
+  if (effectiveGameStatus() == GAME_MODE_LOADING ||
+      effectiveGameStatus() == GAME_MODE_TITLE)
+    return;
+
+  if (redraw_mask & REDRAW_ALL)
+    DrawMaskedBorder_ALL();
+  else
+  {
+    if (redraw_mask & REDRAW_FIELD)
+      DrawMaskedBorder_FIELD();
+    if (redraw_mask & REDRAW_DOOR_1)
+      DrawMaskedBorder_DOOR_1();
+    if (redraw_mask & REDRAW_DOOR_2)
+      DrawMaskedBorder_DOOR_2();
+    if (redraw_mask & REDRAW_DOOR_3)
+      DrawMaskedBorder_DOOR_3();
+  }
+}
+
 void BackToFront()
 {
   int x,y;
   DrawBuffer *buffer = (drawto_field == window ? backbuffer : drawto_field);
 
-  if (setup.direct_draw && game_status == GAME_MODE_PLAYING)
-    redraw_mask &= ~REDRAW_MAIN;
-
   if (redraw_mask & REDRAW_TILES && redraw_tiles > REDRAWTILES_THRESHOLD)
     redraw_mask |= REDRAW_FIELD;
 
@@ -201,6 +269,11 @@ void BackToFront()
   if (redraw_mask == REDRAW_NONE)
     return;
 
+  if (redraw_mask & REDRAW_TILES &&
+      game_status == GAME_MODE_PLAYING &&
+      border.draw_masked[GAME_MODE_PLAYING])
+    redraw_mask |= REDRAW_FIELD;
+
   if (global.fps_slowdown && game_status == GAME_MODE_PLAYING)
   {
     static boolean last_frame_skipped = FALSE;
@@ -238,6 +311,14 @@ void BackToFront()
 
   SyncDisplay();
 
+  /* prevent drawing masked border to backbuffer when using playfield buffer */
+  if (game_status != GAME_MODE_PLAYING ||
+      redraw_mask & REDRAW_FROM_BACKBUFFER ||
+      buffer == backbuffer)
+    DrawMaskedBorder(redraw_mask);
+  else
+    DrawMaskedBorder(redraw_mask & REDRAW_DOORS);
+
   if (redraw_mask & REDRAW_ALL)
   {
     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
@@ -268,7 +349,23 @@ void BackToFront()
          ABS(ScreenMovPos) == ScrollStepSize ||
          redraw_tiles > REDRAWTILES_THRESHOLD)
       {
-       BlitBitmap(buffer, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
+       if (border.draw_masked[GAME_MODE_PLAYING])
+       {
+         if (buffer != backbuffer)
+         {
+           /* copy playfield buffer to backbuffer to add masked border */
+           BlitBitmap(buffer, backbuffer, fx, fy, SXSIZE, SYSIZE, SX, SY);
+           DrawMaskedBorder(REDRAW_FIELD);
+         }
+
+         BlitBitmap(backbuffer, window,
+                    REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
+                    REAL_SX, REAL_SY);
+       }
+       else
+       {
+         BlitBitmap(buffer, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
+       }
 
 #if 0
 #ifdef DEBUG
@@ -343,125 +440,290 @@ void BackToFront()
   redraw_mask = REDRAW_NONE;
 }
 
-void FadeToFront()
+static void FadeCrossSaveBackbuffer()
 {
-#if 0
-  long fading_delay = 300;
+  BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
+}
 
-  if (setup.fading && (redraw_mask & REDRAW_FIELD))
+static void FadeExt(int fade_mask, int fade_mode, int fade_type)
+{
+  static int fade_type_skip = FADE_TYPE_NONE;
+  void (*draw_border_function)(void) = NULL;
+  Bitmap *bitmap = (fade_mode & FADE_TYPE_TRANSFORM ? bitmap_db_cross : NULL);
+  int x, y, width, height;
+  int fade_delay, post_delay;
+
+  if (fade_type == FADE_TYPE_FADE_OUT)
   {
+    if (fade_type_skip != FADE_TYPE_NONE)
+    {
+#if 0
+      printf("::: skipping %d ... [%d] (X)\n", fade_mode, fade_type_skip);
 #endif
 
-#if 0
-    int x,y;
+      /* skip all fade operations until specified fade operation */
+      if (fade_type & fade_type_skip)
+       fade_type_skip = FADE_TYPE_NONE;
 
-    ClearRectangle(window, REAL_SX,REAL_SY,FULL_SXSIZE,FULL_SYSIZE);
-    FlushDisplay();
+      return;
+    }
 
-    for (i = 0; i < 2 * FULL_SYSIZE; i++)
+    if (fading.fade_mode & FADE_TYPE_TRANSFORM)
     {
-      for (y = 0; y < FULL_SYSIZE; y++)
-      {
-       BlitBitmap(backbuffer, window,
-                  REAL_SX,REAL_SY+i, FULL_SXSIZE,1, REAL_SX,REAL_SY+i);
-      }
-      FlushDisplay();
-      Delay(10);
+      FadeCrossSaveBackbuffer();
+
+      return;
     }
-#endif
+  }
 
+  redraw_mask |= fade_mask;
+
+  if (fade_type == FADE_TYPE_SKIP)
+  {
 #if 0
-    for (i = 1; i < FULL_SYSIZE; i+=2)
-      BlitBitmap(backbuffer, window,
-                REAL_SX,REAL_SY+i, FULL_SXSIZE,1, REAL_SX,REAL_SY+i);
-    FlushDisplay();
-    Delay(fading_delay);
+    printf("::: will skip %d ... [%d]\n", fade_mode, fade_type_skip);
 #endif
 
-#if 0
-    SetClipOrigin(clip_gc[PIX_FADEMASK], 0, 0);
-    BlitBitmapMasked(backbuffer, window,
-                    REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
-                    REAL_SX,REAL_SY);
-    FlushDisplay();
-    Delay(fading_delay);
-
-    SetClipOrigin(clip_gc[PIX_FADEMASK], -1, -1);
-    BlitBitmapMasked(backbuffer, window,
-                    REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
-                    REAL_SX,REAL_SY);
-    FlushDisplay();
-    Delay(fading_delay);
-
-    SetClipOrigin(clip_gc[PIX_FADEMASK], 0, -1);
-    BlitBitmapMasked(backbuffer, window,
-                    REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
-                    REAL_SX,REAL_SY);
-    FlushDisplay();
-    Delay(fading_delay);
-
-    SetClipOrigin(clip_gc[PIX_FADEMASK], -1, 0);
-    BlitBitmapMasked(backbuffer, window,
-                    REAL_SX,REAL_SY, FULL_SXSIZE,FULL_SYSIZE,
-                    REAL_SX,REAL_SY);
-    FlushDisplay();
-    Delay(fading_delay);
+    fade_type_skip = fade_mode;
 
-    redraw_mask &= ~REDRAW_MAIN;
+    return;
   }
+
+  if (fade_type_skip != FADE_TYPE_NONE)
+  {
+#if 0
+    printf("::: skipping %d ... [%d]\n", fade_mode, fade_type_skip);
 #endif
 
-  BackToFront();
-}
+    /* skip all fade operations until specified fade operation */
+    if (fade_type & fade_type_skip)
+      fade_type_skip = FADE_TYPE_NONE;
 
-void FadeIn(int fade_delay)
-{
-  if (fade_delay == 0)
+    return;
+  }
+
+#if 1
+  if (global.autoplay_leveldir)
+  {
+    // fading.fade_mode = FADE_MODE_NONE;
+
+    return;
+  }
+#endif
+
+#if 0
+  if (fading.fade_mode == FADE_MODE_NONE)
   {
     BackToFront();
 
     return;
   }
+#endif
 
-  FadeScreen(NULL, FADE_MODE_FADE_IN, fade_delay, 0);
+  /* !!! what abount fade_mask == REDRAW_FIELD | REDRAW_ALL ??? !!! */
 
-  redraw_mask = REDRAW_NONE;
-}
+#if 0
+  printf("::: NOW FADING %d ... [%d]\n", fade_mode, fade_type);
+#endif
 
-void FadeOut(int fade_delay, int post_delay)
-{
-  if (fade_delay == 0)
+#if 0
+  if (fade_mask == REDRAW_NONE)
+    fade_mask = REDRAW_FIELD;
+#endif
+
+  // if (fade_mask & REDRAW_FIELD)
+  if (fade_mask == REDRAW_FIELD)
   {
-    ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
+    x = REAL_SX;
+    y = REAL_SY;
+    width  = FULL_SXSIZE;
+    height = FULL_SYSIZE;
+
+    fade_delay = fading.fade_delay;
+    post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
+
+    if (border.draw_masked_when_fading)
+      draw_border_function = DrawMaskedBorder_FIELD;   /* update when fading */
+    else
+      DrawMaskedBorder_FIELD();                                /* draw once */
+  }
+  else         /* REDRAW_ALL */
+  {
+    x = 0;
+    y = 0;
+    width  = WIN_XSIZE;
+    height = WIN_YSIZE;
+
+    fade_delay = fading.fade_delay;
+    post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
+  }
+
+#if 1
+  if (!setup.fade_screens ||
+      fade_delay == 0 ||
+      fading.fade_mode == FADE_MODE_NONE)
+#else
+  if (!setup.fade_screens || fade_delay == 0)
+#endif
+  {
+    if (fade_mode == FADE_MODE_FADE_OUT)
+      return;
+
+#if 0
+    if (fade_mode == FADE_MODE_FADE_OUT &&
+       fading.fade_mode != FADE_MODE_NONE)
+      ClearRectangle(backbuffer, x, y, width, height);
+#endif
+
+#if 1
+    BlitBitmap(backbuffer, window, x, y, width, height, x, y);
+    redraw_mask = REDRAW_NONE;
+#else
     BackToFront();
+#endif
 
     return;
   }
 
-  FadeScreen(NULL, FADE_MODE_FADE_OUT, fade_delay, post_delay);
+  FadeRectangle(bitmap, x, y, width, height, fade_mode, fade_delay, post_delay,
+               draw_border_function);
 
-  redraw_mask = REDRAW_NONE;
+  redraw_mask &= ~fade_mask;
 }
 
-void FadeCross(int fade_delay)
+void FadeIn(int fade_mask)
 {
-  if (fade_delay == 0)
-  {
-    BlitBitmap(bitmap_db_title, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
-    BackToFront();
+  if (fading.fade_mode & FADE_TYPE_TRANSFORM)
+    FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
+  else
+    FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
+}
 
-    return;
-  }
+void FadeOut(int fade_mask)
+{
+  if (fading.fade_mode & FADE_TYPE_TRANSFORM)
+    FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
+  else
+    FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
 
-  FadeScreen(bitmap_db_title, FADE_MODE_CROSSFADE, fade_delay, 0);
+  global.border_status = game_status;
+}
 
-  redraw_mask = REDRAW_NONE;
+static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
+{
+  static struct TitleFadingInfo fading_leave_stored;
+
+  if (set)
+    fading_leave_stored = fading_leave;
+  else
+    fading = fading_leave_stored;
+}
+
+void FadeSetEnterMenu()
+{
+  fading = menu.enter_menu;
+
+#if 0
+  printf("::: storing enter_menu\n");
+#endif
+
+  FadeSetLeaveNext(fading, TRUE);      /* (keep same fade mode) */
+}
+
+void FadeSetLeaveMenu()
+{
+  fading = menu.leave_menu;
+
+#if 0
+  printf("::: storing leave_menu\n");
+#endif
+
+  FadeSetLeaveNext(fading, TRUE);      /* (keep same fade mode) */
+}
+
+void FadeSetEnterScreen()
+{
+  fading = menu.enter_screen[game_status];
+
+#if 0
+  printf("::: storing leave_screen[%d]\n", game_status);
+#endif
+
+  FadeSetLeaveNext(menu.leave_screen[game_status], TRUE);      /* store */
+}
+
+void FadeSetNextScreen()
+{
+  fading = menu.next_screen;
+
+#if 0
+  printf("::: storing next_screen\n");
+#endif
+
+  // (do not overwrite fade mode set by FadeSetEnterScreen)
+  // FadeSetLeaveNext(fading, TRUE);   /* (keep same fade mode) */
+}
+
+void FadeSetLeaveScreen()
+{
+#if 0
+  printf("::: recalling last stored value\n");
+#endif
+
+  FadeSetLeaveNext(menu.leave_screen[game_status], FALSE);     /* recall */
+}
+
+void FadeSetFromType(int type)
+{
+  if (type & TYPE_ENTER_SCREEN)
+    FadeSetEnterScreen();
+  else if (type & TYPE_ENTER)
+    FadeSetEnterMenu();
+  else if (type & TYPE_LEAVE)
+    FadeSetLeaveMenu();
+}
+
+void FadeSetDisabled()
+{
+  static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
+
+  fading = fading_none;
+}
+
+void FadeSkipNextFadeIn()
+{
+  FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
+}
+
+void FadeSkipNextFadeOut()
+{
+  FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
+}
+
+void SetWindowBackgroundImageIfDefined(int graphic)
+{
+  if (graphic_info[graphic].bitmap)
+    SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
 }
 
 void SetMainBackgroundImageIfDefined(int graphic)
 {
   if (graphic_info[graphic].bitmap)
-    SetMainBackgroundImage(graphic);
+    SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
+}
+
+void SetDoorBackgroundImageIfDefined(int graphic)
+{
+  if (graphic_info[graphic].bitmap)
+    SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
+}
+
+void SetWindowBackgroundImage(int graphic)
+{
+  SetWindowBackgroundBitmap(graphic == IMG_UNDEFINED ? NULL :
+                           graphic_info[graphic].bitmap ?
+                           graphic_info[graphic].bitmap :
+                           graphic_info[IMG_BACKGROUND].bitmap);
 }
 
 void SetMainBackgroundImage(int graphic)
@@ -480,17 +742,54 @@ void SetDoorBackgroundImage(int graphic)
                          graphic_info[IMG_BACKGROUND].bitmap);
 }
 
-void DrawBackground(int dst_x, int dst_y, int width, int height)
+void SetPanelBackground()
 {
-  ClearRectangleOnBackground(backbuffer, dst_x, dst_y, width, height);
+  BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, bitmap_db_panel,
+             DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, 0, 0);
+
+  SetDoorBackgroundBitmap(bitmap_db_panel);
+}
+
+void DrawBackground(int x, int y, int width, int height)
+{
+  /* !!! "drawto" might still point to playfield buffer here (see below) !!! */
+  /* (when entering hall of fame after playing) */
+#if 0
+  ClearRectangleOnBackground(drawto, x, y, width, height);
+#else
+  ClearRectangleOnBackground(backbuffer, x, y, width, height);
+#endif
 
   redraw_mask |= REDRAW_FIELD;
 }
 
-void ClearWindow()
+void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
 {
+  struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
+
+  if (font->bitmap == NULL)
+    return;
+
+  DrawBackground(x, y, width, height);
+}
+
+void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
+{
+  struct GraphicInfo *g = &graphic_info[graphic];
+
+  if (g->bitmap == NULL)
+    return;
+
+  DrawBackground(x, y, width, height);
+}
+
+void ClearField()
+{
+  /* !!! "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 !!! */
   if (setup.soft_scrolling && game_status == GAME_MODE_PLAYING)
   {
     ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
@@ -498,12 +797,6 @@ void ClearWindow()
   }
   else
     SetDrawtoField(DRAW_BACKBUFFER);
-
-  if (setup.direct_draw && game_status == GAME_MODE_PLAYING)
-  {
-    ClearRectangle(window, REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
-    SetDrawtoField(DRAW_DIRECT);
-  }
 }
 
 void MarkTileDirty(int x, int y)
@@ -537,6 +830,39 @@ 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)
+{
+  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 */
+  if (field[from_x][from_y] == fill_element)
+    return;
+
+  safety++;
+
+  if (safety > max_fieldx * max_fieldy)
+    Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
+
+  old_element = field[from_x][from_y];
+  field[from_x][from_y] = fill_element;
+
+  for (i = 0; i < 4; i++)
+  {
+    x = from_x + check[i][0];
+    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);
+  }
+
+  safety--;
+}
+
 void SetRandomAnimationValue(int x, int y)
 {
   gfx.anim_random_frame = GfxRandom[x][y];
@@ -548,17 +874,6 @@ inline int getGraphicAnimationFrame(int graphic, int sync_frame)
   if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
     sync_frame = FrameCounter;
 
-#if 0
-  if (graphic == element_info[EL_CUSTOM_START + 255].graphic[ACTION_DEFAULT] &&
-      sync_frame == 0 &&
-      FrameCounter > 10)
-  {
-    int x = 1 / 0;
-
-    printf("::: FOO!\n");
-  }
-#endif
-
   return getAnimationFrame(graphic_info[graphic].anim_frames,
                           graphic_info[graphic].anim_delay,
                           graphic_info[graphic].anim_mode,
@@ -566,6 +881,82 @@ inline int getGraphicAnimationFrame(int graphic, int sync_frame)
                           sync_frame);
 }
 
+void getSizedGraphicSource(int graphic, int frame, int tilesize_raw,
+                          Bitmap **bitmap, int *x, int *y)
+{
+  struct
+  {
+    int width_mult, width_div;
+    int height_mult, height_div;
+  }
+  offset_calc[6] =
+  {
+    { 15, 16,  2, 3    },      /* 1 x 1 */
+    { 7, 8,    2, 3    },      /* 2 x 2 */
+    { 3, 4,    2, 3    },      /* 4 x 4 */
+    { 1, 2,    2, 3    },      /* 8 x 8 */
+    { 0, 1,    2, 3    },      /* 16 x 16 */
+    { 0, 1,    0, 1    },      /* 32 x 32 */
+  };
+  struct GraphicInfo *g = &graphic_info[graphic];
+  Bitmap *src_bitmap = g->bitmap;
+  int tilesize = MIN(MAX(1, tilesize_raw), TILESIZE);
+  int offset_calc_pos = log_2(tilesize);
+  int width_mult  = offset_calc[offset_calc_pos].width_mult;
+  int width_div   = offset_calc[offset_calc_pos].width_div;
+  int height_mult = offset_calc[offset_calc_pos].height_mult;
+  int height_div  = offset_calc[offset_calc_pos].height_div;
+  int startx = src_bitmap->width * width_mult / width_div;
+  int starty = src_bitmap->height * height_mult / height_div;
+  int src_x = g->src_x * tilesize / TILESIZE;
+  int src_y = g->src_y * tilesize / TILESIZE;
+  int width = g->width * tilesize / TILESIZE;
+  int height = g->height * tilesize / TILESIZE;
+  int offset_x = g->offset_x * tilesize / TILESIZE;
+  int offset_y = g->offset_y * tilesize / TILESIZE;
+
+  if (g->offset_y == 0)                /* frames are ordered horizontally */
+  {
+    int max_width = g->anim_frames_per_line * width;
+    int pos = (src_y / height) * max_width + src_x + frame * offset_x;
+
+    src_x = pos % max_width;
+    src_y = src_y % height + pos / max_width * height;
+  }
+  else if (g->offset_x == 0)   /* frames are ordered vertically */
+  {
+    int max_height = g->anim_frames_per_line * height;
+    int pos = (src_x / width) * max_height + src_y + frame * offset_y;
+
+    src_x = src_x % width + pos / max_height * width;
+    src_y = pos % max_height;
+  }
+  else                         /* frames are ordered diagonally */
+  {
+    src_x = src_x + frame * offset_x;
+    src_y = src_y + frame * offset_y;
+  }
+
+  *bitmap = src_bitmap;
+  *x = startx + src_x;
+  *y = starty + src_y;
+}
+
+void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
+{
+#if 1
+  getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
+#else
+  struct GraphicInfo *g = &graphic_info[graphic];
+  int mini_startx = 0;
+  int mini_starty = g->bitmap->height * 2 / 3;
+
+  *bitmap = g->bitmap;
+  *x = mini_startx + g->src_x / 2;
+  *y = mini_starty + g->src_y / 2;
+#endif
+}
+
 inline void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
                                int *x, int *y, boolean get_backside)
 {
@@ -657,21 +1048,27 @@ void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY, dst_x, dst_y);
 }
 
-void DrawMiniGraphic(int x, int y, int graphic)
+void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
 {
-  DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
-  MarkTileDirty(x / 2, y / 2);
+  DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
+                     frame, tilesize);
+  MarkTileDirty(x / tilesize, y / tilesize);
 }
 
-void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
+void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
+                        int tilesize)
 {
-  struct GraphicInfo *g = &graphic_info[graphic];
-  int mini_startx = 0;
-  int mini_starty = g->bitmap->height * 2 / 3;
+  Bitmap *src_bitmap;
+  int src_x, src_y;
 
-  *bitmap = g->bitmap;
-  *x = mini_startx + g->src_x / 2;
-  *y = mini_starty + g->src_y / 2;
+  getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
+  BlitBitmap(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);
+  MarkTileDirty(x / 2, y / 2);
 }
 
 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
@@ -967,6 +1364,13 @@ void DrawLevelFieldThruMask(int x, int y)
   DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
 }
 
+/* !!! 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) ||                        \
+                            (e) == EL_QUICKSAND_EMPTYING ||            \
+                            (e) == EL_QUICKSAND_FAST_EMPTYING))
+
 static void DrawLevelFieldCrumbledSandExt(int x, int y, int graphic, int frame)
 {
   Bitmap *src_bitmap;
@@ -989,7 +1393,11 @@ static void DrawLevelFieldCrumbledSandExt(int x, int y, int graphic, int frame)
   element = TILE_GFX_ELEMENT(x, y);
 
   /* crumble field itself */
+#if 1
+  if (IS_CRUMBLED_TILE(x, y, element))
+#else
   if (GFX_CRUMBLED(element) && !IS_MOVING(x, y))
+#endif
   {
     if (!IN_SCR_FIELD(sx, sy))
       return;
@@ -1005,8 +1413,13 @@ static void DrawLevelFieldCrumbledSandExt(int x, int y, int graphic, int frame)
                 BorderElement);
 
       /* check if neighbour field is of same type */
+#if 1
+      if (IS_CRUMBLED_TILE(xx, yy, element))
+       continue;
+#else
       if (GFX_CRUMBLED(element) && !IS_MOVING(xx, yy))
        continue;
+#endif
 
       if (i == 1 || i == 2)
       {
@@ -1039,33 +1452,30 @@ static void DrawLevelFieldCrumbledSandExt(int x, int y, int graphic, int frame)
       int syy = sy + xy[i][1];
 
 #if 1
+      if (!IN_LEV_FIELD(xx, yy) ||
+         !IN_SCR_FIELD(sxx, syy))
+       continue;
+#else
       if (!IN_LEV_FIELD(xx, yy) ||
          !IN_SCR_FIELD(sxx, syy) ||
          IS_MOVING(xx, yy))
        continue;
+#endif
 
-#if 1
       if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
        continue;
-#endif
 
       element = TILE_GFX_ELEMENT(xx, yy);
 
-      if (!GFX_CRUMBLED(element))
+#if 1
+      if (!IS_CRUMBLED_TILE(xx, yy, element))
        continue;
 #else
-      if (!IN_LEV_FIELD(xx, yy) ||
-         !IN_SCR_FIELD(sxx, syy) ||
-         !GFX_CRUMBLED(Feld[xx][yy]) ||
-         IS_MOVING(xx, yy))
+      if (!GFX_CRUMBLED(element))
        continue;
 #endif
 
-#if 1
       graphic = el_act2crm(element, ACTION_DEFAULT);
-#else
-      graphic = el_act2crm(Feld[xx][yy], ACTION_DEFAULT);
-#endif
       crumbled_border_size = graphic_info[graphic].border_size;
 
       getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
@@ -1217,6 +1627,7 @@ void DrawScreenField(int x, int y)
       element = getBorderElement(lx, ly);
 
     DrawScreenElement(x, y, element);
+
     return;
   }
 
@@ -1229,17 +1640,35 @@ void DrawScreenField(int x, int y)
     boolean cut_mode = NO_CUTTING;
 
     if (element == EL_QUICKSAND_EMPTYING ||
+       element == EL_QUICKSAND_FAST_EMPTYING ||
        element == EL_MAGIC_WALL_EMPTYING ||
        element == EL_BD_MAGIC_WALL_EMPTYING ||
+       element == EL_DC_MAGIC_WALL_EMPTYING ||
        element == EL_AMOEBA_DROPPING)
       cut_mode = CUT_ABOVE;
     else if (element == EL_QUICKSAND_FILLING ||
+            element == EL_QUICKSAND_FAST_FILLING ||
             element == EL_MAGIC_WALL_FILLING ||
-            element == EL_BD_MAGIC_WALL_FILLING)
+            element == EL_BD_MAGIC_WALL_FILLING ||
+            element == EL_DC_MAGIC_WALL_FILLING)
       cut_mode = CUT_BELOW;
 
+#if 0
+    if (lx == 9 && ly == 1)
+      printf("::: %s [%d] [%d, %d] [%d]\n",
+            EL_NAME(TILE_GFX_ELEMENT(lx, ly)),
+            el_act2crm(TILE_GFX_ELEMENT(lx, ly), ACTION_DEFAULT),
+            element_info[EL_QUICKSAND_EMPTYING].graphic[ACTION_DEFAULT],
+            element_info[EL_QUICKSAND_EMPTYING].crumbled[ACTION_DEFAULT],
+            GFX_CRUMBLED(TILE_GFX_ELEMENT(lx, ly)));
+#endif
+
     if (cut_mode == CUT_ABOVE)
+#if 1
+      DrawScreenElement(x, y, element);
+#else
       DrawScreenElementShifted(x, y, 0, 0, element, NO_CUTTING);
+#endif
     else
       DrawScreenElement(x, y, EL_EMPTY);
 
@@ -1248,8 +1677,16 @@ void DrawScreenField(int x, int y)
     else if (cut_mode == NO_CUTTING)
       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
     else
+    {
       DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
 
+#if 1
+      if (cut_mode == CUT_BELOW &&
+         IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
+       DrawLevelElement(lx, ly + 1, element);
+#endif
+    }
+
     if (content == EL_ACID)
     {
       int dir = MovDir[lx][ly];
@@ -1277,8 +1714,10 @@ void DrawScreenField(int x, int y)
     content_old = Store[oldx][oldy];
 
     if (element_old == EL_QUICKSAND_EMPTYING ||
+       element_old == EL_QUICKSAND_FAST_EMPTYING ||
        element_old == EL_MAGIC_WALL_EMPTYING ||
        element_old == EL_BD_MAGIC_WALL_EMPTYING ||
+       element_old == EL_DC_MAGIC_WALL_EMPTYING ||
        element_old == EL_AMOEBA_DROPPING)
       cut_mode = CUT_ABOVE;
 
@@ -1425,9 +1864,17 @@ void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
     for (yy = 0; yy < ysize; yy++) for (xx = 0; xx < xsize; xx++)
       DrawEnvelopeBackground(envelope_nr, sx,sy, xx,yy, xsize, ysize, font_nr);
 
+#if 1
+    DrawTextBuffer(SX + sx + font_width, SY + sy + font_height,
+                  level.envelope[envelope_nr].text, font_nr, max_xsize,
+                  xsize - 2, ysize - 2, mask_mode,
+                  level.envelope[envelope_nr].autowrap,
+                  level.envelope[envelope_nr].centered, FALSE);
+#else
     DrawTextToTextArea(SX + sx + font_width, SY + sy + font_height,
                       level.envelope[envelope_nr].text, font_nr, max_xsize,
                       xsize - 2, ysize - 2, mask_mode);
+#endif
 
     redraw_mask |= REDRAW_FIELD | REDRAW_FROM_BACKBUFFER;
     BackToFront();
@@ -1480,36 +1927,28 @@ void ShowEnvelope(int envelope_nr)
   BackToFront();
 }
 
-void getMicroGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
-{
-  Bitmap *src_bitmap = graphic_info[graphic].bitmap;
-  int mini_startx = src_bitmap->width * 3 / 4;
-  int mini_starty = src_bitmap->height * 2 / 3;
-  int src_x = mini_startx + graphic_info[graphic].src_x / 8;
-  int src_y = mini_starty + graphic_info[graphic].src_y / 8;
-
-  *bitmap = src_bitmap;
-  *x = src_x;
-  *y = src_y;
-}
-
-void DrawMicroElement(int xpos, int ypos, int element)
+void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
 {
   Bitmap *src_bitmap;
   int src_x, src_y;
   int graphic = el2preimg(element);
 
-  getMicroGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
-  BlitBitmap(src_bitmap, drawto, src_x, src_y, MICRO_TILEX, MICRO_TILEY,
-            xpos, ypos);
+  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 x,y;
 
+#if 1
+  SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
+  SetDrawBackgroundMask(REDRAW_FIELD);
+#else
   SetDrawBackgroundMask(REDRAW_NONE);
-  ClearWindow();
+#endif
+
+  ClearField();
 
   for (x = BX1; x <= BX2; x++)
     for (y = BY1; y <= BY2; y++)
@@ -1529,33 +1968,36 @@ void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
   redraw_mask |= REDRAW_FIELD;
 }
 
-static void DrawMicroLevelExt(int xpos, int ypos, int from_x, int from_y)
+static void DrawPreviewLevelExt(int from_x, int from_y)
 {
+  boolean show_level_border = (BorderElement != EL_EMPTY);
+  int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
+  int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
+  int tile_size = preview.tile_size;
+  int preview_width  = preview.xsize * tile_size;
+  int preview_height = preview.ysize * tile_size;
+  int real_preview_xsize = MIN(level_xsize, preview.xsize);
+  int real_preview_ysize = MIN(level_ysize, preview.ysize);
+  int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
+  int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
   int x, y;
 
-  DrawBackground(xpos, ypos, MICROLEVEL_XSIZE, MICROLEVEL_YSIZE);
-
-  if (lev_fieldx < STD_LEV_FIELDX)
-    xpos += (STD_LEV_FIELDX - lev_fieldx) / 2 * MICRO_TILEX;
-  if (lev_fieldy < STD_LEV_FIELDY)
-    ypos += (STD_LEV_FIELDY - lev_fieldy) / 2 * MICRO_TILEY;
+  DrawBackground(dst_x, dst_y, preview_width, preview_height);
 
-  xpos += MICRO_TILEX;
-  ypos += MICRO_TILEY;
+  dst_x += (preview_width  - real_preview_xsize * tile_size) / 2;
+  dst_y += (preview_height - real_preview_ysize * tile_size) / 2;
 
-  for (x = -1; x <= STD_LEV_FIELDX; x++)
+  for (x = 0; x < real_preview_xsize; x++)
   {
-    for (y = -1; y <= STD_LEV_FIELDY; y++)
+    for (y = 0; y < real_preview_ysize; y++)
     {
-      int lx = from_x + x, ly = from_y + y;
-
-      if (lx >= 0 && lx < lev_fieldx && ly >= 0 && ly < lev_fieldy)
-       DrawMicroElement(xpos + x * MICRO_TILEX, ypos + y * MICRO_TILEY,
-                        level.field[lx][ly]);
-      else if (lx >= -1 && lx < lev_fieldx+1 && ly >= -1 && ly < lev_fieldy+1
-              && BorderElement != EL_EMPTY)
-       DrawMicroElement(xpos + x * MICRO_TILEX, ypos + y * MICRO_TILEY,
-                        getBorderElement(lx, ly));
+      int lx = from_x + x + (show_level_border ? -1 : 0);
+      int ly = from_y + y + (show_level_border ? -1 : 0);
+      int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
+                    getBorderElement(lx, ly));
+
+      DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
+                        element, tile_size);
     }
   }
 
@@ -1571,10 +2013,35 @@ static void DrawMicroLevelExt(int xpos, int ypos, int from_x, int from_y)
 #define MICROLABEL_IMPORTED_BY_HEAD    6
 #define MICROLABEL_IMPORTED_BY         7
 
-static void DrawMicroLevelLabelExt(int mode)
+static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
+{
+  int max_text_width = SXSIZE;
+  int font_width = getFontWidth(font_nr);
+
+  if (pos->align == ALIGN_CENTER)
+    max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
+  else if (pos->align == ALIGN_RIGHT)
+    max_text_width = pos->x;
+  else
+    max_text_width = SXSIZE - pos->x;
+
+  return max_text_width / font_width;
+}
+
+static void DrawPreviewLevelLabelExt(int mode)
 {
+  struct TextPosInfo *pos = &menu.main.text.level_info_2;
   char label_text[MAX_OUTPUT_LINESIZE + 1];
   int max_len_label_text;
+#if 1
+  int font_nr = pos->font;
+  int i;
+
+  if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
+      mode == MICROLABEL_IMPORTED_FROM_HEAD ||
+      mode == MICROLABEL_IMPORTED_BY_HEAD)
+    font_nr = pos->font_alt;
+#else
   int font_nr = FONT_TEXT_2;
   int i;
 
@@ -1582,8 +2049,18 @@ static void DrawMicroLevelLabelExt(int mode)
       mode == MICROLABEL_IMPORTED_FROM_HEAD ||
       mode == MICROLABEL_IMPORTED_BY_HEAD)
     font_nr = FONT_TEXT_3;
+#endif
 
+#if 1
+  max_len_label_text = getMaxTextLength(pos, font_nr);
+#else
   max_len_label_text = SXSIZE / getFontWidth(font_nr);
+#endif
+
+#if 1
+  if (pos->size != -1)
+    max_len_label_text = pos->size;
+#endif
 
   for (i = 0; i < max_len_label_text; i++)
     label_text[i] = ' ';
@@ -1591,10 +2068,14 @@ static void DrawMicroLevelLabelExt(int mode)
 
   if (strlen(label_text) > 0)
   {
+#if 1
+    DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
+#else
     int lxpos = SX + (SXSIZE - getTextWidth(label_text, font_nr)) / 2;
     int lypos = MICROLABEL2_YPOS;
 
     DrawText(lxpos, lypos, label_text, font_nr);
+#endif
   }
 
   strncpy(label_text,
@@ -1610,35 +2091,58 @@ static void DrawMicroLevelLabelExt(int mode)
 
   if (strlen(label_text) > 0)
   {
+#if 1
+    DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
+#else
     int lxpos = SX + (SXSIZE - getTextWidth(label_text, font_nr)) / 2;
     int lypos = MICROLABEL2_YPOS;
 
     DrawText(lxpos, lypos, label_text, font_nr);
+#endif
   }
 
   redraw_mask |= REDRAW_MICROLEVEL;
 }
 
-void DrawMicroLevel(int xpos, int ypos, boolean restart)
+void DrawPreviewLevel(boolean restart)
 {
   static unsigned long scroll_delay = 0;
   static unsigned long label_delay = 0;
   static int from_x, from_y, scroll_direction;
   static int label_state, label_counter;
-  int last_game_status = game_status;  /* save current game status */
+  unsigned long scroll_delay_value = preview.step_delay;
+  boolean show_level_border = (BorderElement != EL_EMPTY);
+  int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
+  int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
+  int last_game_status = game_status;          /* save current game status */
 
+#if 0
   /* force PREVIEW font on preview level */
   game_status = GAME_MODE_PSEUDO_PREVIEW;
+#endif
 
   if (restart)
   {
-    from_x = from_y = 0;
+    from_x = 0;
+    from_y = 0;
+
+    if (preview.anim_mode == ANIM_CENTERED)
+    {
+      if (level_xsize > preview.xsize)
+       from_x = (level_xsize - preview.xsize) / 2;
+      if (level_ysize > preview.ysize)
+       from_y = (level_ysize - preview.ysize) / 2;
+    }
+
+    from_x += preview.xoffset;
+    from_y += preview.yoffset;
+
     scroll_direction = MV_RIGHT;
     label_state = 1;
     label_counter = 0;
 
-    DrawMicroLevelExt(xpos, ypos, from_x, from_y);
-    DrawMicroLevelLabelExt(label_state);
+    DrawPreviewLevelExt(from_x, from_y);
+    DrawPreviewLevelLabelExt(label_state);
 
     /* initialize delay counters */
     DelayReached(&scroll_delay, 0);
@@ -1646,18 +2150,39 @@ void DrawMicroLevel(int xpos, int ypos, boolean restart)
 
     if (leveldir_current->name)
     {
+      struct TextPosInfo *pos = &menu.main.text.level_info_1;
       char label_text[MAX_OUTPUT_LINESIZE + 1];
+#if 1
+      int font_nr = pos->font;
+#else
       int font_nr = FONT_TEXT_1;
+#endif
+#if 1
+      int max_len_label_text = getMaxTextLength(pos, font_nr);
+#else
       int max_len_label_text = SXSIZE / getFontWidth(font_nr);
+#endif
+#if 0
+      int text_width;
       int lxpos, lypos;
+#endif
+
+#if 1
+      if (pos->size != -1)
+       max_len_label_text = pos->size;
+#endif
 
       strncpy(label_text, leveldir_current->name, max_len_label_text);
       label_text[max_len_label_text] = '\0';
 
+#if 1
+      DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
+#else
       lxpos = SX + (SXSIZE - getTextWidth(label_text, font_nr)) / 2;
       lypos = SY + MICROLABEL1_YPOS;
 
       DrawText(lxpos, lypos, label_text, font_nr);
+#endif
     }
 
     game_status = last_game_status;    /* restore current game status */
@@ -1665,36 +2190,51 @@ void DrawMicroLevel(int xpos, int ypos, boolean restart)
     return;
   }
 
-  /* scroll micro level, if needed */
-  if ((lev_fieldx > STD_LEV_FIELDX || lev_fieldy > STD_LEV_FIELDY) &&
-      DelayReached(&scroll_delay, MICROLEVEL_SCROLL_DELAY))
+  /* 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))
   {
     switch (scroll_direction)
     {
       case MV_LEFT:
        if (from_x > 0)
-         from_x--;
+       {
+         from_x -= preview.step_offset;
+         from_x = (from_x < 0 ? 0 : from_x);
+       }
        else
          scroll_direction = MV_UP;
        break;
 
       case MV_RIGHT:
-       if (from_x < lev_fieldx - STD_LEV_FIELDX)
-         from_x++;
+       if (from_x < level_xsize - preview.xsize)
+       {
+         from_x += preview.step_offset;
+         from_x = (from_x > level_xsize - preview.xsize ?
+                   level_xsize - preview.xsize : from_x);
+       }
        else
          scroll_direction = MV_DOWN;
        break;
 
       case MV_UP:
        if (from_y > 0)
-         from_y--;
+       {
+         from_y -= preview.step_offset;
+         from_y = (from_y < 0 ? 0 : from_y);
+       }
        else
          scroll_direction = MV_RIGHT;
        break;
 
       case MV_DOWN:
-       if (from_y < lev_fieldy - STD_LEV_FIELDY)
-         from_y++;
+       if (from_y < level_ysize - preview.ysize)
+       {
+         from_y += preview.step_offset;
+         from_y = (from_y > level_ysize - preview.ysize ?
+                   level_ysize - preview.ysize : from_y);
+       }
        else
          scroll_direction = MV_LEFT;
        break;
@@ -1703,7 +2243,7 @@ void DrawMicroLevel(int xpos, int ypos, boolean restart)
        break;
     }
 
-    DrawMicroLevelExt(xpos, ypos, from_x, from_y);
+    DrawPreviewLevelExt(from_x, from_y);
   }
 
   /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
@@ -1744,7 +2284,7 @@ void DrawMicroLevel(int xpos, int ypos, boolean restart)
       label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
                     MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
 
-    DrawMicroLevelLabelExt(label_state);
+    DrawPreviewLevelLabelExt(label_state);
   }
 
   game_status = last_game_status;      /* restore current game status */
@@ -1901,12 +2441,10 @@ void DrawPlayer(struct PlayerInfo *player)
   int last_player_frame = player->Frame;
   int frame = 0;
 
-#if 1
   /* 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;
-#endif
 
   if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
     return;
@@ -1932,10 +2470,8 @@ void DrawPlayer(struct PlayerInfo *player)
            player->is_dropping   ? ACTION_DROPPING        :
            player->is_waiting    ? player->action_waiting : ACTION_DEFAULT);
 
-#if 1
   if (player->is_waiting)
     move_dir = player->dir_waiting;
-#endif
 
   InitPlayerGfxAnimation(player, action, move_dir);
 
@@ -1979,9 +2515,6 @@ void DrawPlayer(struct PlayerInfo *player)
   if (!IN_SCR_FIELD(sx, sy))
     return;
 
-  if (setup.direct_draw)
-    SetDrawtoField(DRAW_BUFFERED);
-
   /* ----------------------------------------------------------------------- */
   /* draw things behind the player, if needed                                */
   /* ----------------------------------------------------------------------- */
@@ -2011,8 +2544,15 @@ void DrawPlayer(struct PlayerInfo *player)
       GfxElement[jx][jy] = EL_UNDEFINED;
 
       /* make sure that pushed elements are drawn with correct frame rate */
+#if 1
+      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;
+#else
       if (player->is_pushing && player->is_moving)
        GfxFrame[jx][jy] = player->StepFrame;
+#endif
 
       DrawLevelField(jx, jy);
     }
@@ -2072,15 +2612,26 @@ void DrawPlayer(struct PlayerInfo *player)
     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 */
+    {
       element = Feld[next_jx][next_jy];
+      gfx_frame = GfxFrame[next_jx][next_jy];
+    }
 
     graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
+
+#if 1
+    sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
+    frame = getGraphicAnimationFrame(graphic, sync_frame);
+#else
     frame = getGraphicAnimationFrame(graphic, player->StepFrame);
+#endif
 
     /* draw background element under pushed element (like the Sokoban field) */
     if (Back[next_jx][next_jy])
@@ -2142,18 +2693,6 @@ void DrawPlayer(struct PlayerInfo *player)
       DrawLevelFieldThruMask(jx, jy);
   }
 
-  if (setup.direct_draw)
-  {
-    int dst_x = SX + SCREENX(MIN(jx, last_jx)) * TILEX;
-    int dst_y = SY + SCREENY(MIN(jy, last_jy)) * TILEY;
-    int x_size = TILEX * (1 + ABS(jx - last_jx));
-    int y_size = TILEY * (1 + ABS(jy - last_jy));
-
-    BlitBitmap(drawto_field, window,
-              dst_x, dst_y, x_size, y_size, dst_x, dst_y);
-    SetDrawtoField(DRAW_DIRECT);
-  }
-
   MarkTileDirty(sx, sy);
 }
 
@@ -2169,6 +2708,10 @@ void WaitForEventToContinue()
 
   button_status = MB_RELEASED;
 
+#if 1
+  ClearEventQueue();
+#endif
+
   while (still_wait)
   {
     if (PendingEvent())
@@ -2226,7 +2769,11 @@ boolean Request(char *text, unsigned int req_state)
     if (max_word_len > MAX_REQUEST_LINE_FONT1_LEN)
     {
       max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
+#if 1
+      font_nr = FONT_TEXT_1;
+#else
       font_nr = FONT_LEVEL_NUMBER;
+#endif
 
       break;
     }
@@ -2268,12 +2815,16 @@ boolean Request(char *text, unsigned int req_state)
               DOOR_GFX_PAGEX2, DOOR_GFX_PAGEY1);
   }
 
+#if 1
+  SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
+#endif
+
   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
 
   /* clear door drawing field */
   DrawBackground(DX, DY, DXSIZE, DYSIZE);
 
-  /* force DOOR font on preview level */
+  /* force DOOR font inside door area */
   game_status = GAME_MODE_PSEUDO_DOOR;
 
   /* write text for request */
@@ -2337,7 +2888,15 @@ boolean Request(char *text, unsigned int req_state)
 
   if (!(req_state & REQUEST_WAIT_FOR_INPUT))
   {
-    SetDrawBackgroundMask(REDRAW_FIELD);
+    if (game_status == GAME_MODE_PLAYING)
+    {
+      SetPanelBackground();
+      SetDrawBackgroundMask(REDRAW_DOOR_1);
+    }
+    else
+    {
+      SetDrawBackgroundMask(REDRAW_FIELD);
+    }
 
     return FALSE;
   }
@@ -2359,7 +2918,7 @@ boolean Request(char *text, unsigned int req_state)
 
       NextEvent(&event);
 
-      switch(event.type)
+      switch (event.type)
       {
        case EVENT_BUTTONPRESS:
        case EVENT_BUTTONRELEASE:
@@ -2391,7 +2950,7 @@ boolean Request(char *text, unsigned int req_state)
          /* this sets 'request_gadget_id' */
          HandleGadgets(mx, my, button_status);
 
-         switch(request_gadget_id)
+         switch (request_gadget_id)
          {
            case TOOL_CTRL_ID_YES:
              result = TRUE;
@@ -2424,8 +2983,13 @@ boolean Request(char *text, unsigned int req_state)
        }
 
        case EVENT_KEYPRESS:
-         switch(GetEventKey((KeyEvent *)&event, TRUE))
+         switch (GetEventKey((KeyEvent *)&event, TRUE))
          {
+           case KSYM_space:
+             if (req_state & REQ_CONFIRM)
+               result = 1;
+             break;
+
            case KSYM_Return:
              result = 1;
              break;
@@ -2437,6 +3001,7 @@ boolean Request(char *text, unsigned int req_state)
            default:
              break;
          }
+
          if (req_state & REQ_PLAYER)
            result = 0;
          break;
@@ -2460,10 +3025,35 @@ boolean Request(char *text, unsigned int req_state)
        result = 0;
     }
 
+#if 1
+
+    if (game_status == GAME_MODE_PLAYING && local_player->LevelSolved_GameEnd)
+    {
+      HandleGameActions();
+    }
+    else
+    {
+      DoAnimation();
+
+      if (!PendingEvent())     /* delay only if no pending events */
+       Delay(10);
+    }
+
+    BackToFront();
+
+#else
+
     DoAnimation();
 
+#if 1
+    if (!PendingEvent())       /* delay only if no pending events */
+      Delay(10);
+#else
     /* don't eat all CPU time */
     Delay(10);
+#endif
+
+#endif
   }
 
   if (game_status != GAME_MODE_MAIN)
@@ -2482,7 +3072,15 @@ boolean Request(char *text, unsigned int req_state)
 
   RemapAllGadgets();
 
-  SetDrawBackgroundMask(REDRAW_FIELD);
+  if (game_status == GAME_MODE_PLAYING)
+  {
+    SetPanelBackground();
+    SetDrawBackgroundMask(REDRAW_DOOR_1);
+  }
+  else
+  {
+    SetDrawBackgroundMask(REDRAW_FIELD);
+  }
 
 #if defined(NETWORK_AVALIABLE)
   /* continue network game after request */
@@ -2596,7 +3194,7 @@ unsigned int MoveDoor(unsigned int door_state)
 
   if (setup.quick_doors)
   {
-    stepsize = 20;             /* must be choosen to always draw last frame */
+    stepsize = 20;             /* must be chosen to always draw last frame */
     door_delay_value = 0;
   }
 
@@ -2606,6 +3204,11 @@ unsigned int MoveDoor(unsigned int door_state)
     door_state &= ~DOOR_CLOSE_ALL;
   }
 
+#if 1
+  if (game_status == GAME_MODE_EDITOR)
+    door_state |= DOOR_NO_DELAY;
+#endif
+
   if (door_state & DOOR_ACTION)
   {
     boolean handle_door_1 = (door_state & DOOR_ACTION_1);
@@ -2621,17 +3224,8 @@ unsigned int MoveDoor(unsigned int door_state)
     int door_size     = (handle_door_1 ? door_size_1     : door_size_2);
     int max_door_size = (handle_door_1 ? max_door_size_1 : max_door_size_2);
     int door_skip = max_door_size - door_size;
-#if 1
     int end = door_size;
-#else
-    int end = (door_state & DOOR_ACTION_1 && door_1.anim_mode & ANIM_VERTICAL ?
-              DYSIZE : DXSIZE);
-#endif
-#if 1
     int start = ((door_state & DOOR_NO_DELAY) ? end : 0);
-#else
-    int start = ((door_state & DOOR_NO_DELAY) ? end : offset_skip);
-#endif
     int k;
 
     if (!(door_state & DOOR_NO_DELAY) && !setup.quick_doors)
@@ -2743,15 +3337,9 @@ unsigned int MoveDoor(unsigned int door_state)
 
       if (door_state & DOOR_ACTION_2)
       {
-#if 1
        int a = MIN(x * door_2.step_offset, door_size);
        int p = (door_state & DOOR_OPEN_2 ? door_size - a : a);
        int i = p + door_skip;
-#else
-       int a = MIN(x * door_2.step_offset, door_size_2);
-       int p = (door_state & DOOR_OPEN_2 ? door_size_2 - a : a);
-       int i = p + door_skip;
-#endif
 
        if (door_2.anim_mode & ANIM_STATIC_PANEL)
        {
@@ -3020,6 +3608,7 @@ void CreateToolButtons()
                      GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
                      GDI_DECORATION_SIZE, MINI_TILEX, MINI_TILEY,
                      GDI_DECORATION_SHIFTING, 1, 1,
+                     GDI_DIRECT_DRAW, FALSE,
                      GDI_EVENT_MASK, event_mask,
                      GDI_CALLBACK_ACTION, HandleToolButtons,
                      GDI_END);
@@ -4182,19 +4771,19 @@ em_object_mapping_list[] =
   },
   {
     Xexit,                             TRUE,   FALSE,
-    EL_EXIT_CLOSED,                    -1, -1
+    EL_EM_EXIT_CLOSED,                 -1, -1
   },
   {
     Xexit_1,                           TRUE,   FALSE,
-    EL_EXIT_OPEN,                      -1, -1
+    EL_EM_EXIT_OPEN,                   -1, -1
   },
   {
     Xexit_2,                           FALSE,  FALSE,
-    EL_EXIT_OPEN,                      -1, -1
+    EL_EM_EXIT_OPEN,                   -1, -1
   },
   {
     Xexit_3,                           FALSE,  FALSE,
-    EL_EXIT_OPEN,                      -1, -1
+    EL_EM_EXIT_OPEN,                   -1, -1
   },
   {
     Xdynamite,                         TRUE,   FALSE,
@@ -4268,6 +4857,32 @@ em_object_mapping_list[] =
     Xsand_stonein_4,                   FALSE,  TRUE,
     EL_ROCK,                           ACTION_FILLING, -1
   },
+#if 1
+  {
+    Xsand_stonesand_1,                 FALSE,  FALSE,
+    EL_QUICKSAND_EMPTYING,             -1, -1
+  },
+  {
+    Xsand_stonesand_2,                 FALSE,  FALSE,
+    EL_QUICKSAND_EMPTYING,             -1, -1
+  },
+  {
+    Xsand_stonesand_3,                 FALSE,  FALSE,
+    EL_QUICKSAND_EMPTYING,             -1, -1
+  },
+  {
+    Xsand_stonesand_4,                 FALSE,  FALSE,
+    EL_QUICKSAND_EMPTYING,             -1, -1
+  },
+  {
+    Xsand_stonesand_quickout_1,                FALSE,  FALSE,
+    EL_QUICKSAND_EMPTYING,             -1, -1
+  },
+  {
+    Xsand_stonesand_quickout_2,                FALSE,  FALSE,
+    EL_QUICKSAND_EMPTYING,             -1, -1
+  },
+#else
   {
     Xsand_stonesand_1,                 FALSE,  FALSE,
     EL_QUICKSAND_FULL,                 -1, -1
@@ -4284,6 +4899,7 @@ em_object_mapping_list[] =
     Xsand_stonesand_4,                 FALSE,  FALSE,
     EL_QUICKSAND_FULL,                 -1, -1
   },
+#endif
   {
     Xsand_stoneout_1,                  FALSE,  FALSE,
     EL_ROCK,                           ACTION_EMPTYING, -1
@@ -4292,6 +4908,24 @@ em_object_mapping_list[] =
     Xsand_stoneout_2,                  FALSE,  FALSE,
     EL_ROCK,                           ACTION_EMPTYING, -1
   },
+#if 1
+  {
+    Xsand_sandstone_1,                 FALSE,  FALSE,
+    EL_QUICKSAND_FILLING,              -1, -1
+  },
+  {
+    Xsand_sandstone_2,                 FALSE,  FALSE,
+    EL_QUICKSAND_FILLING,              -1, -1
+  },
+  {
+    Xsand_sandstone_3,                 FALSE,  FALSE,
+    EL_QUICKSAND_FILLING,              -1, -1
+  },
+  {
+    Xsand_sandstone_4,                 FALSE,  FALSE,
+    EL_QUICKSAND_FILLING,              -1, -1
+  },
+#else
   {
     Xsand_sandstone_1,                 FALSE,  FALSE,
     EL_QUICKSAND_FULL,                 -1, -1
@@ -4308,6 +4942,7 @@ em_object_mapping_list[] =
     Xsand_sandstone_4,                 FALSE,  FALSE,
     EL_QUICKSAND_FULL,                 -1, -1
   },
+#endif
   {
     Xplant,                            TRUE,   FALSE,
     EL_EMC_PLANT,                      -1, -1
@@ -5099,14 +5734,18 @@ int map_direction_EM_to_RND(int direction)
 
 int get_next_element(int element)
 {
-  switch(element)
+  switch (element)
   {
     case EL_QUICKSAND_FILLING:         return EL_QUICKSAND_FULL;
     case EL_QUICKSAND_EMPTYING:                return EL_QUICKSAND_EMPTY;
+    case EL_QUICKSAND_FAST_FILLING:    return EL_QUICKSAND_FAST_FULL;
+    case EL_QUICKSAND_FAST_EMPTYING:   return EL_QUICKSAND_FAST_EMPTY;
     case EL_MAGIC_WALL_FILLING:                return EL_MAGIC_WALL_FULL;
     case EL_MAGIC_WALL_EMPTYING:       return EL_MAGIC_WALL_ACTIVE;
     case EL_BD_MAGIC_WALL_FILLING:     return EL_BD_MAGIC_WALL_FULL;
     case EL_BD_MAGIC_WALL_EMPTYING:    return EL_BD_MAGIC_WALL_ACTIVE;
+    case EL_DC_MAGIC_WALL_FILLING:     return EL_DC_MAGIC_WALL_FULL;
+    case EL_DC_MAGIC_WALL_EMPTYING:    return EL_DC_MAGIC_WALL_ACTIVE;
     case EL_AMOEBA_DROPPING:           return EL_AMOEBA_WET;
 
     default:                           return element;
@@ -5192,55 +5831,152 @@ int el2img(int element)
   return element_info[element].graphic[ACTION_DEFAULT];
 }
 
-int el2edimg(int element)
+int el2edimg(int element)
+{
+  element = GFX_ELEMENT(element);
+
+  return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
+}
+
+int el2preimg(int element)
+{
+  element = GFX_ELEMENT(element);
+
+  return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
+}
+
+int el2panelimg(int element)
+{
+  element = GFX_ELEMENT(element);
+
+  return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
+}
+
+int font2baseimg(int font_nr)
+{
+  return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
+}
+
+int getBeltNrFromBeltElement(int element)
+{
+  return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
+         element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
+         element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
+}
+
+int getBeltNrFromBeltActiveElement(int element)
+{
+  return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
+         element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
+         element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
+}
+
+int getBeltNrFromBeltSwitchElement(int element)
+{
+  return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
+         element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
+         element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
+}
+
+int getBeltDirNrFromBeltElement(int element)
+{
+  static int belt_base_element[4] =
+  {
+    EL_CONVEYOR_BELT_1_LEFT,
+    EL_CONVEYOR_BELT_2_LEFT,
+    EL_CONVEYOR_BELT_3_LEFT,
+    EL_CONVEYOR_BELT_4_LEFT
+  };
+
+  int belt_nr = getBeltNrFromBeltElement(element);
+  int belt_dir_nr = element - belt_base_element[belt_nr];
+
+  return (belt_dir_nr % 3);
+}
+
+int getBeltDirNrFromBeltSwitchElement(int element)
 {
-  element = GFX_ELEMENT(element);
+  static int belt_base_element[4] =
+  {
+    EL_CONVEYOR_BELT_1_SWITCH_LEFT,
+    EL_CONVEYOR_BELT_2_SWITCH_LEFT,
+    EL_CONVEYOR_BELT_3_SWITCH_LEFT,
+    EL_CONVEYOR_BELT_4_SWITCH_LEFT
+  };
 
-  return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
+  int belt_nr = getBeltNrFromBeltSwitchElement(element);
+  int belt_dir_nr = element - belt_base_element[belt_nr];
+
+  return (belt_dir_nr % 3);
 }
 
-int el2preimg(int element)
+int getBeltDirFromBeltElement(int element)
 {
-  element = GFX_ELEMENT(element);
+  static int belt_move_dir[3] =
+  {
+    MV_LEFT,
+    MV_NONE,
+    MV_RIGHT
+  };
 
-  return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
+  int belt_dir_nr = getBeltDirNrFromBeltElement(element);
+
+  return belt_move_dir[belt_dir_nr];
 }
 
-int font2baseimg(int font_nr)
+int getBeltDirFromBeltSwitchElement(int element)
 {
-  return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
+  static int belt_move_dir[3] =
+  {
+    MV_LEFT,
+    MV_NONE,
+    MV_RIGHT
+  };
+
+  int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
+
+  return belt_move_dir[belt_dir_nr];
 }
 
-#if 0
-void setCenteredPlayerNr_EM(int centered_player_nr)
+int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
 {
-  game.centered_player_nr = game.centered_player_nr_next = centered_player_nr;
+  static int belt_base_element[4] =
+  {
+    EL_CONVEYOR_BELT_1_LEFT,
+    EL_CONVEYOR_BELT_2_LEFT,
+    EL_CONVEYOR_BELT_3_LEFT,
+    EL_CONVEYOR_BELT_4_LEFT
+  };
+
+  return belt_base_element[belt_nr] + belt_dir_nr;
 }
 
-int getCenteredPlayerNr_EM()
+int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
 {
-#if 0
-  if (game.centered_player_nr_next >= 0 &&
-      !native_em_level.ply[game.centered_player_nr_next]->alive)
-    game.centered_player_nr_next = game.centered_player_nr;
-#endif
+  int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
 
-  if (game.centered_player_nr != game.centered_player_nr_next)
-    game.centered_player_nr = game.centered_player_nr_next;
-
-  return game.centered_player_nr;
+  return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
 }
 
-void setSetCenteredPlayer_EM(boolean set_centered_player)
+int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
 {
-  game.set_centered_player = set_centered_player;
+  static int belt_base_element[4] =
+  {
+    EL_CONVEYOR_BELT_1_SWITCH_LEFT,
+    EL_CONVEYOR_BELT_2_SWITCH_LEFT,
+    EL_CONVEYOR_BELT_3_SWITCH_LEFT,
+    EL_CONVEYOR_BELT_4_SWITCH_LEFT
+  };
+
+  return belt_base_element[belt_nr] + belt_dir_nr;
 }
 
-boolean getSetCenteredPlayer_EM()
+int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
 {
-  return game.set_centered_player;
+  int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
+
+  return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
 }
-#endif
 
 int getNumActivePlayers_EM()
 {
@@ -5257,7 +5993,6 @@ int getNumActivePlayers_EM()
   return num_players;
 }
 
-#if 1
 int getGameFrameDelay_EM(int native_em_game_frame_delay)
 {
   int game_frame_delay_value;
@@ -5272,20 +6007,390 @@ int getGameFrameDelay_EM(int native_em_game_frame_delay)
 
   return game_frame_delay_value;
 }
-#endif
 
 unsigned int InitRND(long seed)
 {
   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
-    return InitEngineRND_EM(seed);
+    return InitEngineRandom_EM(seed);
+  else
+    return InitEngineRandom_RND(seed);
+}
+
+#if 1
+static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
+static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
+#endif
+
+inline static int get_effective_element_EM(int tile, int frame_em)
+{
+  int element             = object_mapping[tile].element_rnd;
+  int action              = object_mapping[tile].action;
+  boolean is_backside     = object_mapping[tile].is_backside;
+  boolean action_removing = (action == ACTION_DIGGING ||
+                            action == ACTION_SNAPPING ||
+                            action == ACTION_COLLECTING);
+
+  if (frame_em < 7)
+  {
+    switch (tile)
+    {
+      case Yacid_splash_eB:
+      case Yacid_splash_wB:
+       return (frame_em > 5 ? EL_EMPTY : element);
+
+      default:
+       return element;
+    }
+  }
+  else /* frame_em == 7 */
+  {
+    switch (tile)
+    {
+      case Yacid_splash_eB:
+      case Yacid_splash_wB:
+       return EL_EMPTY;
+
+      case Yemerald_stone:
+       return EL_EMERALD;
+
+      case Ydiamond_stone:
+       return EL_ROCK;
+
+      case Xdrip_stretch:
+      case Xdrip_stretchB:
+      case Ydrip_s1:
+      case Ydrip_s1B:
+      case Xball_1B:
+      case Xball_2:
+      case Xball_2B:
+      case Yball_eat:
+      case Ykey_1_eat:
+      case Ykey_2_eat:
+      case Ykey_3_eat:
+      case Ykey_4_eat:
+      case Ykey_5_eat:
+      case Ykey_6_eat:
+      case Ykey_7_eat:
+      case Ykey_8_eat:
+      case Ylenses_eat:
+      case Ymagnify_eat:
+      case Ygrass_eat:
+      case Ydirt_eat:
+      case Xsand_stonein_1:
+      case Xsand_stonein_2:
+      case Xsand_stonein_3:
+      case Xsand_stonein_4:
+       return element;
+
+      default:
+       return (is_backside || action_removing ? EL_EMPTY : element);
+    }
+  }
+}
+
+inline static boolean check_linear_animation_EM(int tile)
+{
+  switch (tile)
+  {
+    case Xsand_stonesand_1:
+    case Xsand_stonesand_quickout_1:
+    case Xsand_sandstone_1:
+    case Xsand_stonein_1:
+    case Xsand_stoneout_1:
+    case Xboom_1:
+    case Xdynamite_1:
+    case Ybug_w_n:
+    case Ybug_n_e:
+    case Ybug_e_s:
+    case Ybug_s_w:
+    case Ybug_e_n:
+    case Ybug_s_e:
+    case Ybug_w_s:
+    case Ybug_n_w:
+    case Ytank_w_n:
+    case Ytank_n_e:
+    case Ytank_e_s:
+    case Ytank_s_w:
+    case Ytank_e_n:
+    case Ytank_s_e:
+    case Ytank_w_s:
+    case Ytank_n_w:
+      return TRUE;
+  }
+
+  return FALSE;
+}
+
+inline 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 (crumbled == IMG_EMPTY_SPACE)
+    has_crumbled_graphics = FALSE;
+
+  if (has_crumbled_graphics)
+  {
+    struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
+    int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
+                                          g_crumbled->anim_delay,
+                                          g_crumbled->anim_mode,
+                                          g_crumbled->anim_start_frame,
+                                          sync_frame);
+
+    getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
+                    &g_em->crumbled_src_x, &g_em->crumbled_src_y);
+
+    g_em->crumbled_border_size = graphic_info[crumbled].border_size;
+
+    g_em->has_crumbled_graphics = TRUE;
+  }
+  else
+  {
+    g_em->crumbled_bitmap = NULL;
+    g_em->crumbled_src_x = 0;
+    g_em->crumbled_src_y = 0;
+    g_em->crumbled_border_size = 0;
+
+    g_em->has_crumbled_graphics = FALSE;
+  }
+}
+
+void ResetGfxAnimation_EM(int x, int y, int tile)
+{
+  GfxFrame[x][y] = 0;
+}
+
+void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
+                       int tile, int frame_em, int x, int y)
+{
+  int action = object_mapping[tile].action;
+#if 1
+  int direction = object_mapping[tile].direction;
+  int effective_element = get_effective_element_EM(tile, frame_em);
+  int graphic = (direction == MV_NONE ?
+                el_act2img(effective_element, action) :
+                el_act_dir2img(effective_element, action, direction));
+  struct GraphicInfo *g = &graphic_info[graphic];
+  int sync_frame;
+#endif
+  boolean action_removing = (action == ACTION_DIGGING ||
+                            action == ACTION_SNAPPING ||
+                            action == ACTION_COLLECTING);
+  boolean action_moving   = (action == ACTION_FALLING ||
+                            action == ACTION_MOVING ||
+                            action == ACTION_PUSHING ||
+                            action == ACTION_EATING ||
+                            action == ACTION_FILLING ||
+                            action == ACTION_EMPTYING);
+  boolean action_falling  = (action == ACTION_FALLING ||
+                            action == ACTION_FILLING ||
+                            action == ACTION_EMPTYING);
+
+#if 0
+  if (tile == Xsand_stonesand_1 ||
+      tile == Xsand_stonesand_2 ||
+      tile == Xsand_stonesand_3 ||
+      tile == Xsand_stonesand_4)
+    printf("::: 1: quicksand frame %d [%d]\n", GfxFrame[x][y], tile);
+#endif
+
+#if 1
+  if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
+  {
+    GfxFrame[x][y] = 0;
+
+    // printf("::: resetting... [%d]\n", tile);
+  }
+#else
+  if (action_removing || check_linear_animation_EM(tile))
+  {
+    GfxFrame[x][y] = frame_em;
+
+    // printf("::: resetting... [%d]\n", tile);
+  }
+#endif
+  else if (action_moving)
+  {
+    boolean is_backside = object_mapping[tile].is_backside;
+
+    if (is_backside)
+    {
+      int direction = object_mapping[tile].direction;
+      int move_dir = (action_falling ? MV_DOWN : direction);
+
+      GfxFrame[x][y]++;
+
+      if (move_dir == MV_LEFT)
+       GfxFrame[x - 1][y] = GfxFrame[x][y];
+      else if (move_dir == MV_RIGHT)
+       GfxFrame[x + 1][y] = GfxFrame[x][y];
+      else if (move_dir == MV_UP)
+       GfxFrame[x][y - 1] = GfxFrame[x][y];
+      else if (move_dir == MV_DOWN)
+       GfxFrame[x][y + 1] = GfxFrame[x][y];
+    }
+  }
+  else
+  {
+    GfxFrame[x][y]++;
+
+    /* 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]++;
+  }
+
+#if 0
+  if (tile == Xsand_stonesand_1 ||
+      tile == Xsand_stonesand_2 ||
+      tile == Xsand_stonesand_3 ||
+      tile == Xsand_stonesand_4)
+    printf("::: 2: quicksand frame %d [%d]\n", GfxFrame[x][y], tile);
+#endif
+
+#if 1
+  if (graphic_info[graphic].anim_global_sync)
+    sync_frame = FrameCounter;
+  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) */
+
+  SetRandomAnimationValue(x, y);
+
+  int frame = getAnimationFrame(g->anim_frames,
+                               g->anim_delay,
+                               g->anim_mode,
+                               g->anim_start_frame,
+                               sync_frame);
+
+  g_em->unique_identifier =
+    (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
+#endif
+}
+
+void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
+                                 int tile, int frame_em, int x, int y)
+{
+  int action = object_mapping[tile].action;
+  int direction = object_mapping[tile].direction;
+  int effective_element = get_effective_element_EM(tile, frame_em);
+  int graphic = (direction == MV_NONE ?
+                el_act2img(effective_element, action) :
+                el_act_dir2img(effective_element, action, direction));
+  int crumbled = (direction == MV_NONE ?
+                 el_act2crm(effective_element, action) :
+                 el_act_dir2crm(effective_element, action, direction));
+  int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
+  int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
+  boolean has_crumbled_graphics = (base_crumbled != base_graphic);
+  struct GraphicInfo *g = &graphic_info[graphic];
+#if 0
+  struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
+#endif
+  int sync_frame;
+
+#if 0
+  if (frame_em == 0)   /* reset animation frame for certain elements */
+  {
+    if (check_linear_animation_EM(tile))
+      GfxFrame[x][y] = 0;
+  }
+#endif
+
+  if (graphic_info[graphic].anim_global_sync)
+    sync_frame = FrameCounter;
+  else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
+    sync_frame = GfxFrame[x][y];
   else
-    return InitEngineRND(seed);
+    sync_frame = 0;    /* playfield border (pseudo steel) */
+
+  SetRandomAnimationValue(x, y);
+
+  int frame = getAnimationFrame(g->anim_frames,
+                               g->anim_delay,
+                               g->anim_mode,
+                               g->anim_start_frame,
+                               sync_frame);
+
+  getGraphicSourceExt(graphic, frame, &g_em->bitmap,
+                     &g_em->src_x, &g_em->src_y, FALSE);
+
+  /* (updating the "crumbled" graphic definitions is probably not really needed,
+     as animations for crumbled graphics can't be longer than one EMC cycle) */
+#if 1
+  set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
+                          sync_frame);
+
+#else
+
+  g_em->crumbled_bitmap = NULL;
+  g_em->crumbled_src_x = 0;
+  g_em->crumbled_src_y = 0;
+
+  g_em->has_crumbled_graphics = FALSE;
+
+  if (has_crumbled_graphics && crumbled != IMG_EMPTY_SPACE)
+  {
+    int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
+                                          g_crumbled->anim_delay,
+                                          g_crumbled->anim_mode,
+                                          g_crumbled->anim_start_frame,
+                                          sync_frame);
+
+    getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
+                    &g_em->crumbled_src_x, &g_em->crumbled_src_y);
+
+    g_em->has_crumbled_graphics = TRUE;
+  }
+#endif
+}
+
+void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
+                                 int player_nr, int anim, int frame_em)
+{
+  int element   = player_mapping[player_nr][anim].element_rnd;
+  int action    = player_mapping[player_nr][anim].action;
+  int direction = player_mapping[player_nr][anim].direction;
+  int graphic = (direction == MV_NONE ?
+                el_act2img(element, action) :
+                el_act_dir2img(element, action, direction));
+  struct GraphicInfo *g = &graphic_info[graphic];
+  int sync_frame;
+
+  InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
+
+  stored_player[player_nr].StepFrame = frame_em;
+
+  sync_frame = stored_player[player_nr].Frame;
+
+  int frame = getAnimationFrame(g->anim_frames,
+                               g->anim_delay,
+                               g->anim_mode,
+                               g->anim_start_frame,
+                               sync_frame);
+
+  getGraphicSourceExt(graphic, frame, &g_em->bitmap,
+                     &g_em->src_x, &g_em->src_y, FALSE);
+
+#if 0
+  printf("::: %d: %d, %d [%d]\n",
+        player_nr,
+        stored_player[player_nr].Frame,
+        stored_player[player_nr].StepFrame,
+        FrameCounter);
+#endif
 }
 
 void InitGraphicInfo_EM(void)
 {
+#if 0
   struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
   struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
+#endif
   int i, j, p;
 
 #if DEBUG_EM_GFX
@@ -5357,9 +6462,11 @@ void InitGraphicInfo_EM(void)
     int action = object_mapping[i].action;
     int direction = object_mapping[i].direction;
     boolean is_backside = object_mapping[i].is_backside;
+#if 0
     boolean action_removing = (action == ACTION_DIGGING ||
                               action == ACTION_SNAPPING ||
                               action == ACTION_COLLECTING);
+#endif
     boolean action_exploding = ((action == ACTION_EXPLODING ||
                                 action == ACTION_SMASHED_BY_ROCK ||
                                 action == ACTION_SMASHED_BY_SPRING) &&
@@ -5369,6 +6476,9 @@ void InitGraphicInfo_EM(void)
 
     for (j = 0; j < 8; j++)
     {
+#if 1
+      int effective_element = get_effective_element_EM(i, j);
+#else
       int effective_element = (j > 5 && i == Yacid_splash_eB ? EL_EMPTY :
                               j > 5 && i == Yacid_splash_wB ? EL_EMPTY :
                               j < 7 ? element :
@@ -5401,6 +6511,7 @@ void InitGraphicInfo_EM(void)
                               is_backside ? EL_EMPTY :
                               action_removing ? EL_EMPTY :
                               element);
+#endif
       int effective_action = (j < 7 ? action :
                              i == Xdrip_stretch ? action :
                              i == Xdrip_stretchB ? action :
@@ -5442,6 +6553,9 @@ void InitGraphicInfo_EM(void)
       boolean has_action_graphics = (graphic != base_graphic);
       boolean has_crumbled_graphics = (base_crumbled != base_graphic);
       struct GraphicInfo *g = &graphic_info[graphic];
+#if 0
+      struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
+#endif
       struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
       Bitmap *src_bitmap;
       int src_x, src_y;
@@ -5578,13 +6692,20 @@ void InitGraphicInfo_EM(void)
       g_em->width  = TILEX;
       g_em->height = TILEY;
 
+      g_em->preserve_background = FALSE;
+
+#if 1
+      set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
+                              sync_frame);
+
+#else
+
       g_em->crumbled_bitmap = NULL;
       g_em->crumbled_src_x = 0;
       g_em->crumbled_src_y = 0;
       g_em->crumbled_border_size = 0;
 
       g_em->has_crumbled_graphics = FALSE;
-      g_em->preserve_background = FALSE;
 
 #if 0
       if (has_crumbled_graphics && crumbled == IMG_EMPTY_SPACE)
@@ -5594,18 +6715,47 @@ void InitGraphicInfo_EM(void)
 #endif
 
       /* if element can be crumbled, but certain action graphics are just empty
-        space (like snapping sand with the original R'n'D graphics), do not
+        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 (has_crumbled_graphics && crumbled != IMG_EMPTY_SPACE)
       {
-       getGraphicSource(crumbled, frame, &src_bitmap, &src_x, &src_y);
+       int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
+                                              g_crumbled->anim_delay,
+                                              g_crumbled->anim_mode,
+                                              g_crumbled->anim_start_frame,
+                                              sync_frame);
+
+       getGraphicSource(crumbled, frame_crumbled, &src_bitmap, &src_x, &src_y);
 
        g_em->has_crumbled_graphics = TRUE;
        g_em->crumbled_bitmap = src_bitmap;
        g_em->crumbled_src_x = src_x;
        g_em->crumbled_src_y = src_y;
        g_em->crumbled_border_size = graphic_info[crumbled].border_size;
+
+
+#if 0
+       if (g_em == &graphic_info_em_object[207][0])
+         printf("... %d, %d [%d, %d, %d, %d] [%d, %d, %d, %d, %d, %d => %d]\n",
+                graphic_info_em_object[207][0].crumbled_src_x,
+                graphic_info_em_object[207][0].crumbled_src_y,
+
+                crumbled, frame, src_x, src_y,
+
+                g->anim_frames,
+                g->anim_delay,
+                g->anim_mode,
+                g->anim_start_frame,
+                sync_frame,
+                gfx.anim_random_frame,
+                frame);
+#endif
+
+#if 0
+       printf("::: EMC tile %d is crumbled\n", i);
+#endif
       }
+#endif
 
 #if 0
       if (element == EL_ROCK &&
@@ -5677,7 +6827,6 @@ void InitGraphicInfo_EM(void)
        g_em->height = TILEY - cy * step;
       }
 
-#if 1
       /* 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
@@ -5685,29 +6834,12 @@ void InitGraphicInfo_EM(void)
         bit  5 -  0 ( 6 bit): graphic height */
       g_em->unique_identifier =
        (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
-#else
-      /* create unique graphic identifier to decide if tile must be redrawn */
-      /* bit 31 - 16 (16 bit): EM style element
-        bit 15 - 12 ( 4 bit): EM style frame
-        bit 11 -  6 ( 6 bit): graphic width
-        bit  5 -  0 ( 6 bit): graphic height */
-      g_em->unique_identifier =
-       (i << 16) | (j << 12) | (g_em->width << 6) | g_em->height;
-#endif
-
-#if 0
-      if (effective_element == EL_ROCK)
-       printf("::: EL_ROCK(%d, %d): %d, %d => %d\n",
-              effective_action, j, graphic, frame, g_em->unique_identifier);
-#endif
 
 #if DEBUG_EM_GFX
 
-#if 1
       /* skip check for EMC elements not contained in original EMC artwork */
       if (element == EL_EMC_FAKE_ACID)
        continue;
-#endif
 
       if (g_em->bitmap != debug_bitmap ||
          g_em->src_x != debug_src_x ||
@@ -5781,13 +6913,8 @@ void InitGraphicInfo_EM(void)
       int action = object_mapping[i].action;
       int direction = object_mapping[i].direction;
       boolean is_backside = object_mapping[i].is_backside;
-#if 1
       int graphic_action  = el_act_dir2img(element, action, direction);
       int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
-#else
-      int graphic_action  = element_info[element].graphic[action];
-      int graphic_default = element_info[element].graphic[ACTION_DEFAULT];
-#endif
 
       if ((action == ACTION_SMASHED_BY_ROCK ||
           action == ACTION_SMASHED_BY_SPRING ||
@@ -5813,9 +6940,7 @@ void InitGraphicInfo_EM(void)
        g_em->dst_offset_y      = g_xx->dst_offset_y;
        g_em->width             = g_xx->width;
        g_em->height            = g_xx->height;
-#if 1
        g_em->unique_identifier = g_xx->unique_identifier;
-#endif
 
        if (!is_backside)
          g_em->preserve_background = TRUE;
@@ -5857,7 +6982,7 @@ void InitGraphicInfo_EM(void)
                                      g->anim_start_frame,
                                      sync_frame);
 
-       getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x,&src_y, FALSE);
+       getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
 
        g_em->bitmap = src_bitmap;
        g_em->src_x = src_x;
@@ -5871,12 +6996,10 @@ void InitGraphicInfo_EM(void)
 
 #if DEBUG_EM_GFX
 
-#if 1
        /* skip check for EMC elements not contained in original EMC artwork */
        if (element == EL_PLAYER_3 ||
            element == EL_PLAYER_4)
          continue;
-#endif
 
        if (g_em->bitmap != debug_bitmap ||
            g_em->src_x != debug_src_x ||
@@ -5930,10 +7053,8 @@ void InitGraphicInfo_EM(void)
 #endif
 }
 
-void PlayMenuSound()
+void PlayMenuSoundExt(int sound)
 {
-  int sound = menu.sound[game_status];
-
   if (sound == SND_UNDEFINED)
     return;
 
@@ -5947,6 +7068,11 @@ void PlayMenuSound()
     PlaySound(sound);
 }
 
+void PlayMenuSound()
+{
+  PlayMenuSoundExt(menu.sound[game_status]);
+}
+
 void PlayMenuSoundStereo(int sound, int stereo_position)
 {
   if (sound == SND_UNDEFINED)
@@ -5962,10 +7088,8 @@ void PlayMenuSoundStereo(int sound, int stereo_position)
     PlaySoundStereo(sound, stereo_position);
 }
 
-void PlayMenuSoundIfLoop()
+void PlayMenuSoundIfLoopExt(int sound)
 {
-  int sound = menu.sound[game_status];
-
   if (sound == SND_UNDEFINED)
     return;
 
@@ -5977,12 +7101,94 @@ void PlayMenuSoundIfLoop()
     PlaySoundLoop(sound);
 }
 
-void PlayMenuMusic()
+void PlayMenuSoundIfLoop()
 {
-  int music = menu.music[game_status];
+  PlayMenuSoundIfLoopExt(menu.sound[game_status]);
+}
 
+void PlayMenuMusicExt(int music)
+{
   if (music == MUS_UNDEFINED)
     return;
 
+  if (!setup.sound_music)
+    return;
+
   PlayMusic(music);
 }
+
+void PlayMenuMusic()
+{
+  PlayMenuMusicExt(menu.music[game_status]);
+}
+
+void PlaySoundActivating()
+{
+#if 0
+  PlaySound(SND_MENU_ITEM_ACTIVATING);
+#endif
+}
+
+void PlaySoundSelecting()
+{
+#if 0
+  PlaySound(SND_MENU_ITEM_SELECTING);
+#endif
+}
+
+void ToggleFullscreenIfNeeded()
+{
+  boolean change_fullscreen = (setup.fullscreen !=
+                              video.fullscreen_enabled);
+  boolean change_fullscreen_mode = (video.fullscreen_enabled &&
+                                   !strEqual(setup.fullscreen_mode,
+                                             video.fullscreen_mode_current));
+
+  if (!video.fullscreen_available)
+    return;
+
+#if 1
+  if (change_fullscreen || change_fullscreen_mode)
+#else
+  if (setup.fullscreen != video.fullscreen_enabled ||
+      setup.fullscreen_mode != video.fullscreen_mode_current)
+#endif
+  {
+    Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
+
+    /* save backbuffer content which gets lost when toggling fullscreen mode */
+    BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
+
+#if 1
+    if (change_fullscreen_mode)
+#else
+    if (setup.fullscreen && video.fullscreen_enabled)
+#endif
+    {
+      /* keep fullscreen, but change fullscreen mode (screen resolution) */
+#if 1
+      /* (this is now set in sdl.c) */
+#else
+      video.fullscreen_mode_current = setup.fullscreen_mode;
+#endif
+      video.fullscreen_enabled = FALSE;                /* force new fullscreen mode */
+    }
+
+    /* toggle fullscreen */
+    ChangeVideoModeIfNeeded(setup.fullscreen);
+
+    setup.fullscreen = video.fullscreen_enabled;
+
+    /* restore backbuffer content from temporary backbuffer backup bitmap */
+    BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
+
+    FreeBitmap(tmp_backbuffer);
+
+#if 1
+    /* update visible window/screen */
+    BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
+#else
+    redraw_mask = REDRAW_ALL;
+#endif
+  }
+}