added clearing player action for non-game finger release events
[rocksndiamonds.git] / src / tools.c
index 03ba73b79cb3ae617a4c5987a1f93d8a2015c0bd..3bc4a77fef5b9cd1d2f569ab1e059b42fd73a32d 100644 (file)
@@ -4,7 +4,7 @@
 // (c) 1995-2014 by Artsoft Entertainment
 //                         Holger Schemel
 //                 info@artsoft.org
-//                 http://www.artsoft.org/
+//                 https://www.artsoft.org/
 // ----------------------------------------------------------------------------
 // tools.c
 // ============================================================================
@@ -193,22 +193,6 @@ static char *print_if_not_empty(int element)
   return s;
 }
 
-int correctLevelPosX_EM(int lx)
-{
-  lx -= 1;
-  lx -= (BorderElement != EL_EMPTY ? 1 : 0);
-
-  return lx;
-}
-
-int correctLevelPosY_EM(int ly)
-{
-  ly -= 1;
-  ly -= (BorderElement != EL_EMPTY ? 1 : 0);
-
-  return ly;
-}
-
 int getFieldbufferOffsetX_RND(int dir, int pos)
 {
   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
@@ -307,8 +291,6 @@ static int getLevelFromScreenX_EM(int sx)
   int px = sx - SX;
   int lx = LEVELX((px + dx) / TILESIZE_VAR);
 
-  lx = correctLevelPosX_EM(lx);
-
   return lx;
 }
 
@@ -324,8 +306,6 @@ static int getLevelFromScreenY_EM(int sy)
   int py = sy - SY;
   int ly = LEVELY((py + dy) / TILESIZE_VAR);
 
-  ly = correctLevelPosY_EM(ly);
-
   return ly;
 }
 
@@ -417,35 +397,35 @@ void DumpTile(int x, int y)
   int sy = SCREENY(y);
   char *token_name;
 
-  printf_line("-", 79);
-  printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
-  printf_line("-", 79);
+  Info("---");
+  Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
+  Info("---");
 
   if (!IN_LEV_FIELD(x, y))
   {
-    printf("(not in level field)\n");
-    printf("\n");
+    Info("(not in level field)");
+    Info("");
 
     return;
   }
 
-  token_name = element_info[Feld[x][y]].token_name;
+  token_name = element_info[Tile[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]));
-  printf("  StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
-  printf("  MovPos:      %d\n", MovPos[x][y]);
-  printf("  MovDir:      %d\n", MovDir[x][y]);
-  printf("  MovDelay:    %d\n", MovDelay[x][y]);
-  printf("  ChangeDelay: %d\n", ChangeDelay[x][y]);
-  printf("  CustomValue: %d\n", CustomValue[x][y]);
-  printf("  GfxElement:  %d\n", GfxElement[x][y]);
-  printf("  GfxAction:   %d\n", GfxAction[x][y]);
-  printf("  GfxFrame:    %d [%d]\n", GfxFrame[x][y], FrameCounter);
-  printf("  Player x/y:  %d, %d\n", local_player->jx, local_player->jy);
-  printf("\n");
+  Info("Tile:        %d\t['%s']",      Tile[x][y], token_name);
+  Info("Back:        %s",              print_if_not_empty(Back[x][y]));
+  Info("Store:       %s",              print_if_not_empty(Store[x][y]));
+  Info("Store2:      %s",              print_if_not_empty(Store2[x][y]));
+  Info("StorePlayer: %s",              print_if_not_empty(StorePlayer[x][y]));
+  Info("MovPos:      %d",              MovPos[x][y]);
+  Info("MovDir:      %d",              MovDir[x][y]);
+  Info("MovDelay:    %d",              MovDelay[x][y]);
+  Info("ChangeDelay: %d",              ChangeDelay[x][y]);
+  Info("CustomValue: %d",              CustomValue[x][y]);
+  Info("GfxElement:  %d",              GfxElement[x][y]);
+  Info("GfxAction:   %d",              GfxAction[x][y]);
+  Info("GfxFrame:    %d [%d]",         GfxFrame[x][y], FrameCounter);
+  Info("Player x/y:  %d, %d",          local_player->jx, local_player->jy);
+  Info("");
 }
 
 void DumpTileFromScreen(int sx, int sy)
