rnd-20070312-3-src
[rocksndiamonds.git] / src / game.c
index 3652cc99beaa7b21ce567dd691abd531f83d0081..840f476f3fc63d837b76f8417baaa899d5b6ec62 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
@@ -86,7 +88,8 @@
 #define EX_TYPE_SINGLE_TILE    (EX_TYPE_CENTER | EX_TYPE_BORDER)
 
 #if 1
-#define        PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0)
+#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 DX_TIME                        (DX + XX_TIME)
 #define DY_TIME                        (DY + YY_TIME)
 
+#if 0
+/* game panel display and control definitions */
+
+#define GAME_CONTROL_LEVEL                     0
+#define GAME_CONTROL_GEMS                      1
+#define GAME_CONTROL_INVENTORY                 2
+#define GAME_CONTROL_KEYS                      3
+#define GAME_CONTROL_SCORE                     4
+#define GAME_CONTROL_TIME                      5
+
+
+
+#define GAME_CONTROL_LEVELS                    1
+#define GAME_CONTROL_SCORES                    2
+#define GAME_CONTROL_EDITOR                    3
+#define GAME_CONTROL_INFO                      4
+#define GAME_CONTROL_GAME                      5
+#define GAME_CONTROL_SETUP                     6
+#define GAME_CONTROL_QUIT                      7
+#define GAME_CONTROL_PREV_LEVEL                        8
+#define GAME_CONTROL_NEXT_LEVEL                        9
+#define GAME_CONTROL_CURRENT_LEVEL             10
+#define GAME_CONTROL_FIRST_LEVEL               11
+#define GAME_CONTROL_LAST_LEVEL                        12
+#define GAME_CONTROL_LEVEL_INFO_1              13
+#define GAME_CONTROL_LEVEL_INFO_2              14
+#define GAME_CONTROL_LEVEL_NAME                        15
+#define GAME_CONTROL_LEVEL_AUTHOR              16
+#define GAME_CONTROL_LEVEL_YEAR                        17
+#define GAME_CONTROL_LEVEL_IMPORTED_FROM       18
+#define GAME_CONTROL_LEVEL_IMPORTED_BY         19
+#define GAME_CONTROL_LEVEL_TESTED_BY           20
+#define GAME_CONTROL_TITLE_1                   21
+#define GAME_CONTROL_TITLE_2                   22
+#define GAME_CONTROL_TITLE_3                   23
+
+static char str_game_text_name[10];
+static char str_game_text_current_level[10];
+static char str_game_text_first_level[10];
+static char str_game_text_last_level[10];
+
+static char *game_text_name                    = str_game_text_name;
+static char *game_text_current_level           = str_game_text_current_level;
+static char *game_text_first_level             = str_game_text_first_level;
+static char *game_text_last_level              = str_game_text_last_level;
+static char *game_text_levels                  = "Levelset";
+static char *game_text_scores                  = "Hall Of Fame";
+static char *game_text_editor                  = "Level Creator";
+static char *game_text_info                    = "Info Screen";
+static char *game_text_game                    = "Start Game";
+static char *game_text_setup                   = "Setup";
+static char *game_text_quit                    = "Quit";
+static char *game_text_level_name              = level.name;
+static char *game_text_level_author            = level.author;
+static char *game_text_level_year              = NULL;
+static char *game_text_level_imported_from     = NULL;
+static char *game_text_level_imported_by       = NULL;
+static char *game_text_level_tested_by         = NULL;
+static char *game_text_title_1                 = PROGRAM_TITLE_STRING;
+static char *game_text_title_2                 = PROGRAM_COPYRIGHT_STRING;
+static char *game_text_title_3                 = PROGRAM_GAME_BY_STRING;
+
+struct GameControlInfo
+{
+  int nr;
+
+  struct MenuPosInfo *pos_button;
+  int button_graphic;
+
+  struct TextPosInfo *pos_text;
+  char **text;
+
+  struct TextPosInfo *pos_input;
+  char **input;
+};
+
+static struct GameControlInfo game_controls[] =
+{
+  {
+    GAME_CONTROL_NAME,
+    &menu.game.button.name,            IMG_MENU_BUTTON,
+    &menu.game.text.name,              &game_text_name,
+    &menu.game.input.name,             &setup.player_name,
+  },
+  {
+    GAME_CONTROL_LEVELS,
+    &menu.game.button.levels,          IMG_MENU_BUTTON_ENTER_MENU,
+    &menu.game.text.levels,            &game_text_levels,
+    NULL,                              NULL,
+  },
+  {
+    GAME_CONTROL_SCORES,
+    &menu.game.button.scores,          IMG_MENU_BUTTON,
+    &menu.game.text.scores,            &game_text_scores,
+    NULL,                              NULL,
+  },
+  {
+    GAME_CONTROL_EDITOR,
+    &menu.game.button.editor,          IMG_MENU_BUTTON,
+    &menu.game.text.editor,            &game_text_editor,
+    NULL,                              NULL,
+  },
+  {
+    GAME_CONTROL_INFO,
+    &menu.game.button.info,            IMG_MENU_BUTTON_ENTER_MENU,
+    &menu.game.text.info,              &game_text_info,
+    NULL,                              NULL,
+  },
+  {
+    GAME_CONTROL_GAME,
+    &menu.game.button.game,            IMG_MENU_BUTTON,
+    &menu.game.text.game,              &game_text_game,
+    NULL,                              NULL,
+  },
+  {
+    GAME_CONTROL_SETUP,
+    &menu.game.button.setup,           IMG_MENU_BUTTON_ENTER_MENU,
+    &menu.game.text.setup,             &game_text_setup,
+    NULL,                              NULL,
+  },
+  {
+    GAME_CONTROL_QUIT,
+    &menu.game.button.quit,            IMG_MENU_BUTTON,
+    &menu.game.text.quit,              &game_text_quit,
+    NULL,                              NULL,
+  },
+#if 0
+  /* (these two buttons are real gadgets) */
+  {
+    GAME_CONTROL_PREV_LEVEL,
+    &menu.game.button.prev_level,      IMG_MENU_BUTTON_PREV_LEVEL,
+    NULL,                              NULL,
+    NULL,                              NULL,
+  },
+  {
+    GAME_CONTROL_NEXT_LEVEL,
+    &menu.game.button.next_level,      IMG_MENU_BUTTON_NEXT_LEVEL,
+    NULL,                              NULL,
+    NULL,                              NULL,
+  },
+#endif
+  {
+    GAME_CONTROL_CURRENT_LEVEL,
+    NULL,                              -1,
+    &menu.game.text.current_level,     &game_text_current_level,
+    NULL,                              NULL,
+  },
+  {
+    GAME_CONTROL_FIRST_LEVEL,
+    NULL,                              -1,
+    &menu.game.text.first_level,       &game_text_first_level,
+    NULL,                              NULL,
+  },
+  {
+    GAME_CONTROL_LAST_LEVEL,
+    NULL,                              -1,
+    &menu.game.text.last_level,                &game_text_last_level,
+    NULL,                              NULL,
+  },
+  {
+    GAME_CONTROL_LEVEL_INFO_1,
+    NULL,                              -1,
+    &menu.game.text.level_info_1,      NULL,
+    NULL,                              NULL,
+  },
+  {
+    GAME_CONTROL_LEVEL_INFO_2,
+    NULL,                              -1,
+    &menu.game.text.level_info_2,      NULL,
+    NULL,                              NULL,
+  },
+  {
+    GAME_CONTROL_LEVEL_NAME,
+    NULL,                              -1,
+    &menu.game.text.level_name,                &game_text_level_name,
+    NULL,                              NULL,
+  },
+  {
+    GAME_CONTROL_LEVEL_AUTHOR,
+    NULL,                              -1,
+    &menu.game.text.level_author,      &game_text_level_author,
+    NULL,                              NULL,
+  },
+  {
+    GAME_CONTROL_LEVEL_YEAR,
+    NULL,                              -1,
+    &menu.game.text.level_year,                &game_text_level_year,
+    NULL,                              NULL,
+  },
+  {
+    GAME_CONTROL_LEVEL_IMPORTED_FROM,
+    NULL,                              -1,
+    &menu.game.text.level_imported_from, &game_text_level_imported_from,
+    NULL,                              NULL,
+  },
+  {
+    GAME_CONTROL_LEVEL_IMPORTED_BY,
+    NULL,                              -1,
+    &menu.game.text.level_imported_by, &game_text_level_imported_by,
+    NULL,                              NULL,
+  },
+  {
+    GAME_CONTROL_LEVEL_TESTED_BY,
+    NULL,                              -1,
+    &menu.game.text.level_tested_by,   &game_text_level_tested_by,
+    NULL,                              NULL,
+  },
+  {
+    GAME_CONTROL_TITLE_1,
+    NULL,                              -1,
+    &menu.game.text.title_1,           &game_text_title_1,
+    NULL,                              NULL,
+  },
+  {
+    GAME_CONTROL_TITLE_2,
+    NULL,                              -1,
+    &menu.game.text.title_2,           &game_text_title_2,
+    NULL,                              NULL,
+  },
+  {
+    GAME_CONTROL_TITLE_3,
+    NULL,                              -1,
+    &menu.game.text.title_3,           &game_text_title_3,
+    NULL,                              NULL,
+  },
+
+  {
+    -1,
+    NULL,                              -1,
+    NULL,                              NULL,
+    NULL,                              NULL,
+  }
+};
+#endif
+
+
 /* values for delayed check of falling and moving elements and for collision */
 #define CHECK_DELAY_MOVING     3
 #define CHECK_DELAY_FALLING    CHECK_DELAY_MOVING
