rnd-20060816-2-src
[rocksndiamonds.git] / src / game.c
index e3d3fb1936ba1a93257bd68df031853c40f50047..4e4c901c990286248b043614b2716d243a3d822e 100644 (file)
 #define USE_NEW_CONTINUOUS_SNAPPING    (USE_NEW_STUFF          * 1)
 #define USE_GFX_RESET_GFX_ANIMATION    (USE_NEW_STUFF          * 1)
 #define USE_BOTH_SWITCHGATE_SWITCHES   (USE_NEW_STUFF          * 1)
+#define USE_PLAYER_GRAVITY             (USE_NEW_STUFF          * 1)
+#define USE_FIXED_BORDER_RUNNING_GFX   (USE_NEW_STUFF          * 1)
+#define USE_QUICKSAND_BD_ROCK_BUGFIX   (USE_NEW_STUFF          * 0)
 
 #define USE_QUICKSAND_IMPACT_BUGFIX    (USE_NEW_STUFF          * 0)
 
+#define USE_CODE_THAT_BREAKS_SNAKE_BITE        (USE_NEW_STUFF          * 1)
+
+#define USE_UFAST_PLAYER_EXIT_BUGFIX   (USE_NEW_STUFF          * 1)
+#define USE_NEW_GAME_WON               (USE_NEW_STUFF          * 1)
+
+
 /* for DigField() */
 #define DF_NO_PUSH             0
 #define DF_DIG                 1
 #define EX_TYPE_DYNA           (1 << 4)
 #define EX_TYPE_SINGLE_TILE    (EX_TYPE_CENTER | EX_TYPE_BORDER)
 
+#if 1
+
+#define        PANEL_DEACTIVATED(p)    ((p).x < 0 || (p).y < 0)
+
+/* special positions in the game control window (relative to control window) */
+#define XX_LEVEL1              (game.panel.level.x)
+#define XX_LEVEL2              (game.panel.level.x - 1)
+#define YY_LEVEL               (game.panel.level.y)
+#define XX_EMERALDS            (game.panel.gems.x)
+#define YY_EMERALDS            (game.panel.gems.y)
+#define XX_DYNAMITE            (game.panel.inventory.x)
+#define YY_DYNAMITE            (game.panel.inventory.y)
+#define XX_KEYS                        (game.panel.keys.x)
+#define YY_KEYS                        (game.panel.keys.y)
+#define XX_SCORE               (game.panel.score.x)
+#define YY_SCORE               (game.panel.score.y)
+#define XX_TIME1               (game.panel.time.x)
+#define XX_TIME2               (game.panel.time.x + 1)
+#define YY_TIME                        (game.panel.time.y)
+
+#else
+
 /* special positions in the game control window (relative to control window) */
 #define XX_LEVEL               37
 #define YY_LEVEL               20
 #define XX_TIME2               30
 #define YY_TIME                        194
 
+#endif
+
 /* special positions in the game control window (relative to main window) */
-#define DX_LEVEL               (DX + XX_LEVEL)
+#define DX_LEVEL1              (DX + XX_LEVEL1)
+#define DX_LEVEL2              (DX + XX_LEVEL2)
 #define DY_LEVEL               (DY + YY_LEVEL)
 #define DX_EMERALDS            (DX + XX_EMERALDS)
 #define DY_EMERALDS            (DY + YY_EMERALDS)
        ((e) >= NUM_FILE_ELEMENTS ? EL_UNKNOWN : (e))
 #endif
 
-#define GET_TARGET_ELEMENT(e, ch, cv, cs)                              \
+#define RESOLVED_REFERENCE_ELEMENT(be, e)                              \
+       ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
+        (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
+        (be) + (e) - EL_SELF)
+
+#define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                          \
        ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
         (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
         (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
         (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
         (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
-        (e) == EL_CURRENT_CE_SCORE ? (cs) : (e))
+        (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
+        (e) >= EL_LAST_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
+        RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
+        (e))
 
 #define CAN_GROW_INTO(e)                                               \
        ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
@@ -779,6 +821,7 @@ static int get_move_delay_from_stepsize(int move_stepsize)
 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
                               boolean init_game)
 {
+  int player_nr = player->index_nr;
   int move_delay = get_move_delay_from_stepsize(move_stepsize);
   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
 
@@ -790,8 +833,8 @@ static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
 
   if (init_game)
   {
-    player->move_delay       = game.initial_move_delay;
-    player->move_delay_value = game.initial_move_delay_value;
+    player->move_delay       = game.initial_move_delay[player_nr];
+    player->move_delay_value = game.initial_move_delay_value[player_nr];
 
     player->move_delay_value_next = -1;
 
@@ -1194,6 +1237,7 @@ static void InitField(int x, int y, boolean init_game)
        InitField(x, y, init_game);
 #endif
       }
+
       break;
   }
 
@@ -1252,6 +1296,9 @@ inline void DrawGameValue_Emeralds(int value)
 {
   int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
 
+  if (PANEL_DEACTIVATED(game.panel.gems))
+    return;
+
   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
 }
 
@@ -1259,6 +1306,9 @@ inline void DrawGameValue_Dynamite(int value)
 {
   int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
 
+  if (PANEL_DEACTIVATED(game.panel.inventory))
+    return;
+
   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
 }
 
@@ -1267,19 +1317,23 @@ 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_KEYS + i * MINI_TILEX, DY_KEYS,
-                        el2edimg(base_key_graphic + i));
+      DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
     else
       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
-                DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
-                MINI_TILEX, MINI_TILEY, DX_KEYS + i * MINI_TILEX, DY_KEYS);
+                DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
   }
 }
 