@@ -772,7 +752,7 @@ static void PrintFrameTimeDebugging(void)
 
   diff_bar[pos++] = '\0';
 
-  Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
+  Debug("time:frame", "%06d [%02d] [%c%02d] %s",
        counter,
        diff_1,
        (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
@@ -971,19 +951,13 @@ static void FadeExt(int fade_mask, int fade_mode, int fade_type)
     height = WIN_YSIZE;
   }
 
-  if (!setup.fade_screens ||
-      fade_delay == 0 ||
-      fading.fade_mode == FADE_MODE_NONE)
-  {
-    if (fade_mode == FADE_MODE_FADE_OUT)
-      return;
-
-    BlitBitmap(backbuffer, window, x, y, width, height, x, y);
-
-    redraw_mask &= ~fade_mask;
+  // when switching screens without fading, set fade delay to zero
+  if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
+    fade_delay = 0;
 
+  // do not display black frame when fading out without fade delay
+  if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
     return;
-  }
 
   FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
                draw_border_function);
@@ -999,8 +973,7 @@ static void SetScreenStates_BeforeFadingIn(void)
   global.anim_status = global.anim_status_next;
 
   // store backbuffer with all animations that will be started after fading in
-  if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
-    PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
+  PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
 
   // set screen mode for animations back to fading
   global.anim_status = GAME_MODE_PSEUDO_FADING;
@@ -1023,8 +996,7 @@ static void SetScreenStates_BeforeFadingOut(void)
   global.anim_status = GAME_MODE_PSEUDO_FADING;
 
   // store backbuffer with all animations that will be stopped for fading out
-  if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
-    PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
+  PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
 }
 
 static void SetScreenStates_AfterFadingOut(void)
@@ -1365,6 +1337,9 @@ static void RedrawGlobalBorderIfNeeded(void)
 
 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
   if (CheckIfGlobalBorderRedrawIsNeeded())
+#else
+  // determine and store new global border bitmap for current game status
+  global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
 #endif
   {
     // redraw global screen border (or clear, if defined to be empty)
@@ -1462,15 +1437,15 @@ void SetBorderElement(void)
 
   BorderElement = EL_EMPTY;
 
-  // the MM game engine does not use a visible border element
-  if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
+  // only the R'n'D game engine may use an additional steelwall border
+  if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
     return;
 
   for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
   {
     for (x = 0; x < lev_fieldx; x++)
     {
-      if (!IS_INDESTRUCTIBLE(Feld[x][y]))
+      if (!IS_INDESTRUCTIBLE(Tile[x][y]))
        BorderElement = EL_STEELWALL;
 
       if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
@@ -1496,7 +1471,7 @@ void FloodFillLevelExt(int from_x, int from_y, int fill_element,
   safety++;
 
   if (safety > max_fieldx * max_fieldy)
-    Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
+    Fail("Something went wrong in 'FloodFill()'. Please debug.");
 
   old_element = field[from_x][from_y];
   field[from_x][from_y] = fill_element;
@@ -1639,8 +1614,9 @@ void DrawGraphic(int x, int y, int graphic, int frame)
 #if DEBUG
   if (!IN_SCR_FIELD(x, y))
   {
-    printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
-    printf("DrawGraphic(): This should never happen!\n");
+    Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
+    Debug("draw:DrawGraphic", "This should never happen!");
+
     return;
   }
 #endif
@@ -1656,8 +1632,10 @@ void DrawFixedGraphic(int x, int y, int graphic, int frame)
 #if DEBUG
   if (!IN_SCR_FIELD(x, y))
   {
-    printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
-    printf("DrawGraphic(): This should never happen!\n");
+    Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
+         x, y, graphic);
+    Debug("draw:DrawFixedGraphic", "This should never happen!");
+
     return;
   }
 #endif
@@ -1693,8 +1671,10 @@ void DrawGraphicThruMask(int x, int y, int graphic, int frame)
 #if DEBUG
   if (!IN_SCR_FIELD(x, y))
   {
-    printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
-    printf("DrawGraphicThruMask(): This should never happen!\n");
+    Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
+         x, y, graphic);
+    Debug("draw:DrawGraphicThruMask", "This should never happen!");
+
     return;
   }
 #endif
@@ -1710,8 +1690,10 @@ void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
 #if DEBUG
   if (!IN_SCR_FIELD(x, y))
   {
-    printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
-    printf("DrawGraphicThruMask(): This should never happen!\n");
+    Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
+         x, y, graphic);
+    Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
+
     return;
   }
 #endif
@@ -1872,8 +1854,10 @@ static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
 #if DEBUG
   if (!IN_SCR_FIELD(x, y))
   {
-    printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
-    printf("DrawGraphicShifted(): This should never happen!\n");
+    Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
+         x, y, graphic);
+    Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
+
     return;
   }
 #endif