@@ -840,7 +1079,7 @@ static int playfield_scan_delta_y = 1;
                                     (y) += playfield_scan_delta_y)     \
                                for ((x) = playfield_scan_start_x;      \
                                     (x) >= 0 && (x) <= lev_fieldx - 1; \
-                                    (x) += playfield_scan_delta_x)     \
+                                    (x) += playfield_scan_delta_x)
 
 #ifdef DEBUG
 void DEBUG_SetMaximumDynamite()
@@ -919,6 +1158,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;
 
@@ -937,58 +1178,7 @@ 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)
+int GetElementFromGroupElement(int element)
 {
   if (IS_GROUP_ELEMENT(element))
   {
@@ -1286,7 +1476,7 @@ static void InitField(int x, int y, boolean init_game)
       }
       else if (IS_GROUP_ELEMENT(element))
       {
-       Feld[x][y] = get_element_from_group_element(element);
+       Feld[x][y] = GetElementFromGroupElement(element);
 
        InitField(x, y, init_game);
       }
@@ -1334,74 +1524,91 @@ static inline void InitField_WithBug2(int x, int y, boolean init_game)
 void DrawGameValue_Emeralds(int value)
 {
   struct TextPosInfo *pos = &game.panel.gems;
+#if 1
+  int font_nr = pos->font;
+#else
   int font_nr = FONT_TEXT_2;
+#endif
   int font_width = getFontWidth(font_nr);
-  int digits = pos->chars;
+  int chars = pos->chars;
 
   if (PANEL_DEACTIVATED(pos))
     return;
 
-  pos->width = digits * font_width;
+  pos->width = chars * font_width;
 
-  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
+  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
 }
 
 void DrawGameValue_Dynamite(int value)
 {
   struct TextPosInfo *pos = &game.panel.inventory;
+#if 1
+  int font_nr = pos->font;
+#else
   int font_nr = FONT_TEXT_2;
+#endif
   int font_width = getFontWidth(font_nr);
-  int digits = pos->chars;
+  int chars = pos->chars;
 
   if (PANEL_DEACTIVATED(pos))
     return;
 
-  pos->width = digits * font_width;
+  pos->width = chars * font_width;
 
-  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
+  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
 }
 
 void DrawGameValue_Score(int value)
 {
   struct TextPosInfo *pos = &game.panel.score;
+#if 1
+  int font_nr = pos->font;
+#else
   int font_nr = FONT_TEXT_2;
+#endif
   int font_width = getFontWidth(font_nr);
-  int digits = pos->chars;
+  int chars = pos->chars;
 
   if (PANEL_DEACTIVATED(pos))
     return;
 
-  pos->width = digits * font_width;
+  pos->width = chars * font_width;
 
-  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
+  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
 }
 
 void DrawGameValue_Time(int value)
 {
   struct TextPosInfo *pos = &game.panel.time;
   static int last_value = -1;
-  int digits1 = 3;
-  int digits2 = 4;
-  int digits = pos->chars;
+  int chars1 = 3;
+  int chars2 = 4;
+  int chars = pos->chars;
+#if 1
+  int font1_nr = pos->font;
+  int font2_nr = pos->font_alt;
+#else
   int font1_nr = FONT_TEXT_2;
   int font2_nr = FONT_TEXT_1;
+#endif
   int font_nr = font1_nr;
-  boolean use_dynamic_digits = (digits == -1 ? TRUE : FALSE);
+  boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
 
   if (PANEL_DEACTIVATED(pos))
     return;
 
-  if (use_dynamic_digits)              /* use dynamic number of digits */
+  if (use_dynamic_chars)               /* use dynamic number of chars */
   {
-    digits  = (value < 1000 ? digits1  : digits2);
+    chars   = (value < 1000 ? chars1   : chars2);
     font_nr = (value < 1000 ? font1_nr : font2_nr);
   }
 
-  /* clear background if value just changed its size (dynamic digits only) */
-  if (use_dynamic_digits && (last_value < 1000) != (value < 1000))
+  /* clear background if value just changed its size (dynamic chars only) */
+  if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
   {
-    int width1 = digits1 * getFontWidth(font1_nr);
-    int width2 = digits2 * getFontWidth(font2_nr);
+    int width1 = chars1 * getFontWidth(font1_nr);
+    int width2 = chars2 * getFontWidth(font2_nr);
     int max_width = MAX(width1, width2);
     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
 
@@ -1411,9 +1618,9 @@ void DrawGameValue_Time(int value)
                               max_width, max_height);
   }
 
-  pos->width = digits * getFontWidth(font_nr);
+  pos->width = chars * getFontWidth(font_nr);
 
-  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
+  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
 
   last_value = value;
 }
