rnd-20070302-1-src
[rocksndiamonds.git] / src / game.c
index 62ed8bbbe66ce3c9c0642420afa724002cea924f..afa0f24f923f9cbcccfbe3ebcac1872545fe2b55 100644 (file)
@@ -59,6 +59,8 @@
 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF          * 1)
 #define USE_FIX_IMPACT_COLLISION       (USE_NEW_STUFF          * 1)
 
+#define USE_GFX_RESET_WHEN_NOT_MOVING  (USE_NEW_STUFF          * 1)
+
 
 /* for DigField() */
 #define DF_NO_PUSH             0
 #define EX_TYPE_DYNA           (1 << 4)
 #define EX_TYPE_SINGLE_TILE    (EX_TYPE_CENTER | EX_TYPE_BORDER)
 
+#if 1
+#define PANEL_OFF()            (local_player->LevelSolved_PanelOff)
+#define        PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
+#define PANEL_XPOS(p)          (DX + ALIGNED_MENU_XPOS(p))
+#define PANEL_YPOS(p)          (DY + ALIGNED_MENU_YPOS(p))
+#else
 #define        PANEL_DEACTIVATED(p)    ((p).x < 0 || (p).y < 0)
 #define PANEL_XPOS(p)          (ALIGNED_XPOS((p).x, (p).width, (p).align))
 #define PANEL_YPOS(p)          ((p).y)
+#endif
 
 /* special positions in the game control window (relative to control window) */
 #define XX_LEVEL1              (PANEL_XPOS(game.panel.level))
@@ -913,6 +922,8 @@ static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
 
 void GetPlayerConfig()
 {
+  GameFrameDelay = setup.game_frame_delay;
+
   if (!audio.sound_available)
     setup.sound_simple = FALSE;
 
@@ -931,57 +942,6 @@ void GetPlayerConfig()
   InitJoysticks();
 }
 