@@ -2025,9 +2009,9 @@ void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
   {
     boolean left_stopped = FALSE, right_stopped = FALSE;
 
-    if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
+    if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
       left_stopped = TRUE;
-    if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
+    if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
       right_stopped = TRUE;
 
     if (left_stopped && right_stopped)
@@ -2079,7 +2063,7 @@ void DrawLevelElementThruMask(int x, int y, int element)
 
 void DrawLevelFieldThruMask(int x, int y)
 {
-  DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
+  DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
 }
 
 // !!! implementation of quicksand is totally broken !!!
@@ -2286,8 +2270,8 @@ static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
        continue;
 
       // do not crumble fields that are being digged or snapped
-      if (Feld[xx][yy] == EL_EMPTY ||
-         Feld[xx][yy] == EL_ELEMENT_SNAPPING)
+      if (Tile[xx][yy] == EL_EMPTY ||
+         Tile[xx][yy] == EL_ELEMENT_SNAPPING)
        continue;
 
       element = TILE_GFX_ELEMENT(xx, yy);
@@ -2316,7 +2300,7 @@ static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
          !IN_SCR_FIELD(sxx, syy))
        continue;
 
-      if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
+      if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
        continue;
 
       element = TILE_GFX_ELEMENT(xx, yy);
@@ -2342,7 +2326,7 @@ void DrawLevelFieldCrumbled(int x, int y)
   if (!IN_LEV_FIELD(x, y))
     return;
 
-  if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
+  if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
       GfxElement[x][y] != EL_UNDEFINED &&
       GFX_CRUMBLED(GfxElement[x][y]))
   {
@@ -2391,7 +2375,7 @@ void DrawLevelFieldCrumbledNeighbours(int x, int y)
 
     if (!IN_LEV_FIELD(xx, yy) ||
        !IN_SCR_FIELD(sxx, syy) ||
-       !GFX_CRUMBLED(Feld[xx][yy]) ||
+       !GFX_CRUMBLED(Tile[xx][yy]) ||
        IS_MOVING(xx, yy))
       continue;
 
@@ -2410,7 +2394,7 @@ void DrawLevelFieldCrumbledNeighbours(int x, int y)
 
     if (!IN_LEV_FIELD(xx, yy) ||
        !IN_SCR_FIELD(sxx, syy) ||
-       !GFX_CRUMBLED(Feld[xx][yy]) ||
+       !GFX_CRUMBLED(Tile[xx][yy]) ||
        IS_MOVING(xx, yy))
       continue;
 
@@ -2475,7 +2459,7 @@ void DrawScreenField(int x, int y)
     return;
   }
 
-  element = Feld[lx][ly];
+  element = Tile[lx][ly];
   content = Store[lx][ly];
 
   if (IS_MOVING(lx, ly))