@@ -1421,26 +1628,31 @@ void DrawGameValue_Time(int value)
 void DrawGameValue_Level(int value)
 {
   struct TextPosInfo *pos = &game.panel.level;
-  int digits1 = 2;
-  int digits2 = 3;
-  int digits = pos->chars;
+  int chars1 = 2;
+  int chars2 = 3;
+  int chars = pos->chars;
+#if 1
+  int font1_nr = pos->font;
+  int font2_nr = pos->font_alt;
+#else
   int font1_nr = FONT_TEXT_2;
   int font2_nr = FONT_TEXT_1;
+#endif
   int font_nr = font1_nr;
-  boolean use_dynamic_digits = (digits == -1 ? TRUE : FALSE);
+  boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
 
   if (PANEL_DEACTIVATED(pos))
     return;
 
-  if (use_dynamic_digits)              /* use dynamic number of digits */
+  if (use_dynamic_chars)               /* use dynamic number of chars */
   {
-    digits  = (level_nr < 100 ? digits1  : digits2);
+    chars   = (level_nr < 100 ? chars1   : chars2);
     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
   }
 
-  pos->width = digits * getFontWidth(font_nr);
+  pos->width = chars * getFontWidth(font_nr);
 
-  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, digits), font_nr);
+  DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
 }
 
 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