@@ -1287,6 +1341,9 @@ inline void DrawGameValue_Score(int value)
 {
   int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2;
 
+  if (PANEL_DEACTIVATED(game.panel.score))
+    return;
+
   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
 }
 
@@ -1295,9 +1352,12 @@ inline void DrawGameValue_Time(int value)
   int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
   int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2;
 
+  if (PANEL_DEACTIVATED(game.panel.time))
+    return;
+
   /* clear background if value just changed its size */
   if (value == 999 || value == 1000)
-    ClearRectangle(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
+    ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
 
   if (value < 1000)
     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2);
@@ -1307,9 +1367,15 @@ inline void DrawGameValue_Time(int value)
 
 inline void DrawGameValue_Level(int value)
 {
+  if (PANEL_DEACTIVATED(game.panel.level))
+    return;
+
   if (level_nr < 100)
-    DrawText(DX_LEVEL, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
+    DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), FONT_TEXT_2);
   else
+#if 1
+    DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), FONT_LEVEL_NUMBER);
+#else
   {
     /* misuse area for displaying emeralds to draw bigger level number */
     DrawTextExt(drawto, DX_EMERALDS, DY_EMERALDS,
@@ -1327,6 +1393,7 @@ inline void DrawGameValue_Level(int value)
 
     /* yes, this is all really ugly :-) */
   }
+#endif
 }
 
 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
@@ -1350,6 +1417,7 @@ void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
 
 void DrawGameDoorValues()
 {
+  int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
   int dynamite_state = 0;
   int key_bits = 0;
   int i, j;
@@ -1398,7 +1466,7 @@ void DrawGameDoorValues()
   }
 
   DrawAllGameValues(local_player->gems_still_needed, dynamite_state,
-                   local_player->score, TimeLeft, key_bits);
+                   local_player->score, time_value, key_bits);
 #endif
 }
 
@@ -1448,6 +1516,18 @@ static void resolve_group_element(int group_element, int recursion_depth)
 }
 #endif
 
+#if 0
+static void replace_reference_element(int base_element, int *element)
+{
+  if (*element >= EL_LAST_CE_8 && *element <= EL_NEXT_CE_8)
+  {
+    *element = base_element + *element - EL_SELF;
+    *element = (*element < EL_CUSTOM_START ? EL_CUSTOM_START :
+               *element > EL_CUSTOM_END   ? EL_CUSTOM_END   : *element);
+  }
+}
+#endif
+
 /*
   =============================================================================
   InitGameEngine()
@@ -1593,8 +1673,9 @@ static void InitGameEngine()
 
 #if 1
   /* dynamically adjust player properties according to level information */
-  game.initial_move_delay_value =
-    get_move_delay_from_stepsize(level.initial_player_stepsize);
+  for (i = 0; i < MAX_PLAYERS; i++)
+    game.initial_move_delay_value[i] =
+      get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
 #else
   /* dynamically adjust player properties according to level information */
   game.initial_move_delay_value =
@@ -1602,8 +1683,10 @@ static void InitGameEngine()
 #endif
 
   /* dynamically adjust player properties according to game engine version */
-  game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
-                            game.initial_move_delay_value : 0);
+  for (i = 0; i < MAX_PLAYERS; i++)
+    game.initial_move_delay[i] =
+      (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
+       game.initial_move_delay_value[i] : 0);
 
   /* ---------- initialize player's initial push delay --------------------- */
 
@@ -1747,6 +1830,11 @@ static void InitGameEngine()
              for (l = 0; l < group->num_elements_resolved; l++)
                trigger_events[group->element_resolved[l]][k] = TRUE;
            }
+#if 1
+           else if (trigger_element == EL_ANY_ELEMENT)
+             for (l = 0; l < MAX_NUM_ELEMENTS; l++)
+               trigger_events[l][k] = TRUE;
+#endif
            else
              trigger_events[trigger_element][k] = TRUE;
          }
