fixed and improved single step mode for all game engines
[rocksndiamonds.git] / src / game.c
index fb6691a72849769c94eacd0015a628acb54ca1af..8978e70750489ebe4c15af503f0d3ae9c461537c 100644 (file)
@@ -19,6 +19,7 @@
 #include "files.h"
 #include "tape.h"
 #include "network.h"
+#include "anim.h"
 
 
 /* DEBUG SETTINGS */
@@ -826,6 +827,14 @@ static struct GamePanelControlInfo game_panel_controls[] =
 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
 #define HALVE_PLAYER_SPEED(p)  (DOUBLE_MOVE_DELAY((p)->move_delay_value))
 
+/* values for scroll positions */
+#define SCROLL_POSITION_X(x)   ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
+                                (x) > SBX_Right + MIDPOSX ? SBX_Right :\
+                                (x) - MIDPOSX)
+#define SCROLL_POSITION_Y(y)   ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
+                                (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
+                                (y) - MIDPOSY)
+
 /* values for other actions */
 #define MOVE_STEPSIZE_NORMAL   (TILEX / MOVE_DELAY_NORMAL_SPEED)
 #define MOVE_STEPSIZE_MIN      (1)
@@ -1610,7 +1619,6 @@ void GetPlayerConfig()
   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
 
   SetAudioMode(setup.sound);
-  InitJoysticks();
 }
 
 int GetElementFromGroupElement(int element)
@@ -2049,6 +2057,16 @@ static int compareGamePanelOrderInfo(const void *object1, const void *object2)
   return compare_result;
 }
 
+int getPlayerInventorySize(int player_nr)
+{
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+    return level.native_em_level->ply[player_nr]->dynamite;
+  else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+    return level.native_sp_level->game_sp->red_disk_count;
+  else
+    return stored_player[player_nr].inventory_size;
+}
+
 void InitGameControlValues()
 {
   int i;
@@ -2186,15 +2204,8 @@ void UpdateGameControlValues()
            get_key_element_from_nr(k);
       }
 
-      if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
-       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
-         level.native_em_level->ply[i]->dynamite;
-      else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
-       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
-         level.native_sp_level->game_sp->red_disk_count;
-      else
-       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
-         stored_player[i].inventory_size;
+      game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+       getPlayerInventorySize(i);
 
       if (stored_player[i].num_white_keys > 0)
        game_panel_controls[GAME_PANEL_KEY_WHITE].value =
@@ -2221,15 +2232,8 @@ void UpdateGameControlValues()
          get_key_element_from_nr(k);
     }
 
-    if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
-      game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
-       level.native_em_level->ply[player_nr]->dynamite;
-    else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
-      game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
-       level.native_sp_level->game_sp->red_disk_count;
-    else
-      game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
-       stored_player[player_nr].inventory_size;
+    game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+      getPlayerInventorySize(player_nr);
 
     if (stored_player[player_nr].num_white_keys > 0)
       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
@@ -2694,6 +2698,9 @@ static void InitGameEngine()
   game.use_block_last_field_bug =
     (game.engine_version < VERSION_IDENT(3,1,1,0));
 
+  game_em.use_single_button =
+    (game.engine_version > VERSION_IDENT(4,0,0,2));
+
   /* ---------------------------------------------------------------------- */
 
   /* set maximal allowed number of custom element changes per game frame */
@@ -3048,6 +3055,7 @@ static void InitGameEngine()
      SNAPSHOT_MODE_EVERY_MOVE :
      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
+  game.snapshot.save_snapshot = FALSE;
 }
 
 int get_num_special_action(int element, int action_first, int action_last)
@@ -3102,9 +3110,6 @@ void InitGame()
 
   SetGameStatus(GAME_MODE_PLAYING);
 
-  /* needed if different viewport properties defined for playing */
-  ChangeViewportPropertiesIfNeeded();
-
   if (level_editor_test_game)
     FadeSkipNextFadeIn();
   else