@@ -2542,7 +2526,7 @@ void DrawScreenField(int x, int y)
     horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
                  MovDir[oldx][oldy] == MV_RIGHT);
 
-    element_old = Feld[oldx][oldy];
+    element_old = Tile[oldx][oldy];
     content_old = Store[oldx][oldy];
 
     if (element_old == EL_QUICKSAND_EMPTYING ||
@@ -2695,7 +2679,7 @@ void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
     DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
-    DrawSizedElement(sx, sy, Feld[x][y], tilesize);
+    DrawSizedElement(sx, sy, Tile[x][y], tilesize);
   else
     DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
 }
@@ -2707,7 +2691,7 @@ void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
   if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
     DrawMiniElement(sx, sy, EL_EMPTY);
   else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
-    DrawMiniElement(sx, sy, Feld[x][y]);
+    DrawMiniElement(sx, sy, Tile[x][y]);
   else
     DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
 }
@@ -3793,7 +3777,7 @@ void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
   if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
     DrawLevelFieldCrumbled(x, y);
 #else
-  if (GFX_CRUMBLED(Feld[x][y]))
+  if (GFX_CRUMBLED(Tile[x][y]))
     DrawLevelFieldCrumbled(x, y);
 #endif
 }
@@ -3897,8 +3881,8 @@ static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
 #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");
+    Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
+    Debug("draw:DrawPlayerExt", "This should never happen!");
 
     draw_player[pnr] = FALSE;
 
@@ -3924,8 +3908,8 @@ static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
   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 element = Tile[jx][jy];
+  int last_element = Tile[last_jx][last_jy];
   int action = (player->is_pushing    ? ACTION_PUSHING         :
                player->is_digging    ? ACTION_DIGGING         :
                player->is_collecting ? ACTION_COLLECTING      :
@@ -4051,7 +4035,7 @@ static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
 
     if (!IS_MOVING(jx, jy))            // push movement already finished
     {
-      element = Feld[next_jx][next_jy];
+      element = Tile[next_jx][next_jy];
       gfx_frame = GfxFrame[next_jx][next_jy];
     }
 
@@ -4085,7 +4069,7 @@ static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
     // 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]);
+      DrawLevelElement(next_jx, next_jy, Tile[next_jx][next_jy]);
     else
       DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
 #else
@@ -4466,6 +4450,7 @@ static int RequestHandleEvents(unsigned int req_state)
            break;
          }
 
+         case EVENT_FINGERRELEASE:
          case EVENT_KEYRELEASE:
            ClearPlayerAction();
            break;
@@ -5367,7 +5352,7 @@ unsigned int MoveDoor(unsigned int door_state)
          if (width > door_rect->width)
            width = door_rect->width;
 
-         // printf("::: k == %d [%d] \n", k, start_step);
+         // Debug("tools:MoveDoor", "k == %d [%d]", k, start_step);
        }
 
        if (pos->step_yoffset < 0)      // door part on bottom side
@@ -5699,7 +5684,7 @@ void CreateToolButtons(void)
                      GDI_END);
 
     if (gi == NULL)
-      Error(ERR_EXIT, "cannot create gadget");
+      Fail("cannot create gadget");
 
     tool_gadget[id] = gi;
   }
@@ -5736,7 +5721,7 @@ static struct Mapping_EM_to_RND_object
   int action;
   int direction;
 }