@@ -1762,8 +1850,22 @@ static void InitGameEngine()
   {
     if (!IS_CUSTOM_ELEMENT(i))
     {
+#if 1
+      /* set default push delay values (corrected since version 3.0.7-1) */
+      if (game.engine_version < VERSION_IDENT(3,0,7,1))
+      {
+       element_info[i].push_delay_fixed = 2;
+       element_info[i].push_delay_random = 8;
+      }
+      else
+      {
+       element_info[i].push_delay_fixed = 8;
+       element_info[i].push_delay_random = 8;
+      }
+#else
       element_info[i].push_delay_fixed  = game.default_push_delay_fixed;
       element_info[i].push_delay_random = game.default_push_delay_random;
+#endif
     }
   }
 
@@ -1870,6 +1972,29 @@ static void InitGameEngine()
         EL_EMPTY);
     }
   }
+
+#if 0
+  /* ---------- initialize reference elements ------------------------------- */
+  for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+  {
+    int element = EL_CUSTOM_START + i;
+    struct ElementInfo *ei = &element_info[element];
+
+    for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
+      replace_reference_element(element, &ei->content.e[x][y]);
+
+    for (j = 0; j < ei->num_change_pages; j++)
+    {
+      struct ElementChangeInfo *change = &ei->change_page[j];
+
+      replace_reference_element(element, &change->target_element);
+      replace_reference_element(element, &change->trigger_element);
+
+      for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
+       replace_reference_element(element, &change->target_content.e[x][y]);
+    }
+  }
+#endif
 }
 
 int get_num_special_action(int element, int action_first, int action_last)
@@ -1912,8 +2037,11 @@ void InitGame()
   boolean emulate_bd = TRUE;   /* unless non-BOULDERDASH elements found */
   boolean emulate_sb = TRUE;   /* unless non-SOKOBAN     elements found */
   boolean emulate_sp = TRUE;   /* unless non-SUPAPLEX    elements found */
+  boolean do_fading = (game_status == GAME_MODE_MAIN);
   int i, j, x, y;
 
+  game_status = GAME_MODE_PLAYING;
+
   InitGameEngine();
 
   /* don't play tapes over network */
@@ -1964,6 +2092,8 @@ void InitGame()
     player->block_last_field = FALSE;  /* initialized in InitPlayerField() */
     player->block_delay_adjustment = 0;        /* initialized in InitPlayerField() */
 
+    player->gravity = level.initial_player_gravity[i];
+
     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
 
     player->actual_frame_counter = 0;
@@ -1972,6 +2102,8 @@ void InitGame()
 
     player->last_move_dir = MV_NONE;
 
+    player->is_active = FALSE;
+
     player->is_waiting = FALSE;
     player->is_moving = FALSE;
     player->is_auto_moving = FALSE;
@@ -2019,7 +2151,7 @@ void InitGame()
     player->show_envelope = 0;
 
 #if 1
-    SetPlayerMoveSpeed(player, level.initial_player_stepsize, TRUE);
+    SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
 #else
     player->move_delay       = game.initial_move_delay;
     player->move_delay_value = game.initial_move_delay_value;
@@ -2051,6 +2183,10 @@ void InitGame()
 
     player->LevelSolved = FALSE;
     player->GameOver = FALSE;
+
+    player->LevelSolved_GameEnd = FALSE;
+    player->LevelSolved_SaveTape = FALSE;
+    player->LevelSolved_SaveScore = FALSE;
   }
 
   network_player_action_received = FALSE;
@@ -2085,8 +2221,15 @@ void InitGame()
   game.timegate_time_left = 0;
   game.switchgate_pos = 0;
   game.wind_direction = level.wind_direction_initial;
+
+#if !USE_PLAYER_GRAVITY
+#if 1
+  game.gravity = FALSE;
+#else
   game.gravity = level.initial_gravity;
+#endif
   game.explosions_delayed = TRUE;
+#endif
 
   game.lenses_time_left = 0;
   game.magnify_time_left = 0;
@@ -2489,9 +2632,14 @@ void InitGame()
                local_player->jy - MIDPOSY);
   }
 
+  StopAnimation();
+
   if (!game.restart_level)
     CloseDoor(DOOR_CLOSE_1);
 
+  if (do_fading)
+    FadeOut(REDRAW_FIELD);
+
   /* !!! FIX THIS (START) !!! */
   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
   {
@@ -2510,10 +2658,16 @@ void InitGame()
       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
 
     redraw_mask |= REDRAW_FROM_BACKBUFFER;
+
+#if 0
     FadeToFront();
+#endif
   }
   /* !!! FIX THIS (END) !!! */
 
+  if (do_fading)
+    FadeIn(REDRAW_FIELD);
+
   if (!game.restart_level)
   {
     /* copy default game door content to main double buffer */
@@ -2521,6 +2675,11 @@ void InitGame()
               DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
   }
 
+#if 1
+  SetPanelBackground();
+  SetDrawBackgroundMask(REDRAW_DOOR_1);
+#endif
+
   DrawGameDoorValues();
 
   if (!game.restart_level)
@@ -2764,6 +2923,194 @@ void InitAmoebaNr(int x, int y)
   AmoebaCnt2[group_nr]++;
 }
 