@@ -3119,6 +3124,9 @@ void InitGame()
 
   FadeOut(fade_mask);
 
+  /* needed if different viewport properties defined for playing */
+  ChangeViewportPropertiesIfNeeded();
+
   ClearField();
 
   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
@@ -3211,6 +3219,13 @@ void InitGame()
     player->is_bored = FALSE;
     player->is_sleeping = FALSE;
 
+    player->was_waiting = TRUE;
+    player->was_moving = FALSE;
+    player->was_snapping = FALSE;
+    player->was_dropping = FALSE;
+
+    player->force_dropping = FALSE;
+
     player->frame_counter_bored = -1;
     player->frame_counter_sleeping = -1;
 
@@ -3893,23 +3908,13 @@ void InitGame()
       }
     }
 
-    scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
-               start_x > SBX_Right + MIDPOSX ? SBX_Right :
-               start_x - MIDPOSX);
-
-    scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
-               start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
-               start_y - MIDPOSY);
+    scroll_x = SCROLL_POSITION_X(start_x);
+    scroll_y = SCROLL_POSITION_Y(start_y);
   }
   else
   {
-    scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
-               local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
-               local_player->jx - MIDPOSX);
-
-    scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
-               local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
-               local_player->jy - MIDPOSY);
+    scroll_x = SCROLL_POSITION_X(local_player->jx);
+    scroll_y = SCROLL_POSITION_Y(local_player->jy);
   }
 
   /* !!! FIX THIS (START) !!! */
@@ -4030,13 +4035,24 @@ void InitGame()
   SaveEngineSnapshotToListInitial();
 }
 
-void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
+void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
+                       int actual_player_x, int actual_player_y)
 {
   /* this is used for non-R'n'D game engines to update certain engine values */
 
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+  {
+    actual_player_x = correctLevelPosX_EM(actual_player_x);
+    actual_player_y = correctLevelPosY_EM(actual_player_y);
+  }
+
   /* needed to determine if sounds are played within the visible screen area */
   scroll_x = actual_scroll_x;
   scroll_y = actual_scroll_y;
+
+  /* needed to get player position for "follow finger" playing input method */
+  local_player->jx = actual_player_x;
+  local_player->jy = actual_player_y;
 }
 
 void InitMovDir(int x, int y)
@@ -4449,7 +4465,8 @@ void GameEnd()
     SaveLevelSetup_SeriesInfo();
   }
 
-  if (level_nr < leveldir_current->last_level)
+  if (setup.increment_levels &&
+      level_nr < leveldir_current->last_level)
     raise_level = TRUE;                        /* advance to next level */
 
   if ((hi_pos = NewHiScore()) >= 0) 
@@ -4482,6 +4499,7 @@ int NewHiScore()
 {
   int k, l;
   int position = -1;
+  boolean one_score_entry_per_name = !program.many_scores_per_name;
 
   LoadScore(level_nr);
 
@@ -4499,13 +4517,15 @@ int NewHiScore()
       {
        int m = MAX_SCORE_ENTRIES - 1;
 
-#ifdef ONE_PER_NAME
-       for (l = k; l < MAX_SCORE_ENTRIES; l++)
-         if (strEqual(setup.player_name, highscore[l].Name))
-           m = l;
-       if (m == k)     /* player's new highscore overwrites his old one */
-         goto put_into_list;
-#endif
+       if (one_score_entry_per_name)
+       {
+         for (l = k; l < MAX_SCORE_ENTRIES; l++)
+           if (strEqual(setup.player_name, highscore[l].Name))
+             m = l;
+
+         if (m == k)   /* player's new highscore overwrites his old one */
+           goto put_into_list;
+       }
 
        for (l = m; l > k; l--)
        {
@@ -4514,22 +4534,19 @@ int NewHiScore()
        }
       }
 
-#ifdef ONE_PER_NAME
       put_into_list:
-#endif
+
       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
       highscore[k].Score = local_player->score_final; 
       position = k;
+
       break;
     }