-em_object_mapping_list[] =
+em_object_mapping_list[GAME_TILE_MAX + 1] =
 {
   {
     Zborder,                           FALSE,  FALSE,
@@ -6563,12 +6548,12 @@ em_object_mapping_list[] =
     EL_SPRING,                         -1, -1
   },
   {
-    Xspring_e,                         FALSE,  FALSE,
-    EL_SPRING,                         -1, -1
+    Xspring_e,                         TRUE,   FALSE,
+    EL_SPRING_RIGHT,                   -1, -1
   },
   {
-    Xspring_w,                         FALSE,  FALSE,
-    EL_SPRING,                         -1, -1
+    Xspring_w,                         TRUE,   FALSE,
+    EL_SPRING_LEFT,                    -1, -1
   },
   {
     Xspring_fall,                      FALSE,  FALSE,
@@ -6657,11 +6642,11 @@ em_object_mapping_list[] =
   },
   {
     Xpush_spring_e,                    FALSE,  FALSE,
-    EL_SPRING,                         -1, MV_BIT_RIGHT
+    EL_SPRING_RIGHT,                   -1, MV_BIT_RIGHT
   },
   {
     Xpush_spring_w,                    FALSE,  FALSE,
-    EL_SPRING,                         -1, MV_BIT_LEFT
+    EL_SPRING_LEFT,                    -1, MV_BIT_LEFT
   },
 
   {
@@ -6880,11 +6865,11 @@ em_object_mapping_list[] =
   },
 
   {
-    Xdrip,                             FALSE,  FALSE,
+    Xdrip,                             TRUE,   FALSE,
     EL_AMOEBA_DROP,                    ACTION_GROWING, -1
   },
   {
-    Xdrip_fall,                                TRUE,   FALSE,
+    Xdrip_fall,                                FALSE,  FALSE,
     EL_AMOEBA_DROP,                    -1, -1
   },
   {
@@ -7492,7 +7477,7 @@ static struct Mapping_EM_to_RND_player
   int action;
   int direction;
 }
-em_player_mapping_list[] =
+em_player_mapping_list[MAX_PLAYERS * PLY_MAX + 1] =
 {
   {
     PLY_walk_n,                                0,
@@ -7709,7 +7694,7 @@ em_player_mapping_list[] =
   }
 };
 
-int map_element_RND_to_EM(int element_rnd)
+int map_element_RND_to_EM_cave(int element_rnd)
 {
   static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
   static boolean mapping_initialized = FALSE;
@@ -7730,17 +7715,49 @@ int map_element_RND_to_EM(int element_rnd)
     mapping_initialized = TRUE;
   }
 
-  if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
-    return mapping_RND_to_EM[element_rnd];
+  if (element_rnd < 0 || element_rnd >= NUM_FILE_ELEMENTS)
+  {
+    Warn("invalid RND level element %d", element_rnd);
+
+    return EL_UNKNOWN;
+  }
+
+  return map_em_element_X_to_C(mapping_RND_to_EM[element_rnd]);
+}
+
+int map_element_EM_to_RND_cave(int element_em_cave)
+{
+  static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
+  static boolean mapping_initialized = FALSE;
+
+  if (!mapping_initialized)
+  {
+    int i;
+
+    // return "EL_UNKNOWN" for all undefined elements in mapping array
+    for (i = 0; i < GAME_TILE_MAX; i++)
+      mapping_EM_to_RND[i] = EL_UNKNOWN;
+
+    for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
+      mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
+       em_object_mapping_list[i].element_rnd;
+
+    mapping_initialized = TRUE;
+  }
+
+  if (element_em_cave < 0 || element_em_cave >= CAVE_TILE_MAX)
+  {
+    Warn("invalid EM cave element %d", element_em_cave);
 
-  Error(ERR_WARN, "invalid RND level element %d", element_rnd);
+    return EL_UNKNOWN;
+  }
 
-  return EL_UNKNOWN;
+  return mapping_EM_to_RND[map_em_element_C_to_X(element_em_cave)];
 }
 
-int map_element_EM_to_RND(int element_em)
+int map_element_EM_to_RND_game(int element_em_game)
 {
-  static unsigned short mapping_EM_to_RND[TILE_MAX];
+  static unsigned short mapping_EM_to_RND[GAME_TILE_MAX];
   static boolean mapping_initialized = FALSE;
 
   if (!mapping_initialized)
@@ -7748,7 +7765,7 @@ int map_element_EM_to_RND(int element_em)
     int i;
 
     // return "EL_UNKNOWN" for all undefined elements in mapping array
-    for (i = 0; i < TILE_MAX; i++)
+    for (i = 0; i < GAME_TILE_MAX; i++)
       mapping_EM_to_RND[i] = EL_UNKNOWN;
 
     for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
@@ -7758,12 +7775,14 @@ int map_element_EM_to_RND(int element_em)
     mapping_initialized = TRUE;
   }
 