+#if USE_NEW_GAME_WON
+
+void GameWon()
+{
+  static boolean score_done = FALSE;
+  static boolean player_done = FALSE;
+  static int game_over_delay = 0;
+  int game_over_delay_value = 50;
+
+  /* do not start end game actions before the player stops moving (to exit) */
+  if (local_player->MovPos)
+    return;
+
+  if (tape.auto_play)          /* tape might already be stopped here */
+    tape.auto_play_level_solved = TRUE;
+
+  if (!local_player->LevelSolved_GameEnd)
+  {
+    local_player->LevelSolved_GameEnd = TRUE;
+    local_player->LevelSolved_SaveTape = tape.recording;
+    local_player->LevelSolved_SaveScore = !tape.playing;
+
+    score_done = FALSE;
+    player_done = FALSE;
+    game_over_delay = 0;
+  }
+
+  PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
+
+  if (TimeLeft > 0)
+  {
+    if (!tape.playing)
+    {
+      if (setup.sound_loops)
+       PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
+                    SND_CTRL_PLAY_LOOP);
+      else
+       PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
+    }
+
+    if (TimeLeft > 100 && TimeLeft % 10 == 0)
+    {
+      TimeLeft -= 10;
+      RaiseScore(level.score[SC_TIME_BONUS] * 10);
+    }
+    else
+    {
+      TimeLeft--;
+      RaiseScore(level.score[SC_TIME_BONUS]);
+    }
+
+    DrawGameValue_Time(TimeLeft);
+
+#if 0
+    if (!tape.playing)
+      Delay(10);
+#endif
+
+    if (TimeLeft <= 0 && !tape.playing && setup.sound_loops)
+      StopSound(SND_GAME_LEVELTIME_BONUS);
+  }
+  else if (level.time == 0 && TimePlayed < 999)        /* level without time limit */
+  {
+    if (!tape.playing)
+    {
+      if (setup.sound_loops)
+       PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
+                    SND_CTRL_PLAY_LOOP);
+      else
+       PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
+    }
+
+    if (TimePlayed < 900 && TimePlayed % 10 == 0)
+    {
+      TimePlayed += 10;
+      RaiseScore(level.score[SC_TIME_BONUS] * 10);
+    }
+    else
+    {
+      TimePlayed++;
+      RaiseScore(level.score[SC_TIME_BONUS]);
+    }
+
+    DrawGameValue_Time(TimePlayed);
+
+    if (TimePlayed >= 999 && !tape.playing && setup.sound_loops)
+      StopSound(SND_GAME_LEVELTIME_BONUS);
+  }
+  else
+  {
+    score_done = TRUE;
+  }
+
+  /* close exit door after last player */
+  if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
+      (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
+       Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
+  {
+    int element = Feld[ExitX][ExitY];
+
+    Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
+                         EL_SP_EXIT_CLOSING);
+
+    PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
+  }
+
+  /* player disappears */
+  if (ExitX >= 0 && ExitY >= 0 && !player_done)
+  {
+    DrawLevelField(ExitX, ExitY);
+
+    player_done = TRUE;
+  }
+
+  game_over_delay++;
+
+  if (game_over_delay < game_over_delay_value || !score_done)
+    return;
+}
+
+void GameEnd()
+{
+  int hi_pos;
+  boolean raise_level = FALSE;
+
+  CloseDoor(DOOR_CLOSE_1);
+
+  if (local_player->LevelSolved_SaveTape)
+  {
+    TapeStop();
+
+    SaveTape(tape.level_nr);           /* Ask to save tape */
+  }
+
+  if (!local_player->LevelSolved_SaveScore)
+  {
+    FadeOut(REDRAW_FIELD);
+
+    game_status = GAME_MODE_MAIN;
+
+    DrawAndFadeInMainMenu(REDRAW_FIELD);
+
+    return;
+  }
+
+  if (level_nr == leveldir_current->handicap_level)
+  {
+    leveldir_current->handicap_level++;
+    SaveLevelSetup_SeriesInfo();
+  }
+
+  if (level_editor_test_game)
+    local_player->score = -1;  /* no highscore when playing from editor */
+  else if (level_nr < leveldir_current->last_level)
+    raise_level = TRUE;                /* advance to next level */
+
+  if ((hi_pos = NewHiScore()) >= 0) 
+  {
+    game_status = GAME_MODE_SCORES;
+
+    DrawHallOfFame(hi_pos);
+
+    if (raise_level)
+    {
+      level_nr++;
+      TapeErase();
+    }
+  }
+  else
+  {
+    FadeOut(REDRAW_FIELD);
+
+    game_status = GAME_MODE_MAIN;
+
+    if (raise_level)
+    {
+      level_nr++;
+      TapeErase();
+    }
+
+    DrawAndFadeInMainMenu(REDRAW_FIELD);
+  }
+
+  local_player->LevelSolved_SaveScore = FALSE;
+}
+
+#else
+
 void GameWon()
 {
   int hi_pos;
@@ -2918,6 +3265,8 @@ void GameWon()
   BackToFront();
 }
 