-
-#ifdef ONE_PER_NAME
-    else if (!strncmp(setup.player_name, highscore[k].Name,
+    else if (one_score_entry_per_name &&
+            !strncmp(setup.player_name, highscore[k].Name,
                      MAX_PLAYER_NAME_LEN))
       break;   /* player already there with a higher score */
-#endif
-
   }
 
   if (position >= 0) 
@@ -4576,11 +4593,10 @@ void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
   }
 }
 
-static void ResetGfxFrame(int x, int y, boolean redraw)
+static void ResetGfxFrame(int x, int y)
 {
   int element = Feld[x][y];
   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
-  int last_gfx_frame = GfxFrame[x][y];
 
   if (graphic_info[graphic].anim_global_sync)
     GfxFrame[x][y] = FrameCounter;
@@ -4590,9 +4606,6 @@ static void ResetGfxFrame(int x, int y, boolean redraw)
     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);
 }
 
 static void ResetGfxAnimation(int x, int y)
@@ -4601,7 +4614,7 @@ static void ResetGfxAnimation(int x, int y)
   GfxDir[x][y] = MovDir[x][y];
   GfxFrame[x][y] = 0;
 
-  ResetGfxFrame(x, y, FALSE);
+  ResetGfxFrame(x, y);
 }
 
 static void ResetRandomAnimationValue(int x, int y)
@@ -4894,6 +4907,7 @@ static void setScreenCenteredToAllPlayers(int *sx, int *sy)
 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
                        boolean center_screen, boolean quick_relocation)
 {
+  unsigned int frame_delay_value_old = GetVideoFrameDelay();
   boolean ffwd_delay = (tape.playing && tape.fast_forward);
   boolean no_delay = (tape.warp_forward);
   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
@@ -4913,36 +4927,21 @@ void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
   {
     /* relocation _with_ centering of screen */
 
-    new_scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
-                   x > SBX_Right + MIDPOSX ? SBX_Right :
-                   x - MIDPOSX);
-
-    new_scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
-                   y > SBY_Lower + MIDPOSY ? SBY_Lower :
-                   y - MIDPOSY);
+    new_scroll_x = SCROLL_POSITION_X(x);
+    new_scroll_y = SCROLL_POSITION_Y(y);
   }
   else
   {
     /* relocation _without_ centering of screen */
 
-    int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
-                          old_x > SBX_Right + MIDPOSX ? SBX_Right :
-                          old_x - MIDPOSX);
-
-    int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
-                          old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
-                          old_y - MIDPOSY);
-
+    int center_scroll_x = SCROLL_POSITION_X(old_x);
+    int center_scroll_y = SCROLL_POSITION_Y(old_y);
     int offset_x = x + (scroll_x - center_scroll_x);
     int offset_y = y + (scroll_y - center_scroll_y);
 
-    new_scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
-                   offset_x > SBX_Right + MIDPOSX ? SBX_Right :
-                   offset_x - MIDPOSX);
-
-    new_scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
-                   offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
-                   offset_y - MIDPOSY);
+    /* for new screen position, apply previous offset to center position */
+    new_scroll_x = SCROLL_POSITION_X(offset_x);
+    new_scroll_y = SCROLL_POSITION_Y(offset_y);
   }
 
   if (quick_relocation)
@@ -4961,6 +4960,8 @@ void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
 
   ScrollScreen(NULL, SCROLL_GO_ON);    /* scroll last frame to full tile */
 
+  SetVideoFrameDelay(wait_delay_value);
+
   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
   {
     int dx = 0, dy = 0;
@@ -4983,16 +4984,15 @@ void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
 
     /* scroll in two steps of half tile size to make things smoother */
     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
-    Delay(wait_delay_value);
 
     /* scroll second step to align at full tile size */
-    BackToFront();
-    Delay(wait_delay_value);
+    BlitScreenToBitmap(window);
   }
 
   DrawAllPlayers();
   BackToFront();