-  if (element_em >= 0 && element_em < TILE_MAX)
-    return mapping_EM_to_RND[element_em];
+  if (element_em_game < 0 || element_em_game >= GAME_TILE_MAX)
+  {
+    Warn("invalid EM game element %d", element_em_game);
 
-  Error(ERR_WARN, "invalid EM level element %d", element_em);
+    return EL_UNKNOWN;
+  }
 
-  return EL_UNKNOWN;
+  return mapping_EM_to_RND[element_em_game];
 }
 
 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
@@ -7772,17 +7791,18 @@ void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
   struct CAVE *cav = level_em->cav;
   int i, j;
 
-  for (i = 0; i < TILE_MAX; i++)
-    cav->android_array[i] = Xblank;
+  for (i = 0; i < GAME_TILE_MAX; i++)
+    cav->android_array[i] = Cblank;
 
   for (i = 0; i < level->num_android_clone_elements; i++)
   {
     int element_rnd = level->android_clone_element[i];
-    int element_em = map_element_RND_to_EM(element_rnd);
+    int element_em_cave = map_element_RND_to_EM_cave(element_rnd);
 
     for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
       if (em_object_mapping_list[j].element_rnd == element_rnd)
-       cav->android_array[em_object_mapping_list[j].element_em] = element_em;
+       cav->android_array[em_object_mapping_list[j].element_em] =
+         element_em_cave;
   }
 }
 
@@ -7794,16 +7814,16 @@ void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
 
   level->num_android_clone_elements = 0;
 
-  for (i = 0; i < TILE_MAX; i++)
+  for (i = 0; i < GAME_TILE_MAX; i++)
   {
-    int element_em = cav->android_array[i];
+    int element_em_cave = cav->android_array[i];
     int element_rnd;
     boolean element_found = FALSE;
 
-    if (element_em == Xblank)
+    if (element_em_cave == Cblank)
       continue;
 
-    element_rnd = map_element_EM_to_RND(element_em);
+    element_rnd = map_element_EM_to_RND_cave(element_em_cave);
 
     for (j = 0; j < level->num_android_clone_elements; j++)
       if (level->android_clone_element[j] == element_rnd)
@@ -8232,11 +8252,21 @@ int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
   return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
 }
 
+boolean swapTiles_EM(boolean is_pre_emc_cave)
+{
+  return is_pre_emc_cave && leveldir_current->use_emc_tiles;
+}
+
 boolean getTeamMode_EM(void)
 {
   return game.team_mode || network_playing;
 }
 
+boolean isActivePlayer_EM(int player_nr)
+{
+  return stored_player[player_nr].active;
+}
+
 unsigned int InitRND(int seed)
 {
   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
@@ -8249,7 +8279,7 @@ unsigned int InitRND(int seed)
     return InitEngineRandom_RND(seed);
 }
 
-static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
+static struct Mapping_EM_to_RND_object object_mapping[GAME_TILE_MAX];
 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][PLY_MAX];
 
 static int get_effective_element_EM(int tile, int frame_em)
@@ -8595,7 +8625,7 @@ void InitGraphicInfo_EM(void)
   int i, j, p;
 
   // always start with reliable default values
-  for (i = 0; i < TILE_MAX; i++)
+  for (i = 0; i < GAME_TILE_MAX; i++)
   {
     object_mapping[i].element_rnd = EL_UNKNOWN;
     object_mapping[i].is_backside = FALSE;
@@ -8644,7 +8674,7 @@ void InitGraphicInfo_EM(void)
        MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
   }
 