+#endif
+
 int NewHiScore()
 {
   int k, l;
@@ -3038,6 +3387,8 @@ static void ResetGfxFrame(int x, int y, boolean redraw)
     GfxFrame[x][y] = CustomValue[x][y];
   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
     GfxFrame[x][y] = element_info[element].collect_score;
+  else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
+    GfxFrame[x][y] = ChangeDelay[x][y];
 
   if (redraw && GfxFrame[x][y] != last_gfx_frame)
     DrawLevelGraphicAnimation(x, y, graphic);
@@ -3064,6 +3415,8 @@ static void ResetGfxAnimation(int x, int y)
     GfxFrame[x][y] = CustomValue[x][y];
   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
     GfxFrame[x][y] = element_info[element].collect_score;
+  else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
+    GfxFrame[x][y] = ChangeDelay[x][y];
 #endif
 
 #if USE_GFX_RESET_GFX_ANIMATION
@@ -3104,6 +3457,8 @@ void InitMovingField(int x, int y, int direction)
     GfxFrame[x][y] = CustomValue[x][y];
   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
     GfxFrame[x][y] = element_info[element].collect_score;
+  else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
+    GfxFrame[x][y] = ChangeDelay[x][y];
 #endif
 
   /* this is needed for CEs with property "can move" / "not moving" */
@@ -5772,6 +6127,8 @@ static void TurnRound(int x, int y)
     GfxFrame[x][y] = CustomValue[x][y];
   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
     GfxFrame[x][y] = element_info[element].collect_score;
+  else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
+    GfxFrame[x][y] = ChangeDelay[x][y];
 #endif
 }
 
@@ -5822,7 +6179,12 @@ void StartMoving(int x, int y)
        started_moving = TRUE;
 
        Feld[x][y] = EL_QUICKSAND_EMPTYING;
+#if USE_QUICKSAND_BD_ROCK_BUGFIX
+       if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
+         Store[x][y] = EL_ROCK;
+#else
        Store[x][y] = EL_ROCK;
+#endif
 
        PlayLevelSoundAction(x, y, ACTION_EMPTYING);
       }
@@ -7765,7 +8127,8 @@ void MauerAbleger(int ax, int ay)
 
   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
       element == EL_EXPANDABLE_WALL_ANY ||
-      element == EL_EXPANDABLE_WALL)
+      element == EL_EXPANDABLE_WALL ||
+      element == EL_BD_EXPANDABLE_WALL)
   {
     if (links_frei)
     {
@@ -7991,7 +8354,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
      CA_ARG_MAX);
 
   int action_arg_number_reset =
-    (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize :
+    (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
      action_type == CA_SET_LEVEL_TIME ? level.time :
      action_type == CA_SET_LEVEL_SCORE ? 0 :
@@ -8117,6 +8480,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       break;
     }
 
+#if !USE_PLAYER_GRAVITY
     case CA_SET_LEVEL_GRAVITY:
     {
       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
@@ -8125,6 +8489,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
                      game.gravity);
       break;
     }
+#endif
 
     case CA_SET_LEVEL_WIND:
     {
@@ -8211,6 +8576,10 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
            action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
                           CA_MODE_MULTIPLY);
          }
+         else if (action_arg == CA_ARG_NUMBER_RESET)
+         {
+           action_arg_number = level.initial_player_stepsize[i];
+         }
 
          move_stepsize =
            getModifiedActionNumber(move_stepsize,
@@ -8263,6 +8632,25 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       break;
     }
 