@@ -2240,7 +2452,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;
   }
@@ -2972,10 +3186,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;
 
@@ -2983,7 +3199,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;
 
@@ -2994,7 +3210,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;
@@ -3076,9 +3293,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;
   }
@@ -3101,7 +3318,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()
@@ -3109,6 +3341,8 @@ void GameEnd()
   int hi_pos;
   boolean raise_level = FALSE;
 
+  local_player->LevelSolved_GameEnd = TRUE;
+
   CloseDoor(DOOR_CLOSE_1);
 
   if (local_player->LevelSolved_SaveTape)
@@ -3335,7 +3569,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
@@ -6880,6 +7119,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 */
@@ -8740,7 +8990,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
 static void CreateFieldExt(int x, int y, int element, boolean is_change)
 {
   int old_element = Feld[x][y];
-  int new_element = get_element_from_group_element(element);
+  int new_element = GetElementFromGroupElement(element);
   int previous_move_direction = MovDir[x][y];
 #if USE_NEW_CUSTOM_VALUE
   int last_ce_value = CustomValue[x][y];
@@ -9072,8 +9322,19 @@ static void HandleElementChange(int x, int y, int page)
 
     if (change->can_change)
     {
-      ResetGfxAnimation(x, y);
-      ResetRandomAnimationValue(x, y);
+#if 1
+      /* !!! not clear why graphic animation should be reset at all here !!! */
+      /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
+#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);
@@ -9868,7 +10129,7 @@ void GameActions()
       AllPlayersGone = TRUE;
   }
 
-  if (local_player->LevelSolved)
+  if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
     GameWon();
 
   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
@@ -10105,13 +10366,15 @@ void GameActions_RND()
 
        /* continue moving after pushing (this is actually a bug) */
        if (!IS_MOVING(x, y))
-       {
          Stop[x][y] = FALSE;
-       }
       }
     }
   }
 