-  for (i = 0; i < TILE_MAX; i++)
+  for (i = 0; i < GAME_TILE_MAX; i++)
   {
     int element = object_mapping[i].element_rnd;
     int action = object_mapping[i].action;
@@ -8909,7 +8939,7 @@ void InitGraphicInfo_EM(void)
     }
   }
 
-  for (i = 0; i < TILE_MAX; i++)
+  for (i = 0; i < GAME_TILE_MAX; i++)
   {
     for (j = 0; j < 8; j++)
     {
@@ -9000,7 +9030,7 @@ static void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
                                       boolean any_player_snapping,
                                       boolean any_player_dropping)
 {
-  if (frame == 0 && !any_player_dropping)
+  if (frame == 7 && !any_player_dropping)
   {
     if (!local_player->was_waiting)
     {
@@ -9052,17 +9082,19 @@ static void CheckSaveEngineSnapshot_MM(boolean element_clicked,
   }
 }
 
-void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
-                           boolean any_player_moving,
-                           boolean any_player_snapping,
-                           boolean any_player_dropping)
+boolean CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
+                              boolean any_player_moving,
+                              boolean any_player_snapping,
+                              boolean any_player_dropping)
 {
   if (tape.single_step && tape.recording && !tape.pausing)
-    if (frame == 0 && !any_player_dropping)
+    if (frame == 7 && !any_player_dropping)
       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
 
   CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
                             any_player_snapping, any_player_dropping);
+
+  return tape.pausing;
 }
 
 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
@@ -9226,51 +9258,59 @@ void PlaySoundSelecting(void)
 #endif
 }
 
-void ToggleFullscreenOrChangeWindowScalingIfNeeded(void)
+void ToggleFullscreenIfNeeded(void)
 {
-  boolean change_fullscreen = (setup.fullscreen !=
-                              video.fullscreen_enabled);
-  boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
-                                          setup.window_scaling_percent !=
-                                          video.window_scaling_percent);
-
-  if (change_window_scaling_percent && video.fullscreen_enabled)
+  // if setup and video fullscreen state are already matching, nothing do do
+  if (setup.fullscreen == video.fullscreen_enabled ||
+      !video.fullscreen_available)
     return;
 
-  if (!change_window_scaling_percent && !video.fullscreen_available)
-    return;
+  SDLSetWindowFullscreen(setup.fullscreen);
 
-  if (change_window_scaling_percent)
-  {
-    SDLSetWindowScaling(setup.window_scaling_percent);
+  // set setup value according to successfully changed fullscreen mode
+  setup.fullscreen = video.fullscreen_enabled;
+}
 
+void ChangeWindowScalingIfNeeded(void)
+{
+  // if setup and video window scaling are already matching, nothing do do
+  if (setup.window_scaling_percent == video.window_scaling_percent ||
+      video.fullscreen_enabled)
     return;
-  }
-  else if (change_fullscreen)
-  {
-    SDLSetWindowFullscreen(setup.fullscreen);
 
-    // set setup value according to successfully changed fullscreen mode
-    setup.fullscreen = video.fullscreen_enabled;
+  SDLSetWindowScaling(setup.window_scaling_percent);
+
+  // set setup value according to successfully changed window scaling
+  setup.window_scaling_percent = video.window_scaling_percent;
+}
+
+void ChangeVsyncModeIfNeeded(void)
+{
+  int setup_vsync_mode = VSYNC_MODE_STR_TO_INT(setup.vsync_mode);
+  int video_vsync_mode = video.vsync_mode;
 
+  // if setup and video vsync mode are already matching, nothing do do
+  if (setup_vsync_mode == video_vsync_mode)
     return;
-  }
 
-  if (change_fullscreen ||
-      change_window_scaling_percent)
+  // if renderer is using OpenGL, vsync mode can directly be changed
+  SDLSetScreenVsyncMode(setup.vsync_mode);
+
+  // if vsync mode unchanged, try re-creating renderer to set vsync mode
+  if (video.vsync_mode == video_vsync_mode)
   {
     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 re-creating screen
     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
-    }
+    // force re-creating screen and renderer to set new vsync mode
+    video.fullscreen_enabled = !setup.fullscreen;
+
+    // when creating new renderer, destroy textures linked to old renderer
+    FreeAllImageTextures();    // needs old renderer to free the textures
 