-static 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);
-}
-
-static 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);
-}
-
-static 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);
-}
-
-static int getBeltDirNrFromBeltSwitchElement(int 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
-  };
-
-  int belt_nr = getBeltNrFromBeltSwitchElement(element);
-  int belt_dir_nr = element - belt_base_element[belt_nr];
-
-  return (belt_dir_nr % 3);
-}
-
-static int getBeltDirFromBeltSwitchElement(int element)
-{
-  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];
-}
-
 static int get_element_from_group_element(int element)
 {
   if (IS_GROUP_ELEMENT(element))
@@ -1325,148 +1285,151 @@ static inline void InitField_WithBug2(int x, int y, boolean init_game)
 
 #if 1
 
-inline void DrawGameValue_Emeralds(int value)
+void DrawGameValue_Emeralds(int value)
 {
+  struct TextPosInfo *pos = &game.panel.gems;
   int font_nr = FONT_TEXT_2;
   int font_width = getFontWidth(font_nr);
-  int xpos = (3 * 14 - 3 * font_width) / 2;
+  int digits = pos->chars;
 
-  if (PANEL_DEACTIVATED(game.panel.gems))
+  if (PANEL_DEACTIVATED(pos))
     return;
 
-  game.panel.gems.width = game.panel.gems.chars * font_width;
-  xpos = 0;
+  pos->width = digits * font_width;
 
-  DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
+  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
 }
 
-inline void DrawGameValue_Dynamite(int value)
+void DrawGameValue_Dynamite(int value)
 {
+  struct TextPosInfo *pos = &game.panel.inventory;
   int font_nr = FONT_TEXT_2;
   int font_width = getFontWidth(font_nr);
-  int xpos = (3 * 14 - 3 * font_width) / 2;
-
-  if (PANEL_DEACTIVATED(game.panel.inventory))
-    return;
-
-  game.panel.inventory.width = game.panel.inventory.chars * font_width;
-  xpos = 0;
-
-  DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
-}
-
-inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
-{
-  int base_key_graphic = EL_KEY_1;
-  int i;
+  int digits = pos->chars;
 
-  if (PANEL_DEACTIVATED(game.panel.keys))
+  if (PANEL_DEACTIVATED(pos))
     return;
 
-  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
-    base_key_graphic = EL_EM_KEY_1;
-
-  /* currently only 4 of 8 possible keys are displayed */
-  for (i = 0; i < STD_NUM_KEYS; i++)
-  {
-    int x = XX_KEYS + i * MINI_TILEX;
-    int y = YY_KEYS;
+  pos->width = digits * font_width;
 
-    if (key[i])
-      DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
-    else
-      BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
-                DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
-  }
+  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
 }
 
-inline void DrawGameValue_Score(int value)
+void DrawGameValue_Score(int value)
 {
+  struct TextPosInfo *pos = &game.panel.score;
   int font_nr = FONT_TEXT_2;
   int font_width = getFontWidth(font_nr);
-  int xpos = (5 * 14 - 5 * font_width) / 2;
+  int digits = pos->chars;
 
-  if (PANEL_DEACTIVATED(game.panel.score))
+  if (PANEL_DEACTIVATED(pos))
     return;
 
-  game.panel.score.width = game.panel.score.chars * font_width;
-  xpos = 0;
+  pos->width = digits * font_width;
 
-  DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
+  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
 }
 
-inline void DrawGameValue_Time(int value)
+void DrawGameValue_Time(int value)
 {
+  struct TextPosInfo *pos = &game.panel.time;
   static int last_value = -1;
-  int num_digits1 = 3;
-  int num_digits2 = 4;
-  int num_digits = game.panel.time.chars;
+  int digits1 = 3;
+  int digits2 = 4;
+  int digits = pos->chars;
   int font1_nr = FONT_TEXT_2;
   int font2_nr = FONT_TEXT_1;
   int font_nr = font1_nr;
-  int font1_width = getFontWidth(font1_nr);
-  int font2_width = getFontWidth(font2_nr);
-  int xpos3 = (3 * 14 - 3 * font1_width) / 2;
-  int xpos4 = (4 * 10 - 4 * font2_width) / 2;
+  boolean use_dynamic_digits = (digits == -1 ? TRUE : FALSE);
 
-  if (PANEL_DEACTIVATED(game.panel.time))
+  if (PANEL_DEACTIVATED(pos))
     return;
 
-  if (num_digits == -1)                        /* use dynamic number of digits */
+  if (use_dynamic_digits)              /* use dynamic number of digits */
   {
-    num_digits = (value < 1000 ? num_digits1 : num_digits2);
-    font_nr    = (value < 1000 ? font1_nr : font2_nr);
+    digits  = (value < 1000 ? digits1  : digits2);
+    font_nr = (value < 1000 ? font1_nr : font2_nr);
   }
 
-  xpos3 = 0;
-  xpos4 = 0;
-
   /* clear background if value just changed its size (dynamic digits only) */
-  if (game.panel.time.chars == -1 && (last_value < 1000) != (value < 1000))
+  if (use_dynamic_digits && (last_value < 1000) != (value < 1000))
   {
-    int width1 = num_digits1 * getFontWidth(font1_nr);
-    int width2 = num_digits2 * getFontWidth(font2_nr);
+    int width1 = digits1 * getFontWidth(font1_nr);
+    int width2 = digits2 * getFontWidth(font2_nr);
     int max_width = MAX(width1, width2);
     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
 
-    game.panel.time.width = max_width;
+    pos->width = max_width;
 
-    ClearRectangleOnBackground(drawto, DX_TIME, DY_TIME, max_width, max_height);
+    ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
+                              max_width, max_height);
   }
 
-  game.panel.time.width = num_digits * getFontWidth(font_nr);
+  pos->width = digits * getFontWidth(font_nr);
 
-  DrawText(DX_TIME, DY_TIME, int2str(value, num_digits), font_nr);
+  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
 
   last_value = value;
 }
 