+#if 0
+  debug_print_timestamp(0, "start main loop profiling");
+#endif
+
   SCAN_PLAYFIELD(x, y)
   {
     ChangeCount[x][y] = 0;
@@ -10186,6 +10449,63 @@ void GameActions_RND()
 #endif
   }
 
+#if 0
+  debug_print_timestamp(0, "- time for pre-main loop:");
+#endif
+
+#if 0  // -------------------- !!! TEST ONLY !!! --------------------
+  SCAN_PLAYFIELD(x, y)
+  {
+    element = Feld[x][y];
+    graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+
+#if 1
+    {
+#if 1
+      int element2 = element;
+      int graphic2 = graphic;
+#else
+      int element2 = Feld[x][y];
+      int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
+#endif
+      int last_gfx_frame = GfxFrame[x][y];
+
+      if (graphic_info[graphic2].anim_global_sync)
+       GfxFrame[x][y] = FrameCounter;
+      else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
+       GfxFrame[x][y] = CustomValue[x][y];
+      else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
+       GfxFrame[x][y] = element_info[element2].collect_score;
+      else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
+       GfxFrame[x][y] = ChangeDelay[x][y];
+
+      if (redraw && GfxFrame[x][y] != last_gfx_frame)
+       DrawLevelGraphicAnimation(x, y, graphic2);
+    }
+#else
+    ResetGfxFrame(x, y, TRUE);
+#endif
+
+#if 1
+    if (ANIM_MODE(graphic) == ANIM_RANDOM &&
+       IS_NEXT_FRAME(GfxFrame[x][y], graphic))
+      ResetRandomAnimationValue(x, y);
+#endif
+
+#if 1
+    SetRandomAnimationValue(x, y);
+#endif
+
+#if 1
+    PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
+#endif
+  }
+#endif // -------------------- !!! TEST ONLY !!! --------------------
+
+#if 0
+  debug_print_timestamp(0, "- time for TEST loop:     -->");
+#endif
+
   SCAN_PLAYFIELD(x, y)
   {
     element = Feld[x][y];
@@ -10229,6 +10549,143 @@ void GameActions_RND()
       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
     }
 