-    // toggle fullscreen
+    // re-create screen and renderer (including change of vsync mode)
     ChangeVideoModeIfNeeded(setup.fullscreen);
 
     // set setup value according to successfully changed fullscreen mode
@@ -9278,12 +9318,17 @@ void ToggleFullscreenOrChangeWindowScalingIfNeeded(void)
 
     // 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
     BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
+
+    // when changing vsync mode, re-create textures for new renderer
+    InitImageTextures();
   }
+
+  // set setup value according to successfully changed vsync mode
+  setup.vsync_mode = VSYNC_MODE_INT_TO_STR(video.vsync_mode);
 }
 
 static void JoinRectangles(int *x, int *y, int *width, int *height,
@@ -9469,7 +9514,7 @@ void ChangeViewportPropertiesIfNeeded(void)
     init_gfx_buffers = TRUE;
     init_gadgets_and_anims = TRUE;
 
-    // printf("::: video: init_video_buffer, init_gfx_buffers\n");
+    // Debug("tools:viewport", "video: init_video_buffer, init_gfx_buffers");
   }
 
   if (new_scr_fieldx != SCR_FIELDX ||
@@ -9480,7 +9525,7 @@ void ChangeViewportPropertiesIfNeeded(void)
     SCR_FIELDX = new_scr_fieldx;
     SCR_FIELDY = new_scr_fieldy;
 
-    // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
+    // Debug("tools:viewport", "new_scr_fieldx != SCR_FIELDX ...");
   }
 
   if (new_sx != SX ||
@@ -9550,7 +9595,7 @@ void ChangeViewportPropertiesIfNeeded(void)
 
     if (new_tilesize_var != TILESIZE_VAR)
     {
-      // printf("::: new_tilesize_var != TILESIZE_VAR\n");
+      // Debug("tools:viewport", "new_tilesize_var != TILESIZE_VAR");
 
       // changing tile size invalidates scroll values of engine snapshots
       FreeEngineSnapshotSingle();
@@ -9584,13 +9629,13 @@ void ChangeViewportPropertiesIfNeeded(void)
     init_gfx_buffers = TRUE;
     init_gadgets_and_anims = TRUE;
 
-    // printf("::: viewports: init_gfx_buffers\n");
-    // printf("::: viewports: init_gadgets_and_anims\n");
+    // Debug("tools:viewport", "viewports: init_gfx_buffers");
+    // Debug("tools:viewport", "viewports: init_gadgets_and_anims");
   }
 
   if (init_gfx_buffers)
   {
-    // printf("::: init_gfx_buffers\n");
+    // Debug("tools:viewport", "init_gfx_buffers");
 
     SCR_FIELDX = new_scr_fieldx_buffers;
     SCR_FIELDY = new_scr_fieldy_buffers;
@@ -9606,7 +9651,7 @@ void ChangeViewportPropertiesIfNeeded(void)
 
   if (init_video_buffer)
   {
-    // printf("::: init_video_buffer\n");
+    // Debug("tools:viewport", "init_video_buffer");
 
     FreeAllImageTextures();    // needs old renderer to free the textures
 
@@ -9616,7 +9661,7 @@ void ChangeViewportPropertiesIfNeeded(void)
 
   if (init_gadgets_and_anims)
   {
-    // printf("::: init_gadgets_and_anims\n");
+    // Debug("tools:viewport", "init_gadgets_and_anims");
 
     InitGadgets();
     InitGlobalAnimations();
@@ -9624,6 +9669,6 @@ void ChangeViewportPropertiesIfNeeded(void)
 
   if (init_em_graphics)
   {
-      InitGraphicInfo_EM();
+    InitGraphicInfo_EM();
   }
 }