-inline void DrawGameValue_Level(int value)
+void DrawGameValue_Level(int value)
 {
-  int num_digits1 = 2;
-  int num_digits2 = 3;
-  int num_digits = game.panel.level.chars;
+  struct TextPosInfo *pos = &game.panel.level;
+  int digits1 = 2;
+  int digits2 = 3;
+  int digits = pos->chars;
   int font1_nr = FONT_TEXT_2;
   int font2_nr = FONT_TEXT_1;
   int font_nr = font1_nr;
+  boolean use_dynamic_digits = (digits == -1 ? TRUE : FALSE);
 
-  if (PANEL_DEACTIVATED(game.panel.level))
+  if (PANEL_DEACTIVATED(pos))
     return;
 
-  if (num_digits == -1)                        /* use dynamic number of digits */
+  if (use_dynamic_digits)              /* use dynamic number of digits */
   {
-    num_digits = (level_nr < 100 ? num_digits1 : num_digits2);
-    font_nr    = (level_nr < 100 ? font1_nr : font2_nr);
+    digits  = (level_nr < 100 ? digits1  : digits2);
+    font_nr = (level_nr < 100 ? font1_nr : font2_nr);
   }
 
-  game.panel.level.width = num_digits * getFontWidth(font_nr);
+  pos->width = digits * getFontWidth(font_nr);
+
+  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
+}
+
+void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
+{
+  struct TextPosInfo *pos = &game.panel.keys;
+  int base_key_graphic = EL_KEY_1;
+  int i;
+
+  if (PANEL_DEACTIVATED(pos))
+    return;
+
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+    base_key_graphic = EL_EM_KEY_1;
+
+  pos->width = 4 * MINI_TILEX;
+
+  /* currently only 4 of 8 possible keys are displayed */
+  for (i = 0; i < STD_NUM_KEYS; i++)
+  {
+    int src_x = DOOR_GFX_PAGEX5 + 18;
+    int src_y = DOOR_GFX_PAGEY1 + 123;
+    int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
+    int dst_y = PANEL_YPOS(pos);
 
-  DrawText(DX_LEVEL, DY_LEVEL, int2str(value, num_digits), font_nr);
+    if (key[i])
+      DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
+    else
+      BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
+                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
+  }
 }
 
 #else
 
-inline void DrawGameValue_Emeralds(int value)
+void DrawGameValue_Emeralds(int value)
 {
   int font_nr = FONT_TEXT_2;
   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
@@ -1477,7 +1440,7 @@ inline void DrawGameValue_Emeralds(int value)
   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
 }
 
-inline void DrawGameValue_Dynamite(int value)
+void DrawGameValue_Dynamite(int value)
 {
   int font_nr = FONT_TEXT_2;
   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
@@ -1488,32 +1451,7 @@ inline void DrawGameValue_Dynamite(int value)
   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
 }
 
-inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
-{
-  int base_key_graphic = EL_KEY_1;
-  int i;
-
-  if (PANEL_DEACTIVATED(game.panel.keys))
-    return;
-
-  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
-    base_key_graphic = EL_EM_KEY_1;
-
-  /* currently only 4 of 8 possible keys are displayed */
-  for (i = 0; i < STD_NUM_KEYS; i++)
-  {
-    int x = XX_KEYS + i * MINI_TILEX;
-    int y = YY_KEYS;
-
-    if (key[i])
-      DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
-    else
-      BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
-                DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
-  }
-}
-
-inline void DrawGameValue_Score(int value)
+void DrawGameValue_Score(int value)
 {
   int font_nr = FONT_TEXT_2;
   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
@@ -1524,7 +1462,7 @@ inline void DrawGameValue_Score(int value)
   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
 }
 
-inline void DrawGameValue_Time(int value)
+void DrawGameValue_Time(int value)
 {
   int font1_nr = FONT_TEXT_2;
 #if 1
@@ -1548,7 +1486,7 @@ inline void DrawGameValue_Time(int value)
     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
 }
 
-inline void DrawGameValue_Level(int value)
+void DrawGameValue_Level(int value)
 {
   int font1_nr = FONT_TEXT_2;
 #if 1
@@ -1566,6 +1504,31 @@ inline void DrawGameValue_Level(int value)
     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
 }
 