-  Delay(wait_delay_value);
+
+  SetVideoFrameDelay(frame_delay_value_old);
 }
 
 void RelocatePlayer(int jx, int jy, int el_player_raw)
@@ -5042,8 +5042,7 @@ void RelocatePlayer(int jx, int jy, int el_player_raw)
 
       DrawPlayer(player);
 
-      BackToFront();
-      Delay(wait_delay_value);
+      BackToFront_WithFrameDelay(wait_delay_value);
     }
 
     DrawPlayer(player);                /* needed here only to cleanup last field */
@@ -7061,7 +7060,7 @@ static void TurnRound(int x, int y)
   if (MovDelay[x][y])
     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
 
-  ResetGfxFrame(x, y, FALSE);
+  ResetGfxFrame(x, y);
 }
 
 static boolean JustBeingPushed(int x, int y)
@@ -10686,32 +10685,28 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
 
 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
 {
-  static boolean player_was_moving = FALSE;
-  static boolean player_was_snapping = FALSE;
-  static boolean player_was_dropping = FALSE;
-
-  if ((!player->is_moving  && player_was_moving) ||
-      (player->MovPos == 0 && player_was_moving) ||
-      (player->is_snapping && !player_was_snapping) ||
-      (player->is_dropping && !player_was_dropping))
+  if ((!player->is_moving  && player->was_moving) ||
+      (player->MovPos == 0 && player->was_moving) ||
+      (player->is_snapping && !player->was_snapping) ||
+      (player->is_dropping && !player->was_dropping))
   {
-    if (!SaveEngineSnapshotToList())
+    if (!CheckSaveEngineSnapshotToList())
       return;
 
-    player_was_moving = FALSE;
-    player_was_snapping = TRUE;
-    player_was_dropping = TRUE;
+    player->was_moving = FALSE;
+    player->was_snapping = TRUE;
+    player->was_dropping = TRUE;
   }
   else
   {
     if (player->is_moving)
-      player_was_moving = TRUE;
+      player->was_moving = TRUE;
 
     if (!player->is_snapping)
-      player_was_snapping = FALSE;
+      player->was_snapping = FALSE;
 
     if (!player->is_dropping)
-      player_was_dropping = FALSE;
+      player->was_dropping = FALSE;
   }
 }
 