+#if 0  // ---------------------------------------------------------------------
+
+    if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
+    {
+      StartMoving(x, y);
+
+      element = Feld[x][y];
+      graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+
+      if (IS_ANIMATED(graphic) &&
+         !IS_MOVING(x, y) &&
+         !Stop[x][y])
+       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+
+      if (IS_GEM(element) || element == EL_SP_INFOTRON)
+       DrawTwinkleOnField(x, y);
+    }
+    else if (IS_MOVING(x, y))
+      ContinueMoving(x, y);
+    else
+    {
+      switch (element)
+      {
+        case EL_ACID:
+        case EL_EXIT_OPEN:
+        case EL_EM_EXIT_OPEN:
+        case EL_SP_EXIT_OPEN:
+        case EL_STEEL_EXIT_OPEN:
+        case EL_EM_STEEL_EXIT_OPEN:
+        case EL_SP_TERMINAL:
+        case EL_SP_TERMINAL_ACTIVE:
+        case EL_EXTRA_TIME:
+        case EL_SHIELD_NORMAL:
+        case EL_SHIELD_DEADLY:
+         if (IS_ANIMATED(graphic))
+           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+         break;
+
+        case EL_DYNAMITE_ACTIVE:
+        case EL_EM_DYNAMITE_ACTIVE:
+        case EL_DYNABOMB_PLAYER_1_ACTIVE:
+        case EL_DYNABOMB_PLAYER_2_ACTIVE:
+        case EL_DYNABOMB_PLAYER_3_ACTIVE:
+        case EL_DYNABOMB_PLAYER_4_ACTIVE:
+        case EL_SP_DISK_RED_ACTIVE:
+         CheckDynamite(x, y);
+         break;
+
+        case EL_AMOEBA_GROWING:
+         AmoebeWaechst(x, y);
+         break;
+
+        case EL_AMOEBA_SHRINKING:
+         AmoebaDisappearing(x, y);
+         break;
+
+#if !USE_NEW_AMOEBA_CODE
+        case EL_AMOEBA_WET:
+        case EL_AMOEBA_DRY:
+        case EL_AMOEBA_FULL:
+        case EL_BD_AMOEBA:
+        case EL_EMC_DRIPPER:
+         AmoebeAbleger(x, y);
+         break;
+#endif
+
+        case EL_GAME_OF_LIFE:
+        case EL_BIOMAZE:
+         Life(x, y);
+         break;
+
+        case EL_EXIT_CLOSED:
+         CheckExit(x, y);
+         break;
+
+        case EL_EM_EXIT_CLOSED:
+         CheckExitEM(x, y);
+         break;
+
+        case EL_STEEL_EXIT_CLOSED:
+         CheckExitSteel(x, y);
+         break;
+
+        case EL_EM_STEEL_EXIT_CLOSED:
+         CheckExitSteelEM(x, y);
+         break;
+
+        case EL_SP_EXIT_CLOSED:
+         CheckExitSP(x, y);
+         break;
+
+        case EL_EXPANDABLE_WALL_GROWING:
+        case EL_EXPANDABLE_STEELWALL_GROWING:
+         MauerWaechst(x, y);
+         break;
+
+        case EL_EXPANDABLE_WALL:
+        case EL_EXPANDABLE_WALL_HORIZONTAL:
+        case EL_EXPANDABLE_WALL_VERTICAL:
+        case EL_EXPANDABLE_WALL_ANY:
+        case EL_BD_EXPANDABLE_WALL:
+         MauerAbleger(x, y);
+         break;
+
+        case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
+        case EL_EXPANDABLE_STEELWALL_VERTICAL:
+        case EL_EXPANDABLE_STEELWALL_ANY:
+         MauerAblegerStahl(x, y);
+         break;
+
+        case EL_FLAMES:
+         CheckForDragon(x, y);
+         break;
+
+        case EL_EXPLOSION:
+         break;
+
+        case EL_ELEMENT_SNAPPING:
+        case EL_DIAGONAL_SHRINKING:
+        case EL_DIAGONAL_GROWING:
+       {
+         graphic =
+           el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
+
+         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+         break;
+       }
+
+        default:
+         if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
+           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+         break;
+      }
+    }
+
+#else  // ---------------------------------------------------------------------
+
     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
     {
       StartMoving(x, y);
@@ -10311,6 +10768,8 @@ void GameActions_RND()
     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
 
+#endif // ---------------------------------------------------------------------
+
     if (IS_BELT_ACTIVE(element))
       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
 
@@ -10336,6 +10795,10 @@ void GameActions_RND()
     }
   }
 
+#if 0
+  debug_print_timestamp(0, "- time for MAIN loop:     -->");
+#endif
+
 #if USE_NEW_AMOEBA_CODE
   /* new experimental amoeba growth stuff */
   if (!(FrameCounter % 8))
@@ -10519,6 +10982,11 @@ void GameActions_RND()
     local_player->show_envelope = 0;
   }
 
+#if 0
+  debug_print_timestamp(0, "stop main loop profiling ");
+  printf("----------------------------------------------------------\n");
+#endif
+
   /* use random number generator in every frame to make it less predictable */
   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
     RND(1);
@@ -10565,25 +11033,85 @@ 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
+
+#if 0
+  /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
+  /* only horizontal XOR vertical scroll direction allowed */
+  if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
+    return;
+#endif
+
+#if 1
+  if (bitmap_db_field2 == NULL)
+    bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
+
+  /* needed when blitting directly to same bitmap -- should not be needed with
+     recent SDL libraries, but apparently does not work in 1.2.11 directly */
+  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
+  /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
+  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++)
@@ -13480,9 +14008,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");
   }
 }