+#if USE_PLAYER_GRAVITY
+    case CA_SET_PLAYER_GRAVITY:
+    {
+      for (i = 0; i < MAX_PLAYERS; i++)
+      {
+       if (trigger_player_bits & (1 << i))
+       {
+         stored_player[i].gravity =
+           (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
+            action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
+            action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
+            stored_player[i].gravity);
+       }
+      }
+
+      break;
+    }
+#endif
+
     case CA_SET_PLAYER_ARTWORK:
     {
       for (i = 0; i < MAX_PLAYERS; i++)
@@ -8306,19 +8694,26 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       printf("::: CE value == %d\n", CustomValue[x][y]);
 #endif
 
-      if (CustomValue[x][y] == 0 && last_ce_value > 0)
+      if (CustomValue[x][y] != last_ce_value)
       {
+       CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
+       CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
+
+       if (CustomValue[x][y] == 0)
+       {
 #if 0
-       printf("::: CE_VALUE_GETS_ZERO\n");
+         printf("::: CE_VALUE_GETS_ZERO\n");
 #endif
 
-       CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
-       CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
+         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
+         CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
 
 #if 0
-       printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
+         printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
 #endif
+       }
       }
+
 #endif
 
       break;
@@ -8335,19 +8730,48 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       printf("::: CE score == %d\n", ei->collect_score);
 #endif
 
-      if (ei->collect_score == 0 && last_ce_score > 0)
+      if (ei->collect_score != last_ce_score)
       {
+       CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
+       CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
+
+       if (ei->collect_score == 0)
+       {
+         int xx, yy;
+
 #if 0
-       printf("::: CE_SCORE_GETS_ZERO\n");
+         printf("::: CE_SCORE_GETS_ZERO\n");
 #endif
 
-       CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
-       CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
+         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
+         CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
 
 #if 0
-       printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
+         printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
 #endif
+
+#if 1
+         /*
+           This is a very special case that seems to be a mixture between
+           CheckElementChange() and CheckTriggeredElementChange(): while
+           the first one only affects single elements that are triggered
+           directly, the second one affects multiple elements in the playfield
+           that are triggered indirectly by another element. This is a third
+           case: Changing the CE score always affects multiple identical CEs,
+           so every affected CE must be checked, not only the single CE for
+           which the CE score was changed in the first place (as every instance
+           of that CE shares the same CE score, and therefore also can change)!
+         */
+         SCAN_PLAYFIELD(xx, yy)
+         {
+           if (Feld[xx][yy] == element)
+             CheckElementChange(xx, yy, element, EL_UNDEFINED,
+                                CE_SCORE_GETS_ZERO);
+         }
+#endif
+       }
       }
+
 #endif
 
       break;
@@ -8375,11 +8799,18 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change)
 #if USE_NEW_CUSTOM_VALUE
   int last_ce_value = CustomValue[x][y];
 #endif
-  boolean add_player = (ELEM_IS_PLAYER(new_element) &&
-                       IS_WALKABLE(old_element));
+  boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
+  boolean add_player_onto_element = (new_element_is_player &&
+#if USE_CODE_THAT_BREAKS_SNAKE_BITE
+                                    /* this breaks SnakeBite when a snake is
+                                       halfway through a door that closes */
+                                    /* NOW FIXED AT LEVEL INIT IN files.c */
+                                    new_element != EL_SOKOBAN_FIELD_PLAYER &&
+#endif
+                                    IS_WALKABLE(old_element));
 
 #if 0
-  /* check if element under player changes from accessible to unaccessible
+  /* check if element under the player changes from accessible to unaccessible
      (needed for special case of dropping element which then changes) */
   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
@@ -8390,7 +8821,7 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change)
   }
 #endif
 
-  if (!add_player)
+  if (!add_player_onto_element)
   {
     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
       RemoveMovingField(x, y);
@@ -8428,8 +8859,9 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change)
   }
 
 #if 1
-  /* check if element under player changes from accessible to unaccessible
+  /* check if element under the player changes from accessible to unaccessible
      (needed for special case of dropping element which then changes) */
+  /* (must be checked after creating new element for walkable group elements) */
   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
   {
@@ -8440,7 +8872,7 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change)
 #endif
 
   /* "ChangeCount" not set yet to allow "entered by player" change one time */
-  if (ELEM_IS_PLAYER(new_element))
+  if (new_element_is_player)
     RelocatePlayer(x, y, new_element);
 
   if (is_change)
@@ -8601,7 +9033,7 @@ static boolean ChangeElement(int x, int y, int element, int page)
          ChangeEvent[ex][ey] = ChangeEvent[x][y];
 
          content_element = change->target_content.e[xx][yy];
-         target_element = GET_TARGET_ELEMENT(content_element, change,
+         target_element = GET_TARGET_ELEMENT(element, content_element, change,
                                              ce_value, ce_score);
 
          CreateElementFromChange(ex, ey, target_element);
@@ -8623,7 +9055,7 @@ static boolean ChangeElement(int x, int y, int element, int page)
   }
   else
   {
-    target_element = GET_TARGET_ELEMENT(change->target_element, change,
+    target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
                                        ce_value, ce_score);
 
     if (element == EL_DIAGONAL_GROWING ||
@@ -9413,10 +9845,6 @@ void StartGameActions(boolean init_network_game, boolean record_tape,
   }
 #endif
 
-  StopAnimation();
-
-  game_status = GAME_MODE_PLAYING;
-
   InitGame();
 }
 
@@ -9563,6 +9991,10 @@ void GameActions_EM_Main()
   for (i = 0; i < MAX_PLAYERS; i++)
     effective_action[i] = stored_player[i].effective_action;
 
+#if 0
+  printf("::: %04d: %08x\n", FrameCounter, effective_action[0]);
+#endif
+
   GameActions_EM(effective_action, warp_mode);
 
   CheckLevelTime();
@@ -9815,6 +10247,17 @@ void GameActions_RND()
 
       GfxFrame[x][y] = element_info[element].collect_score;
 
+#if 1
+      if (GfxFrame[x][y] != old_gfx_frame)
+#endif
+       DrawLevelGraphicAnimation(x, y, graphic);
+    }
+    else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
+    {
+      int old_gfx_frame = GfxFrame[x][y];
+
+      GfxFrame[x][y] = ChangeDelay[x][y];
+
 #if 1
       if (GfxFrame[x][y] != old_gfx_frame)
 #endif
@@ -9922,7 +10365,8 @@ void GameActions_RND()
     else if (element == EL_EXPANDABLE_WALL ||
             element == EL_EXPANDABLE_WALL_HORIZONTAL ||
             element == EL_EXPANDABLE_WALL_VERTICAL ||
-            element == EL_EXPANDABLE_WALL_ANY)
+            element == EL_EXPANDABLE_WALL_ANY ||
+            element == EL_BD_EXPANDABLE_WALL)
       MauerAbleger(x, y);
     else if (element == EL_FLAMES)
       CheckForDragon(x, y);