@@ -10721,7 +10716,9 @@ static void CheckSingleStepMode(struct PlayerInfo *player)
   {
     /* as it is called "single step mode", just return to pause mode when the
        player stopped moving after one tile (or never starts moving at all) */
-    if (!player->is_moving && !player->is_pushing)
+    if (!player->is_moving &&
+       !player->is_pushing &&
+       !player->is_dropping_pressed)
     {
       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
       SnapField(player, 0, 0);                 /* stop snapping */
@@ -10956,9 +10953,11 @@ void StartGameActions(boolean init_network_game, boolean record_tape,
   InitGame();
 }
 
-void GameActions()
+void GameActionsExt()
 {
+#if 0
   static unsigned int game_frame_delay = 0;
+#endif
   unsigned int game_frame_delay_value;
   byte *recorded_player_action;
   byte summarized_player_action = 0;
@@ -11036,6 +11035,9 @@ void GameActions()
   if (tape.playing && tape.warp_forward && !tape.pausing)
     game_frame_delay_value = 0;
 
+  SetVideoFrameDelay(game_frame_delay_value);
+
+#if 0
 #if 0
   /* ---------- main game synchronization point ---------- */
 
@@ -11047,6 +11049,7 @@ void GameActions()
   /* ---------- main game synchronization point ---------- */
 
   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
+#endif
 #endif
 
   if (network_playing && !network_player_action_received)
@@ -11229,6 +11232,24 @@ void GameActions()
   }
 }
 
+static void GameActions_CheckSaveEngineSnapshot()
+{
+  if (!game.snapshot.save_snapshot)
+    return;
+
+  // clear flag for saving snapshot _before_ saving snapshot
+  game.snapshot.save_snapshot = FALSE;
+
+  SaveEngineSnapshotToList();
+}
+
+void GameActions()
+{
+  GameActionsExt();
+
+  GameActions_CheckSaveEngineSnapshot();
+}
+
 void GameActions_EM_Main()
 {
   byte effective_action[MAX_PLAYERS];
@@ -11251,6 +11272,14 @@ void GameActions_SP_Main()
     effective_action[i] = stored_player[i].effective_action;
 
   GameActions_SP(effective_action, warp_mode);
+
+  for (i = 0; i < MAX_PLAYERS; i++)
+  {
+    if (stored_player[i].force_dropping)
+      stored_player[i].action |= KEY_BUTTON_DROP;
+
+    stored_player[i].force_dropping = FALSE;
+  }
 }
 
 void GameActions_RND_Main()
@@ -11261,7 +11290,7 @@ void GameActions_RND_Main()
 void GameActions_RND()
 {
   int magic_wall_x = 0, magic_wall_y = 0;
-  int i, x, y, element, graphic;
+  int i, x, y, element, graphic, last_gfx_frame;
 
   InitPlayfieldScanModeVars();
 
@@ -11448,8 +11477,12 @@ void GameActions_RND()
   {
     element = Feld[x][y];
     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+    last_gfx_frame = GfxFrame[x][y];
+
+    ResetGfxFrame(x, y);
 
-    ResetGfxFrame(x, y, TRUE);
+    if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
+      DrawLevelGraphicAnimation(x, y, graphic);
 
     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
        IS_NEXT_FRAME(GfxFrame[x][y], graphic))
@@ -11494,8 +11527,12 @@ void GameActions_RND()
       if (IS_GEM(element) || element == EL_SP_INFOTRON)
        TEST_DrawTwinkleOnField(x, y);
     }
-    else if ((element == EL_ACID ||
-             element == EL_EXIT_OPEN ||
+    else if (element == EL_ACID)
+    {
+      if (!Stop[x][y])
+       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+    }
+    else if ((element == EL_EXIT_OPEN ||
              element == EL_EM_EXIT_OPEN ||
              element == EL_SP_EXIT_OPEN ||
              element == EL_STEEL_EXIT_OPEN ||
@@ -12059,7 +12096,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
       AdvanceFrameAndPlayerCounters(player->index_nr);
 
       DrawAllPlayers();
-      BackToFront();
+      BackToFront_WithFrameDelay(0);
     }
 
     player->move_delay_value = original_move_delay_value;
@@ -13954,8 +13991,6 @@ static boolean DropElement(struct PlayerInfo *player)
   int drop_side = drop_direction;
   int drop_element = get_next_dropped_element(player);
 
-  player->is_dropping_pressed = TRUE;
-
   /* do not drop an element on top of another element; when holding drop key
      pressed without moving, dropped element must move away before the next
      element can be dropped (this is especially important if the next element
@@ -13983,6 +14018,9 @@ static boolean DropElement(struct PlayerInfo *player)
   if (new_element == EL_UNDEFINED)
     return FALSE;
 
+  /* only set if player has anything that can be dropped */
+  player->is_dropping_pressed = TRUE;
+
   /* check if drop key was pressed long enough for EM style dynamite */
   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
     return FALSE;
@@ -14761,11 +14799,10 @@ void SaveEngineSnapshotSingle()
   snapshot_level_nr = level_nr;
 }
 
-static boolean SaveEngineSnapshotToListExt(boolean initial_snapshot)
+boolean CheckSaveEngineSnapshotToList()
 {
   boolean save_snapshot =
-    (initial_snapshot ||
-     (game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
+    ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
       game.snapshot.changed_action) ||
      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
@@ -14773,30 +14810,28 @@ static boolean SaveEngineSnapshotToListExt(boolean initial_snapshot)
 
   game.snapshot.changed_action = FALSE;
   game.snapshot.collected_item = FALSE;
+  game.snapshot.save_snapshot = save_snapshot;
+
+  return save_snapshot;
+}
 
+void SaveEngineSnapshotToList()
+{
   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
-      tape.quick_resume ||
-      !save_snapshot)
-    return FALSE;
+      tape.quick_resume)
+    return;
 
   ListNode *buffers = SaveEngineSnapshotBuffers();
 
   /* finally save all snapshot buffers to snapshot list */
   SaveSnapshotToList(buffers);
-
-  return TRUE;
-}
-
-boolean SaveEngineSnapshotToList()
-{
-  return SaveEngineSnapshotToListExt(FALSE);
 }
 
 void SaveEngineSnapshotToListInitial()
 {
   FreeEngineSnapshotList();
 
-  SaveEngineSnapshotToListExt(TRUE);
+  SaveEngineSnapshotToList();
 }
 
 void LoadEngineSnapshotValues()
@@ -14990,7 +15025,7 @@ void FreeGameButtons()
     FreeGadget(game_gadget[i]);
 }
 
-static void MapGameButtonsAtSamePosition(int id)
+static void UnmapGameButtonsAtSamePosition(int id)
 {
   int i;
 
@@ -14998,10 +15033,26 @@ static void MapGameButtonsAtSamePosition(int id)
     if (i != id &&
        gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
        gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
-      MapGadget(game_gadget[i]);
+      UnmapGadget(game_gadget[i]);
 }
 
-static void UnmapGameButtonsAtSamePosition(int id)
+static void UnmapGameButtonsAtSamePosition_All()
+{
+  if (setup.show_snapshot_buttons)
+  {
+    UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
+    UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
+    UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
+  }
+  else
+  {
+    UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
+    UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
+    UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
+  }
+}
+
+static void MapGameButtonsAtSamePosition(int id)
 {
   int i;
 
@@ -15009,7 +15060,9 @@ static void UnmapGameButtonsAtSamePosition(int id)
     if (i != id &&
        gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
        gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
-      UnmapGadget(game_gadget[i]);
+      MapGadget(game_gadget[i]);
+
+  UnmapGameButtonsAtSamePosition_All();
 }
 
 void MapUndoRedoButtons()
@@ -15019,6 +15072,8 @@ void MapUndoRedoButtons()
 
   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
+
+  ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
 }
 
 void UnmapUndoRedoButtons()
@@ -15028,6 +15083,8 @@ void UnmapUndoRedoButtons()
 
   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
+
+  ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
 }
 
 void MapGameButtons()
@@ -15039,18 +15096,7 @@ void MapGameButtons()
        i != GAME_CTRL_ID_REDO)
       MapGadget(game_gadget[i]);
 
-  if (setup.show_snapshot_buttons)
-  {
-    UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
-    UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
-    UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
-  }
-  else
-  {
-    UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
-    UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
-    UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
-  }
+  UnmapGameButtonsAtSamePosition_All();
 
   RedrawGameButtons();
 }
@@ -15086,8 +15132,7 @@ void GameUndoRedoExt()
   DrawCompleteVideoDisplay();
   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
-  DrawVideoDisplay((tape.single_step ? VIDEO_STATE_1STEP_ON :
-                   VIDEO_STATE_1STEP_OFF), 0);
+  DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
 
   BackToFront();
 }
@@ -15166,7 +15211,7 @@ static void HandleGameButtonsExt(int id, int button)
          SendToServer_ContinuePlaying();
        else
 #endif
-         TapeTogglePause(TAPE_TOGGLE_MANUAL);
+         TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
       }
       break;