+void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
+{
+  int base_key_graphic = EL_KEY_1;
+  int i;
+
+  if (PANEL_DEACTIVATED(game.panel.keys))
+    return;
+
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+    base_key_graphic = EL_EM_KEY_1;
+
+  /* currently only 4 of 8 possible keys are displayed */
+  for (i = 0; i < STD_NUM_KEYS; i++)
+  {
+    int x = XX_KEYS + i * MINI_TILEX;
+    int y = YY_KEYS;
+
+    if (key[i])
+      DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
+    else
+      BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
+                DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
+  }
+}
+
 #endif
 
 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
@@ -2231,7 +2194,9 @@ void InitGame()
     player->LevelSolved = FALSE;
     player->GameOver = FALSE;
 
+    player->LevelSolved_GameWon = FALSE;
     player->LevelSolved_GameEnd = FALSE;
+    player->LevelSolved_PanelOff = FALSE;
     player->LevelSolved_SaveTape = FALSE;
     player->LevelSolved_SaveScore = FALSE;
   }
@@ -2963,10 +2928,12 @@ void GameWon()
 {
   static int time, time_final;
   static int score, score_final;
-  static int game_over_delay = 0;
-  int game_over_delay_value = 50;
+  static int game_over_delay_1 = 0;
+  static int game_over_delay_2 = 0;
+  int game_over_delay_value_1 = 50;
+  int game_over_delay_value_2 = 50;
 
-  if (!local_player->LevelSolved_GameEnd)
+  if (!local_player->LevelSolved_GameWon)
   {
     int i;
 
@@ -2974,7 +2941,7 @@ void GameWon()
     if (local_player->MovPos)
       return;
 
-    local_player->LevelSolved_GameEnd = TRUE;
+    local_player->LevelSolved_GameWon = TRUE;
     local_player->LevelSolved_SaveTape = tape.recording;
     local_player->LevelSolved_SaveScore = !tape.playing;
 
@@ -2985,7 +2952,8 @@ void GameWon()
     TapeStop();
 #endif
 
-    game_over_delay = game_over_delay_value;
+    game_over_delay_1 = game_over_delay_value_1;
+    game_over_delay_2 = game_over_delay_value_2;
 
     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
     score = score_final = local_player->score_final;
@@ -3067,9 +3035,9 @@ void GameWon()
     PlaySound(SND_GAME_WINNING);
   }
 
-  if (game_over_delay > 0)
+  if (game_over_delay_1 > 0)
   {
-    game_over_delay--;
+    game_over_delay_1--;
 
     return;
   }
@@ -3092,7 +3060,22 @@ void GameWon()
       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
     else
       PlaySound(SND_GAME_LEVELTIME_BONUS);
+
+    return;
+  }
+
+  local_player->LevelSolved_PanelOff = TRUE;
+
+  if (game_over_delay_2 > 0)
+  {
+    game_over_delay_2--;
+
+    return;
   }
+
+#if 1
+  GameEnd();
+#endif
 }
 
 void GameEnd()
@@ -3100,6 +3083,8 @@ void GameEnd()
   int hi_pos;
   boolean raise_level = FALSE;
 
+  local_player->LevelSolved_GameEnd = TRUE;
+
   CloseDoor(DOOR_CLOSE_1);
 
   if (local_player->LevelSolved_SaveTape)
@@ -3326,7 +3311,12 @@ void InitMovingField(int x, int y, int direction)
 
   /* check if element was/is moving or being moved before/after mode change */
 #if 1
+#if 1
+  is_moving_before = (WasJustMoving[x][y] != 0);
+#else
+  /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
   is_moving_before = WasJustMoving[x][y];
+#endif
 #else
   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
 #endif