@@ -10267,11 +10711,15 @@ static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
 
 static void CheckGravityMovement(struct PlayerInfo *player)
 {
+#if USE_PLAYER_GRAVITY
+  if (player->gravity && !player->programmed_action)
+#else
   if (game.gravity && !player->programmed_action)
+#endif
   {
     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
-    boolean player_is_snapping = player->effective_action & JOY_BUTTON_1;
+    boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
     int jx = player->jx, jy = player->jy;
     boolean player_is_moving_to_valid_field =
       (!player_is_snapping &&
@@ -10289,7 +10737,11 @@ static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
 {
   return CheckGravityMovement(player);
 
+#if USE_PLAYER_GRAVITY
+  if (player->gravity && !player->programmed_action)
+#else
   if (game.gravity && !player->programmed_action)
+#endif
   {
     int jx = player->jx, jy = player->jy;
     boolean field_under_player_is_free =
@@ -10422,7 +10874,14 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
 
   PlayerVisit[jx][jy] = FrameCounter;
 
+#if USE_UFAST_PLAYER_EXIT_BUGFIX
+  player->is_moving = TRUE;
+#endif
+
+#if 1
+  /* should better be called in MovePlayer(), but this breaks some tapes */
   ScrollPlayer(player, SCROLL_INIT);
+#endif
 
   return MP_MOVING;
 }
@@ -10490,6 +10949,8 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
     player->move_delay_value = original_move_delay_value;
   }
 
+  player->is_active = FALSE;
+
   if (player->last_move_dir & MV_HORIZONTAL)
   {
     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
@@ -10501,6 +10962,17 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
   }
 
+#if USE_FIXED_BORDER_RUNNING_GFX
+  if (!moved && !player->is_active)
+  {
+    player->is_moving = FALSE;
+    player->is_digging = FALSE;
+    player->is_collecting = FALSE;
+    player->is_snapping = FALSE;
+    player->is_pushing = FALSE;
+  }
+#endif
+
   jx = player->jx;
   jy = player->jy;
 
@@ -10607,6 +11079,11 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
     player->is_dropping = FALSE;
     player->is_dropping_pressed = FALSE;
     player->drop_pressed_delay = 0;
+
+#if 0
+    /* should better be called here than above, but this breaks some tapes */
+    ScrollPlayer(player, SCROLL_INIT);
+#endif
   }
   else
   {
@@ -10672,8 +11149,13 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
        last_field_block_delay += player->move_delay_value;
 
        /* when blocking enabled, prevent moving up despite gravity */
+#if USE_PLAYER_GRAVITY
+       if (player->gravity && player->MovDir == MV_UP)
+         block_delay_adjustment = -1;
+#else
        if (game.gravity && player->MovDir == MV_UP)
          block_delay_adjustment = -1;
+#endif
       }
 
       /* add block delay adjustment (also possible when not blocking) */
@@ -11595,6 +12077,8 @@ int DigField(struct PlayerInfo *player,
           game.engine_version >= VERSION_IDENT(2,2,0,0))
     old_element = Back[jx][jy];
 
+  /* checking here causes player to move into acid even if the current field
+     cannot be left to that direction */
 #if 0
 #if USE_FIXED_DONT_RUN_INTO
   if (player_can_move && DONT_RUN_INTO(element))
@@ -11616,6 +12100,43 @@ int DigField(struct PlayerInfo *player,
 #endif
 #endif
 
+#if 1  /* ------------------------------ NEW ------------------------------ */
+
+  if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
+    return MP_NO_ACTION;       /* field has no opening in this direction */
+
+  if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
+    return MP_NO_ACTION;       /* field has no opening in this direction */
+
+#if USE_FIXED_DONT_RUN_INTO
+  if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
+  {
+    SplashAcid(x, y);
+#if 1
+    Feld[jx][jy] = player->artwork_element;
+#else
+    Feld[jx][jy] = EL_PLAYER_1;
+#endif
+    InitMovingField(jx, jy, MV_DOWN);
+    Store[jx][jy] = EL_ACID;
+    ContinueMoving(jx, jy);
+    BuryPlayer(player);
+
+    return MP_DONT_RUN_INTO;
+  }
+#endif
+
+#if USE_FIXED_DONT_RUN_INTO
+  if (player_can_move && DONT_RUN_INTO(element))
+  {
+    TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
+
+    return MP_DONT_RUN_INTO;
+  }
+#endif
+
+#else  /* ------------------------------ OLD ------------------------------ */
+
 #if 1
 #if USE_FIXED_DONT_RUN_INTO
   if (player_can_move && DONT_RUN_INTO(element))
@@ -11633,6 +12154,7 @@ int DigField(struct PlayerInfo *player,
   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
     return MP_NO_ACTION;       /* field has no opening in this direction */
 
+  /* checking here causes player to explode when moving into acid */
 #if 1
 #if USE_FIXED_DONT_RUN_INTO
   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
@@ -11649,6 +12171,8 @@ int DigField(struct PlayerInfo *player,
 #endif
 #endif
 
+#endif /* ------------------------------ END ------------------------------ */
+
 #if 0
 #if USE_FIXED_DONT_RUN_INTO
   if (player_can_move && DONT_RUN_INTO(element))
@@ -11701,10 +12225,17 @@ int DigField(struct PlayerInfo *player,
     return MP_NO_ACTION;
   }
 
+#if USE_PLAYER_GRAVITY
+  if (player->gravity && is_player && !player->is_auto_moving &&
+      canFallDown(player) && move_direction != MV_DOWN &&
+      !canMoveToValidFieldWithGravity(jx, jy, move_direction))
+    return MP_NO_ACTION;       /* player cannot walk here due to gravity */
+#else
   if (game.gravity && is_player && !player->is_auto_moving &&
       canFallDown(player) && move_direction != MV_DOWN &&
       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
     return MP_NO_ACTION;       /* player cannot walk here due to gravity */
+#endif
 
   if (player_can_move &&
       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
@@ -11768,23 +12299,50 @@ int DigField(struct PlayerInfo *player,
       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
        return MP_NO_ACTION;
     }
+    else if (IS_EMC_GATE(element))
+    {
+      if (!player->key[EMC_GATE_NR(element)])
+       return MP_NO_ACTION;
+    }
+    else if (IS_EMC_GATE_GRAY(element))
+    {
+      if (!player->key[EMC_GATE_GRAY_NR(element)])
+       return MP_NO_ACTION;
+    }
+    else if (IS_EMC_GATE_GRAY_ACTIVE(element))
+    {
+      if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
+       return MP_NO_ACTION;
+    }
     else if (IS_SP_PORT(element))
     {
       if (element == EL_SP_GRAVITY_PORT_LEFT ||
          element == EL_SP_GRAVITY_PORT_RIGHT ||
          element == EL_SP_GRAVITY_PORT_UP ||
          element == EL_SP_GRAVITY_PORT_DOWN)
+#if USE_PLAYER_GRAVITY
+       player->gravity = !player->gravity;
+#else
        game.gravity = !game.gravity;
+#endif
       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
               element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
               element == EL_SP_GRAVITY_ON_PORT_UP ||
               element == EL_SP_GRAVITY_ON_PORT_DOWN)
+#if USE_PLAYER_GRAVITY
+       player->gravity = TRUE;
+#else
        game.gravity = TRUE;
+#endif
       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
               element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
               element == EL_SP_GRAVITY_OFF_PORT_UP ||
               element == EL_SP_GRAVITY_OFF_PORT_DOWN)
+#if USE_PLAYER_GRAVITY
+       player->gravity = FALSE;
+#else
        game.gravity = FALSE;
+#endif
     }
 
     /* automatically move to the next field with double speed */
@@ -12004,6 +12562,7 @@ int DigField(struct PlayerInfo *player,
     }
 
     player->is_pushing = TRUE;
+    player->is_active = TRUE;
 
     if (!(IN_LEV_FIELD(nextx, nexty) &&
          (IS_FREE(nextx, nexty) ||
@@ -12256,7 +12815,10 @@ int DigField(struct PlayerInfo *player,
   if (is_player)               /* function can also be called by EL_PENGUIN */
   {
     if (Feld[x][y] != element)         /* really digged/collected something */
+    {
       player->is_collecting = !player->is_digging;
+      player->is_active = TRUE;
+    }
   }
 
   return MP_MOVING;
@@ -12325,6 +12887,7 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
     return FALSE;
 
   player->is_snapping = TRUE;
+  player->is_active = TRUE;
 
   if (player->MovPos == 0)
   {
@@ -12863,8 +13426,20 @@ void RequestQuitGame(boolean ask_if_really_quit)
     else
 #endif
     {
-      game_status = GAME_MODE_MAIN;
-      DrawMainMenu();
+      if (!ask_if_really_quit || level_editor_test_game)
+      {
+       game_status = GAME_MODE_MAIN;
+
+       DrawMainMenu();
+      }
+      else
+      {
+       FadeOut(REDRAW_FIELD);
+
+       game_status = GAME_MODE_MAIN;
+
+       DrawAndFadeInMainMenu(REDRAW_FIELD);
+      }
     }
   }
   else