@@ -6871,6 +6861,17 @@ void ContinueMoving(int x, int y)
 
   if (ABS(MovPos[x][y]) < TILEX)
   {
+#if 0
+    int ee = Feld[x][y];
+    int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+    int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
+
+    printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
+          x, y, ABS(MovPos[x][y]),
+          ee, gg, ff,
+          GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
+#endif
+
     DrawLevelField(x, y);
 
     return;    /* element is still moving */
@@ -9063,8 +9064,18 @@ static void HandleElementChange(int x, int y, int page)
 
     if (change->can_change)
     {
-      ResetGfxAnimation(x, y);
-      ResetRandomAnimationValue(x, y);
+#if 0
+      /* !!! not clear why graphic animation should be reset at all here !!! */
+#if USE_GFX_RESET_WHEN_NOT_MOVING
+      /* when a custom element is about to change (for example by change delay),
+        do not reset graphic animation when the custom element is moving */
+      if (IS_MOVING(x, y))
+#endif
+      {
+       ResetGfxAnimation(x, y);
+       ResetRandomAnimationValue(x, y);
+      }
+#endif
 
       if (change->pre_change_function)
        change->pre_change_function(x, y);
@@ -9859,7 +9870,7 @@ void GameActions()
       AllPlayersGone = TRUE;
   }
 
-  if (local_player->LevelSolved)
+  if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
     GameWon();
 
   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
@@ -10556,25 +10567,79 @@ static boolean AllPlayersInVisibleScreen()
 
 void ScrollLevel(int dx, int dy)
 {
+#if 1
+  static Bitmap *bitmap_db_field2 = NULL;
   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
   int x, y;
+#else
+  int i, x, y;
+#endif
+
+  /* only horizontal XOR vertical scroll direction allowed */
+  if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
+    return;
+
+#if 1
+  if (bitmap_db_field2 == NULL)
+    bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
+
+  BlitBitmap(drawto_field, bitmap_db_field2,
+            FX + TILEX * (dx == -1) - softscroll_offset,
+            FY + TILEY * (dy == -1) - softscroll_offset,
+            SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
+            SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
+            FX + TILEX * (dx == 1) - softscroll_offset,
+            FY + TILEY * (dy == 1) - softscroll_offset);
+  BlitBitmap(bitmap_db_field2, drawto_field,
+            FX + TILEX * (dx == 1) - softscroll_offset,
+            FY + TILEY * (dy == 1) - softscroll_offset,
+            SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
+            SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
+            FX + TILEX * (dx == 1) - softscroll_offset,
+            FY + TILEY * (dy == 1) - softscroll_offset);
+
+#else
+
+#if 1
+  int xsize = (BX2 - BX1 + 1);
+  int ysize = (BY2 - BY1 + 1);
+  int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
+  int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
+  int step  = (start < end ? +1 : -1);
+
+  for (i = start; i != end; i += step)
+  {
+    BlitBitmap(drawto_field, drawto_field,
+              FX + TILEX * (dx != 0 ? i + step : 0),
+              FY + TILEY * (dy != 0 ? i + step : 0),
+              TILEX * (dx != 0 ? 1 : xsize),
+              TILEY * (dy != 0 ? 1 : ysize),
+              FX + TILEX * (dx != 0 ? i : 0),
+              FY + TILEY * (dy != 0 ? i : 0));
+  }
+
+#else
+
+  int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
 
   BlitBitmap(drawto_field, drawto_field,
             FX + TILEX * (dx == -1) - softscroll_offset,
             FY + TILEY * (dy == -1) - softscroll_offset,
-            SXSIZE - TILEX * (dx!=0) + 2 * softscroll_offset,
-            SYSIZE - TILEY * (dy!=0) + 2 * softscroll_offset,
+            SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
+            SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
             FX + TILEX * (dx == 1) - softscroll_offset,
             FY + TILEY * (dy == 1) - softscroll_offset);
+#endif
+#endif
 
-  if (dx)
+  if (dx != 0)
   {
     x = (dx == 1 ? BX1 : BX2);
     for (y = BY1; y <= BY2; y++)
       DrawScreenField(x, y);
   }
 
-  if (dy)
+  if (dy != 0)
   {
     y = (dy == 1 ? BY1 : BY2);
     for (x = BX1; x <= BX2; x++)
@@ -13471,9 +13536,9 @@ static void LoadEngineSnapshotValues_RND()
 
   if (game.num_random_calls != num_random_calls)
   {
-    Error(ERR_RETURN, "number of random calls out of sync");
-    Error(ERR_RETURN, "number of random calls should be %d", num_random_calls);
-    Error(ERR_RETURN, "number of random calls is %d", game.num_random_calls);
+    Error(ERR_INFO, "number of random calls out of sync");
+    Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
+    Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
     Error(ERR_EXIT, "this should not happen -- please debug");
   }
 }