updated contact info in source file headers
[rocksndiamonds.git] / src / game.c
index daf96fe52f7da0c1e22d0cfc4b6fa5539d17bd27..28905e562b1ef07e942ca56e23be27b128cfade2 100644 (file)
@@ -1,15 +1,13 @@
-/***********************************************************
-* Rocks'n'Diamonds -- McDuffin Strikes Back!               *
-*----------------------------------------------------------*
-* (c) 1995-2006 Artsoft Entertainment                      *
-*               Holger Schemel                             *
-*               Detmolder Strasse 189                      *
-*               33604 Bielefeld                            *
-*               Germany                                    *
-*               e-mail: info@artsoft.org                   *
-*----------------------------------------------------------*
-* game.c                                                   *
-***********************************************************/
+// ============================================================================
+// Rocks'n'Diamonds - McDuffin Strikes Back!
+// ----------------------------------------------------------------------------
+// (c) 1995-2014 by Artsoft Entertainment
+//                         Holger Schemel
+//                 info@artsoft.org
+//                 http://www.artsoft.org/
+// ----------------------------------------------------------------------------
+// game.c
+// ============================================================================
 
 #include "libgame/libgame.h"
 
 #include "tape.h"
 #include "network.h"
 
+
+/* DEBUG SETTINGS */
+#define DEBUG_INIT_PLAYER      1
+#define DEBUG_PLAYER_ACTIONS   0
+
 /* EXPERIMENTAL STUFF */
 #define USE_NEW_AMOEBA_CODE    FALSE
 
 #if USE_DELAYED_GFX_REDRAW
 #define TEST_DrawLevelField(x, y)                              \
        GfxRedraw[x][y] |= GFX_REDRAW_TILE
-#define TEST_DrawLevelFieldCrumbledSand(x, y)                  \
+#define TEST_DrawLevelFieldCrumbled(x, y)                      \
        GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
-#define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)                \
+#define TEST_DrawLevelFieldCrumbledNeighbours(x, y)            \
        GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
 #define TEST_DrawTwinkleOnField(x, y)                          \
        GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
 #else
 #define TEST_DrawLevelField(x, y)                              \
             DrawLevelField(x, y)
-#define TEST_DrawLevelFieldCrumbledSand(x, y)                  \
-            DrawLevelFieldCrumbledSand(x, y)
-#define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)                \
-            DrawLevelFieldCrumbledSandNeighbours(x, y)
+#define TEST_DrawLevelFieldCrumbled(x, y)                      \
+            DrawLevelFieldCrumbled(x, y)
+#define TEST_DrawLevelFieldCrumbledNeighbours(x, y)            \
+            DrawLevelFieldCrumbledNeighbours(x, y)
 #define TEST_DrawTwinkleOnField(x, y)                          \
             DrawTwinkleOnField(x, y)
 #endif
 #define PANEL_XPOS(p)          (DX + ALIGNED_TEXT_XPOS(p))
 #define PANEL_YPOS(p)          (DY + ALIGNED_TEXT_YPOS(p))
 
-/* special positions in the game control window (relative to control window) */
-#define XX_LEVEL1              (PANEL_XPOS(game.panel.level))
-#define XX_LEVEL2              (PANEL_XPOS(game.panel.level) - 1)
-#define XX_LEVEL               (PANEL_XPOS(game.panel.level))
-#define YY_LEVEL               (PANEL_YPOS(game.panel.level))
-#define XX_EMERALDS            (PANEL_XPOS(game.panel.gems))
-#define YY_EMERALDS            (PANEL_YPOS(game.panel.gems))
-#define XX_DYNAMITE            (PANEL_XPOS(game.panel.inventory))
-#define YY_DYNAMITE            (PANEL_YPOS(game.panel.inventory))
-#define XX_KEYS                        (PANEL_XPOS(game.panel.keys))
-#define YY_KEYS                        (PANEL_YPOS(game.panel.keys))
-#define XX_SCORE               (PANEL_XPOS(game.panel.score))
-#define YY_SCORE               (PANEL_YPOS(game.panel.score))
-#define XX_TIME1               (PANEL_XPOS(game.panel.time))
-#define XX_TIME2               (PANEL_XPOS(game.panel.time) + 1)
-#define XX_TIME                        (PANEL_XPOS(game.panel.time))
-#define YY_TIME                        (PANEL_YPOS(game.panel.time))
-
-/* special positions in the game control window (relative to main window) */
-#define DX_LEVEL1              (DX + XX_LEVEL1)
-#define DX_LEVEL2              (DX + XX_LEVEL2)
-#define DX_LEVEL               (DX + XX_LEVEL)
-#define DY_LEVEL               (DY + YY_LEVEL)
-#define DX_EMERALDS            (DX + XX_EMERALDS)
-#define DY_EMERALDS            (DY + YY_EMERALDS)
-#define DX_DYNAMITE            (DX + XX_DYNAMITE)
-#define DY_DYNAMITE            (DY + YY_DYNAMITE)
-#define DX_KEYS                        (DX + XX_KEYS)
-#define DY_KEYS                        (DY + YY_KEYS)
-#define DX_SCORE               (DX + XX_SCORE)
-#define DY_SCORE               (DY + YY_SCORE)
-#define DX_TIME1               (DX + XX_TIME1)
-#define DX_TIME2               (DX + XX_TIME2)
-#define DX_TIME                        (DX + XX_TIME)
-#define DY_TIME                        (DY + YY_TIME)
-
-#if 1
 /* game panel display and control definitions */
-
 #define GAME_PANEL_LEVEL_NUMBER                        0
 #define GAME_PANEL_GEMS                                1
 #define GAME_PANEL_INVENTORY_COUNT             2
 #define GAME_PANEL_TIME_HH                     32
 #define GAME_PANEL_TIME_MM                     33
 #define GAME_PANEL_TIME_SS                     34
-#define GAME_PANEL_SHIELD_NORMAL               35
-#define GAME_PANEL_SHIELD_NORMAL_TIME          36
-#define GAME_PANEL_SHIELD_DEADLY               37
-#define GAME_PANEL_SHIELD_DEADLY_TIME          38
-#define GAME_PANEL_EXIT                                39
-#define GAME_PANEL_EMC_MAGIC_BALL              40
-#define GAME_PANEL_EMC_MAGIC_BALL_SWITCH       41
-#define GAME_PANEL_LIGHT_SWITCH                        42
-#define GAME_PANEL_LIGHT_SWITCH_TIME           43
-#define GAME_PANEL_TIMEGATE_SWITCH             44
-#define GAME_PANEL_TIMEGATE_SWITCH_TIME                45
-#define GAME_PANEL_SWITCHGATE_SWITCH           46
-#define GAME_PANEL_EMC_LENSES                  47
-#define GAME_PANEL_EMC_LENSES_TIME             48
-#define GAME_PANEL_EMC_MAGNIFIER               49
-#define GAME_PANEL_EMC_MAGNIFIER_TIME          50
-#define GAME_PANEL_BALLOON_SWITCH              51
-#define GAME_PANEL_DYNABOMB_NUMBER             52
-#define GAME_PANEL_DYNABOMB_SIZE               53
-#define GAME_PANEL_DYNABOMB_POWER              54
-#define GAME_PANEL_PENGUINS                    55
-#define GAME_PANEL_SOKOBAN_OBJECTS             56
-#define GAME_PANEL_SOKOBAN_FIELDS              57
-#define GAME_PANEL_ROBOT_WHEEL                 58
-#define GAME_PANEL_CONVEYOR_BELT_1             59
-#define GAME_PANEL_CONVEYOR_BELT_2             60
-#define GAME_PANEL_CONVEYOR_BELT_3             61
-#define GAME_PANEL_CONVEYOR_BELT_4             62
-#define GAME_PANEL_CONVEYOR_BELT_1_SWITCH      63
-#define GAME_PANEL_CONVEYOR_BELT_2_SWITCH      64
-#define GAME_PANEL_CONVEYOR_BELT_3_SWITCH      65
-#define GAME_PANEL_CONVEYOR_BELT_4_SWITCH      66
-#define GAME_PANEL_MAGIC_WALL                  67
-#define GAME_PANEL_MAGIC_WALL_TIME             68
-#define GAME_PANEL_GRAVITY_STATE               69
-#define GAME_PANEL_GRAPHIC_1                   70
-#define GAME_PANEL_GRAPHIC_2                   71
-#define GAME_PANEL_GRAPHIC_3                   72
-#define GAME_PANEL_GRAPHIC_4                   73
-#define GAME_PANEL_GRAPHIC_5                   74
-#define GAME_PANEL_GRAPHIC_6                   75
-#define GAME_PANEL_GRAPHIC_7                   76
-#define GAME_PANEL_GRAPHIC_8                   77
-#define GAME_PANEL_ELEMENT_1                   78
-#define GAME_PANEL_ELEMENT_2                   79
-#define GAME_PANEL_ELEMENT_3                   80
-#define GAME_PANEL_ELEMENT_4                   81
-#define GAME_PANEL_ELEMENT_5                   82
-#define GAME_PANEL_ELEMENT_6                   83
-#define GAME_PANEL_ELEMENT_7                   84
-#define GAME_PANEL_ELEMENT_8                   85
-#define GAME_PANEL_ELEMENT_COUNT_1             86
-#define GAME_PANEL_ELEMENT_COUNT_2             87
-#define GAME_PANEL_ELEMENT_COUNT_3             88
-#define GAME_PANEL_ELEMENT_COUNT_4             89
-#define GAME_PANEL_ELEMENT_COUNT_5             90
-#define GAME_PANEL_ELEMENT_COUNT_6             91
-#define GAME_PANEL_ELEMENT_COUNT_7             92
-#define GAME_PANEL_ELEMENT_COUNT_8             93
-#define GAME_PANEL_CE_SCORE_1                  94
-#define GAME_PANEL_CE_SCORE_2                  95
-#define GAME_PANEL_CE_SCORE_3                  96
-#define GAME_PANEL_CE_SCORE_4                  97
-#define GAME_PANEL_CE_SCORE_5                  98
-#define GAME_PANEL_CE_SCORE_6                  99
-#define GAME_PANEL_CE_SCORE_7                  100
-#define GAME_PANEL_CE_SCORE_8                  101
-#define GAME_PANEL_CE_SCORE_1_ELEMENT          102
-#define GAME_PANEL_CE_SCORE_2_ELEMENT          103
-#define GAME_PANEL_CE_SCORE_3_ELEMENT          104
-#define GAME_PANEL_CE_SCORE_4_ELEMENT          105
-#define GAME_PANEL_CE_SCORE_5_ELEMENT          106
-#define GAME_PANEL_CE_SCORE_6_ELEMENT          107
-#define GAME_PANEL_CE_SCORE_7_ELEMENT          108
-#define GAME_PANEL_CE_SCORE_8_ELEMENT          109
-#define GAME_PANEL_PLAYER_NAME                 110
-#define GAME_PANEL_LEVEL_NAME                  111
-#define GAME_PANEL_LEVEL_AUTHOR                        112
-
-#define NUM_GAME_PANEL_CONTROLS                        113
+#define GAME_PANEL_FRAME                       35
+#define GAME_PANEL_SHIELD_NORMAL               36
+#define GAME_PANEL_SHIELD_NORMAL_TIME          37
+#define GAME_PANEL_SHIELD_DEADLY               38
+#define GAME_PANEL_SHIELD_DEADLY_TIME          39
+#define GAME_PANEL_EXIT                                40
+#define GAME_PANEL_EMC_MAGIC_BALL              41
+#define GAME_PANEL_EMC_MAGIC_BALL_SWITCH       42
+#define GAME_PANEL_LIGHT_SWITCH                        43
+#define GAME_PANEL_LIGHT_SWITCH_TIME           44
+#define GAME_PANEL_TIMEGATE_SWITCH             45
+#define GAME_PANEL_TIMEGATE_SWITCH_TIME                46
+#define GAME_PANEL_SWITCHGATE_SWITCH           47
+#define GAME_PANEL_EMC_LENSES                  48
+#define GAME_PANEL_EMC_LENSES_TIME             49
+#define GAME_PANEL_EMC_MAGNIFIER               50
+#define GAME_PANEL_EMC_MAGNIFIER_TIME          51
+#define GAME_PANEL_BALLOON_SWITCH              52
+#define GAME_PANEL_DYNABOMB_NUMBER             53
+#define GAME_PANEL_DYNABOMB_SIZE               54
+#define GAME_PANEL_DYNABOMB_POWER              55
+#define GAME_PANEL_PENGUINS                    56
+#define GAME_PANEL_SOKOBAN_OBJECTS             57
+#define GAME_PANEL_SOKOBAN_FIELDS              58
+#define GAME_PANEL_ROBOT_WHEEL                 59
+#define GAME_PANEL_CONVEYOR_BELT_1             60
+#define GAME_PANEL_CONVEYOR_BELT_2             61
+#define GAME_PANEL_CONVEYOR_BELT_3             62
+#define GAME_PANEL_CONVEYOR_BELT_4             63
+#define GAME_PANEL_CONVEYOR_BELT_1_SWITCH      64
+#define GAME_PANEL_CONVEYOR_BELT_2_SWITCH      65
+#define GAME_PANEL_CONVEYOR_BELT_3_SWITCH      66
+#define GAME_PANEL_CONVEYOR_BELT_4_SWITCH      67
+#define GAME_PANEL_MAGIC_WALL                  68
+#define GAME_PANEL_MAGIC_WALL_TIME             69
+#define GAME_PANEL_GRAVITY_STATE               70
+#define GAME_PANEL_GRAPHIC_1                   71
+#define GAME_PANEL_GRAPHIC_2                   72
+#define GAME_PANEL_GRAPHIC_3                   73
+#define GAME_PANEL_GRAPHIC_4                   74
+#define GAME_PANEL_GRAPHIC_5                   75
+#define GAME_PANEL_GRAPHIC_6                   76
+#define GAME_PANEL_GRAPHIC_7                   77
+#define GAME_PANEL_GRAPHIC_8                   78
+#define GAME_PANEL_ELEMENT_1                   79
+#define GAME_PANEL_ELEMENT_2                   80
+#define GAME_PANEL_ELEMENT_3                   81
+#define GAME_PANEL_ELEMENT_4                   82
+#define GAME_PANEL_ELEMENT_5                   83
+#define GAME_PANEL_ELEMENT_6                   84
+#define GAME_PANEL_ELEMENT_7                   85
+#define GAME_PANEL_ELEMENT_8                   86
+#define GAME_PANEL_ELEMENT_COUNT_1             87
+#define GAME_PANEL_ELEMENT_COUNT_2             88
+#define GAME_PANEL_ELEMENT_COUNT_3             89
+#define GAME_PANEL_ELEMENT_COUNT_4             90
+#define GAME_PANEL_ELEMENT_COUNT_5             91
+#define GAME_PANEL_ELEMENT_COUNT_6             92
+#define GAME_PANEL_ELEMENT_COUNT_7             93
+#define GAME_PANEL_ELEMENT_COUNT_8             94
+#define GAME_PANEL_CE_SCORE_1                  95
+#define GAME_PANEL_CE_SCORE_2                  96
+#define GAME_PANEL_CE_SCORE_3                  97
+#define GAME_PANEL_CE_SCORE_4                  98
+#define GAME_PANEL_CE_SCORE_5                  99
+#define GAME_PANEL_CE_SCORE_6                  100
+#define GAME_PANEL_CE_SCORE_7                  101
+#define GAME_PANEL_CE_SCORE_8                  102
+#define GAME_PANEL_CE_SCORE_1_ELEMENT          103
+#define GAME_PANEL_CE_SCORE_2_ELEMENT          104
+#define GAME_PANEL_CE_SCORE_3_ELEMENT          105
+#define GAME_PANEL_CE_SCORE_4_ELEMENT          106
+#define GAME_PANEL_CE_SCORE_5_ELEMENT          107
+#define GAME_PANEL_CE_SCORE_6_ELEMENT          108
+#define GAME_PANEL_CE_SCORE_7_ELEMENT          109
+#define GAME_PANEL_CE_SCORE_8_ELEMENT          110
+#define GAME_PANEL_PLAYER_NAME                 111
+#define GAME_PANEL_LEVEL_NAME                  112
+#define GAME_PANEL_LEVEL_AUTHOR                        113
+
+#define NUM_GAME_PANEL_CONTROLS                        114
 
 struct GamePanelOrderInfo
 {
@@ -473,6 +439,11 @@ static struct GamePanelControlInfo game_panel_controls[] =
     &game.panel.time_ss,
     TYPE_INTEGER,
   },
+  {
+    GAME_PANEL_FRAME,
+    &game.panel.frame,
+    TYPE_INTEGER,
+  },
   {
     GAME_PANEL_SHIELD_NORMAL,
     &game.panel.shield_normal,
@@ -870,8 +841,6 @@ static struct GamePanelControlInfo game_panel_controls[] =
     -1,
   }
 };
-#endif
-
 
 /* values for delayed check of falling and moving elements and for collision */
 #define CHECK_DELAY_MOVING     3
@@ -1043,8 +1012,10 @@ static struct GamePanelControlInfo game_panel_controls[] =
 #define SOUND_CTRL_ID_MUSIC            3
 #define SOUND_CTRL_ID_LOOPS            4
 #define SOUND_CTRL_ID_SIMPLE           5
+#define GAME_CTRL_ID_SAVE              6
+#define GAME_CTRL_ID_LOAD              7
 
-#define NUM_GAME_BUTTONS               6
+#define NUM_GAME_BUTTONS               8
 
 
 /* forward declaration for internal use */
@@ -1111,7 +1082,6 @@ static void PlayLevelSoundActionIfLoop(int, int, int);
 static void StopLevelSoundActionIfLoop(int, int, int);
 static void PlayLevelMusic();
 
-static void MapGameButtons();
 static void HandleGameButtons(struct GadgetInfo *);
 
 int AmoebeNachbarNr(int, int);
@@ -1774,14 +1744,16 @@ static void InitPlayerField(int x, int y, int element, boolean init_game)
 
       StorePlayer[x][y] = Feld[x][y];
 
+#if DEBUG_INIT_PLAYER
       if (options.debug)
       {
-       printf("Player %d activated.\n", player->element_nr);
-       printf("[Local player is %d and currently %s.]\n",
+       printf("- player element %d activated", player->element_nr);
+       printf(" (local player is %d and currently %s)\n",
               local_player->element_nr,
               local_player->active ? "active" : "not active");
       }
     }
+#endif
 
     Feld[x][y] = EL_EMPTY;
 
@@ -2070,8 +2042,6 @@ static inline void InitField_WithBug2(int x, int y, boolean init_game)
   */
 }
 
-#if 1
-
 static int get_key_element_from_nr(int key_nr)
 {
   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
@@ -2221,17 +2191,25 @@ void UpdateGameControlValues()
              local_player->LevelSolved_CountingTime :
              level.game_engine_type == GAME_ENGINE_TYPE_EM ?
              level.native_em_level->lev->time :
-             level.time == 0 ? TimePlayed : TimeLeft);
+             level.game_engine_type == GAME_ENGINE_TYPE_SP ?
+             level.native_sp_level->game_sp->time_played :
+             game.no_time_limit ? TimePlayed : TimeLeft);
   int score = (local_player->LevelSolved ?
               local_player->LevelSolved_CountingScore :
               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
               level.native_em_level->lev->score :
+              level.game_engine_type == GAME_ENGINE_TYPE_SP ?
+              level.native_sp_level->game_sp->score :
               local_player->score);
   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
              level.native_em_level->lev->required :
+             level.game_engine_type == GAME_ENGINE_TYPE_SP ?
+             level.native_sp_level->game_sp->infotrons_still_needed :
              local_player->gems_still_needed);
   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
                     level.native_em_level->lev->required > 0 :
+                    level.game_engine_type == GAME_ENGINE_TYPE_SP ?
+                    level.native_sp_level->game_sp->infotrons_still_needed > 0 :
                     local_player->gems_still_needed > 0 ||
                     local_player->sokobanfields_still_needed > 0 ||
                     local_player->lights_still_needed > 0);
@@ -2253,6 +2231,10 @@ void UpdateGameControlValues()
   {
     for (i = 0; i < MAX_PLAYERS; i++)
     {
+      /* only one player in Supaplex game engine */
+      if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
+       break;
+
       for (k = 0; k < MAX_NUM_KEYS; k++)
       {
        if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
@@ -2269,6 +2251,9 @@ void UpdateGameControlValues()
       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;
@@ -2301,6 +2286,9 @@ void UpdateGameControlValues()
     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;
@@ -2329,6 +2317,8 @@ void UpdateGameControlValues()
   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
 
+  game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
+
   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
      EL_EMPTY);
@@ -2513,8 +2503,15 @@ void DisplayGameControlValues()
     return;
 
   /* copy default game door content to main double buffer */
+#if 1
+  /* !!! CHECK AGAIN !!! */
+  SetPanelBackground();
+  // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
+  DrawBackground(DX, DY, DXSIZE, DYSIZE);
+#else
   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
             DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
+#endif
 
   /* redraw game control buttons */
 #if 1
@@ -2755,11 +2752,7 @@ void UpdateAndDisplayGameControlValues()
 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 chars = pos->size;
 
@@ -2778,11 +2771,7 @@ void DrawGameValue_Emeralds(int value)
 void DrawGameValue_Dynamite(int value)
 {
   struct TextPosInfo *pos = &game.panel.inventory_count;
-#if 1
   int font_nr = pos->font;
-#else
-  int font_nr = FONT_TEXT_2;
-#endif
   int font_width = getFontWidth(font_nr);
   int chars = pos->size;
 
@@ -2801,11 +2790,7 @@ void DrawGameValue_Dynamite(int value)
 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 chars = pos->size;
 
@@ -2828,13 +2813,8 @@ void DrawGameValue_Time(int value)
   int chars1 = 3;
   int chars2 = 4;
   int chars = pos->size;
-#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_chars = (chars == -1 ? TRUE : FALSE);
 
@@ -2878,13 +2858,8 @@ void DrawGameValue_Level(int value)
   int chars1 = 2;
   int chars2 = 3;
   int chars = pos->size;
-#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_chars = (chars == -1 ? TRUE : FALSE);
 
@@ -2908,63 +2883,27 @@ void DrawGameValue_Level(int value)
 
 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
 {
-#if 0
-  struct TextPosInfo *pos = &game.panel.keys;
-#endif
-#if 0
-  int base_key_graphic = EL_KEY_1;
-#endif
   int i;
 
 #if 1
   return;      /* !!! USE NEW STUFF !!! */
 #endif
 
-#if 0
-  if (PANEL_DEACTIVATED(pos))
-    return;
-#endif
-
-#if 0
-  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
-    base_key_graphic = EL_EM_KEY_1;
-#endif
-
-#if 0
-  pos->width = 4 * MINI_TILEX;
-#endif
-
-#if 1
   for (i = 0; i < MAX_NUM_KEYS; i++)
-#else
-  /* currently only 4 of 8 possible keys are displayed */
-  for (i = 0; i < STD_NUM_KEYS; i++)
-#endif
   {
-#if 1
     struct TextPosInfo *pos = &game.panel.key[i];
-#endif
     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
     int src_y = DOOR_GFX_PAGEY1 + 123;
-#if 1
     int dst_x = PANEL_XPOS(pos);
     int dst_y = PANEL_YPOS(pos);
-#else
-    int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
-    int dst_y = PANEL_YPOS(pos);
-#endif
 
-#if 1
     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
                   level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
                   EL_KEY_1) + i;
     int graphic = el2edimg(element);
-#endif
 
-#if 1
     if (PANEL_DEACTIVATED(pos))
       continue;
-#endif
 
 #if 0
     /* masked blit with tiles from half-size scaled bitmap does not work yet
@@ -2976,9 +2915,6 @@ void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
 
     if (key[i])
     {
-#if 0
-      int graphic = el2edimg(base_key_graphic + i);
-#endif
       Bitmap *src_bitmap;
       int src_x, src_y;
 
@@ -2990,127 +2926,15 @@ void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
                       dst_x, dst_y);
     }
 #else
-#if 1
     if (key[i])
       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
     else
       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
                 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
-#else
-    if (key[i])
-      DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
-    else
-      BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
-                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
-#endif
 #endif
   }
 }
 
-#else
-
-void DrawGameValue_Emeralds(int value)
-{
-  int font_nr = FONT_TEXT_2;
-  int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
-
-  if (PANEL_DEACTIVATED(game.panel.gems))
-    return;
-
-  DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
-}
-
-void DrawGameValue_Dynamite(int value)
-{
-  int font_nr = FONT_TEXT_2;
-  int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
-
-  if (PANEL_DEACTIVATED(game.panel.inventory_count))
-    return;
-
-  DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
-}
-
-void DrawGameValue_Score(int value)
-{
-  int font_nr = FONT_TEXT_2;
-  int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
-
-  if (PANEL_DEACTIVATED(game.panel.score))
-    return;
-
-  DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
-}
-
-void DrawGameValue_Time(int value)
-{
-  int font1_nr = FONT_TEXT_2;
-#if 1
-  int font2_nr = FONT_TEXT_1;
-#else
-  int font2_nr = FONT_LEVEL_NUMBER;
-#endif
-  int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
-  int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
-
-  if (PANEL_DEACTIVATED(game.panel.time))
-    return;
-
-  /* clear background if value just changed its size */
-  if (value == 999 || value == 1000)
-    ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
-
-  if (value < 1000)
-    DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
-  else
-    DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
-}
-
-void DrawGameValue_Level(int value)
-{
-  int font1_nr = FONT_TEXT_2;
-#if 1
-  int font2_nr = FONT_TEXT_1;
-#else
-  int font2_nr = FONT_LEVEL_NUMBER;
-#endif
-
-  if (PANEL_DEACTIVATED(game.panel.level))
-    return;
-
-  if (level_nr < 100)
-    DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
-  else
-    DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
-}
-
-void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
-{
-  int base_key_graphic = EL_KEY_1;
-  int i;
-
-  if (PANEL_DEACTIVATED(game.panel.keys))
-    return;
-
-  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
-    base_key_graphic = EL_EM_KEY_1;
-
-  /* currently only 4 of 8 possible keys are displayed */
-  for (i = 0; i < STD_NUM_KEYS; i++)
-  {
-    int x = XX_KEYS + i * MINI_TILEX;
-    int y = YY_KEYS;
-
-    if (key[i])
-      DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
-    else
-      BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
-                DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
-  }
-}
-
-#endif
-
 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
                       int key_bits)
 {
@@ -3147,7 +2971,7 @@ void DrawGameDoorValues()
 
 void DrawGameDoorValues_OLD()
 {
-  int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
+  int time_value = (game.no_time_limit ? TimePlayed : TimeLeft);
   int dynamite_value = 0;
   int score_value = (local_player->LevelSolved ? local_player->score_final :
                     local_player->score);
@@ -3205,6 +3029,21 @@ static void InitGameEngine()
   game.engine_version = (tape.playing ? tape.engine_version :
                         level.game_version);
 
+  /* set single or multi-player game mode (needed for re-playing tapes) */
+  game.team_mode = setup.team_mode;
+
+  if (tape.playing)
+  {
+    int num_players = 0;
+
+    for (i = 0; i < MAX_PLAYERS; i++)
+      if (tape.player_participates[i])
+       num_players++;
+
+    /* multi-player tapes contain input data for more than one player */
+    game.team_mode = (num_players > 1);
+  }
+
   /* ---------------------------------------------------------------------- */
   /* set flags for bugs and changes according to active game engine version */
   /* ---------------------------------------------------------------------- */
@@ -3679,6 +3518,9 @@ int get_num_special_action(int element, int action_first, int action_last)
 
 void InitGame()
 {
+  int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
+  int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
+
   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 */
@@ -3692,7 +3534,55 @@ void InitGame()
 #endif
   int i, j, x, y;
 
+#if 1
+  game_status = GAME_MODE_PLAYING;
+#endif
+
+#if 1
+
+  StopAnimation();
+
+  if (!game.restart_level)
+    CloseDoor(DOOR_CLOSE_1);
+
+#if 1
+  if (level_editor_test_game)
+    FadeSkipNextFadeIn();
+  else
+    FadeSetEnterScreen();
+#else
+  if (level_editor_test_game)
+    fading = fading_none;
+  else
+    fading = menu.destination;
+#endif
+
+#if 1
+  FadeOut(REDRAW_FIELD);
+#else
+  if (do_fading)
+    FadeOut(REDRAW_FIELD);
+#endif
+
+#endif
+
+#if 0
+  printf("::: FADING OUT: DONE\n");
+  Delay(1000);
+#endif
+
+#if 0
   game_status = GAME_MODE_PLAYING;
+#endif
+
+#if 1
+  /* needed if different viewport properties defined for playing */
+  ChangeViewportPropertiesIfNeeded();
+#endif
+
+#if 1
+  DrawCompleteVideoDisplay();
+#endif
 
   InitGameEngine();
   InitGameControlValues();
@@ -3881,6 +3771,8 @@ void InitGame()
 
   AllPlayersGone = FALSE;
 
+  game.no_time_limit = (level.time == 0);
+
   game.yamyam_content_nr = 0;
   game.robot_wheel_active = FALSE;
   game.magic_wall_active = FALSE;
@@ -3924,6 +3816,13 @@ void InitGame()
   for (i = 0; i < MAX_NUM_AMOEBA; i++)
     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
 
+#if DEBUG_INIT_PLAYER
+  if (options.debug)
+  {
+    printf("Player status at level initialization:\n");
+  }
+#endif
+
   SCAN_PLAYFIELD(x, y)
   {
     Feld[x][y] = level.field[x][y];
@@ -4057,16 +3956,25 @@ void InitGame()
   local_player->connected = TRUE;
   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
 
+#if 0
+  printf("::: TEAM MODE: %d\n", game.team_mode);
+#endif
+
   if (tape.playing)
   {
+#if 1
+    for (i = 0; i < MAX_PLAYERS; i++)
+      stored_player[i].connected = tape.player_participates[i];
+#else
     /* try to guess locally connected team mode players (needed for correct
        assignment of player figures from level to locally playing players) */
 
     for (i = 0; i < MAX_PLAYERS; i++)
       if (tape.player_participates[i])
        stored_player[i].connected = TRUE;
+#endif
   }
-  else if (setup.team_mode && !options.network)
+  else if (game.team_mode && !options.network)
   {
     /* try to guess locally connected team mode players (needed for correct
        assignment of player figures from level to locally playing players) */
@@ -4077,14 +3985,32 @@ void InitGame()
        stored_player[i].connected = TRUE;
   }
 
-#if 0
-  for (i = 0; i < MAX_PLAYERS; i++)
-    printf("::: player %d: %s\n", i,
-          (stored_player[i].connected ? "connected" : "not connected"));
+#if DEBUG_INIT_PLAYER
+  if (options.debug)
+  {
+    printf("Player status after level initialization:\n");
 
-  for (i = 0; i < MAX_PLAYERS; i++)
-    printf("::: player %d: %s\n", i,
-          (stored_player[i].present ? "present" : "not present"));
+    for (i = 0; i < MAX_PLAYERS; i++)
+    {
+      struct PlayerInfo *player = &stored_player[i];
+
+      printf("- player %d: present == %d, connected == %d, active == %d",
+            i + 1,
+            player->present,
+            player->connected,
+            player->active);
+
+      if (local_player == player)
+       printf(" (local player)");
+
+      printf("\n");
+    }
+  }
+#endif
+
+#if DEBUG_INIT_PLAYER
+  if (options.debug)
+    printf("Reassigning players ...\n");
 #endif
 
   /* check if any connected player was not found in playfield */
@@ -4096,12 +4022,29 @@ void InitGame()
     {
       struct PlayerInfo *field_player = NULL;
 
-#if 0
-      printf("::: looking for field player for player %d ...\n", i);
+#if DEBUG_INIT_PLAYER
+      if (options.debug)
+       printf("- looking for field player for player %d ...\n", i + 1);
 #endif
 
       /* assign first free player found that is present in the playfield */
 
+#if 1
+      /* first try: look for unmapped playfield player that is not connected */
+      for (j = 0; j < MAX_PLAYERS; j++)
+       if (field_player == NULL &&
+           stored_player[j].present &&
+           !stored_player[j].mapped &&
+           !stored_player[j].connected)
+         field_player = &stored_player[j];
+
+      /* second try: look for *any* unmapped playfield player */
+      for (j = 0; j < MAX_PLAYERS; j++)
+       if (field_player == NULL &&
+           stored_player[j].present &&
+           !stored_player[j].mapped)
+         field_player = &stored_player[j];
+#else
       /* first try: look for unmapped playfield player that is not connected */
       if (field_player == NULL)
        for (j = 0; j < MAX_PLAYERS; j++)
@@ -4116,13 +4059,15 @@ void InitGame()
          if (stored_player[j].present &&
              !stored_player[j].mapped)
            field_player = &stored_player[j];
+#endif
 
       if (field_player != NULL)
       {
        int jx = field_player->jx, jy = field_player->jy;
 
-#if 0
-       printf("::: found player figure %d\n", field_player->index_nr);
+#if DEBUG_INIT_PLAYER
+       if (options.debug)
+         printf("- found player %d\n", field_player->index_nr + 1);
 #endif
 
        player->present = FALSE;
@@ -4151,9 +4096,10 @@ void InitGame()
 
        field_player->mapped = TRUE;
 
-#if 0
-       printf("::: map_player_action[%d] == %d\n",
-              field_player->index_nr, i);
+#if DEBUG_INIT_PLAYER
+       if (options.debug)
+         printf("- map_player_action[%d] == %d\n",
+                field_player->index_nr + 1, i + 1);
 #endif
       }
     }
@@ -4162,6 +4108,29 @@ void InitGame()
       player->mapped = TRUE;
   }
 
+#if DEBUG_INIT_PLAYER
+  if (options.debug)
+  {
+    printf("Player status after player assignment (first stage):\n");
+
+    for (i = 0; i < MAX_PLAYERS; i++)
+    {
+      struct PlayerInfo *player = &stored_player[i];
+
+      printf("- player %d: present == %d, connected == %d, active == %d",
+            i + 1,
+            player->present,
+            player->connected,
+            player->active);
+
+      if (local_player == player)
+       printf(" (local player)");
+
+      printf("\n");
+    }
+  }
+#endif
+
 #else
 
   /* check if any connected player was not found in playfield */
@@ -4212,6 +4181,11 @@ void InitGame()
     /* when playing a tape, eliminate all players who do not participate */
 
 #if USE_NEW_PLAYER_ASSIGNMENTS
+
+#if 1
+    if (!game.team_mode)
+#endif
+
     for (i = 0; i < MAX_PLAYERS; i++)
     {
       if (stored_player[i].active &&
@@ -4220,12 +4194,19 @@ void InitGame()
        struct PlayerInfo *player = &stored_player[i];
        int jx = player->jx, jy = player->jy;
 
+#if DEBUG_INIT_PLAYER
+       if (options.debug)
+         printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
+#endif
+
        player->active = FALSE;
        StorePlayer[jx][jy] = 0;
        Feld[jx][jy] = EL_EMPTY;
       }
     }
+
 #else
+
     for (i = 0; i < MAX_PLAYERS; i++)
     {
       if (stored_player[i].active &&
@@ -4241,7 +4222,7 @@ void InitGame()
     }
 #endif
   }
-  else if (!options.network && !setup.team_mode)       /* && !tape.playing */
+  else if (!options.network && !game.team_mode)                /* && !tape.playing */
   {
     /* when in single player mode, eliminate all but the first active player */
 
@@ -4281,21 +4262,28 @@ void InitGame()
 #endif
   }
 
+#if DEBUG_INIT_PLAYER
   if (options.debug)
   {
+    printf("Player status after player assignment (final stage):\n");
+
     for (i = 0; i < MAX_PLAYERS; i++)
     {
       struct PlayerInfo *player = &stored_player[i];
 
-      printf("Player %d: present == %d, connected == %d, active == %d.\n",
-            i+1,
+      printf("- player %d: present == %d, connected == %d, active == %d",
+            i + 1,
             player->present,
             player->connected,
             player->active);
+
       if (local_player == player)
-       printf("Player  %d is local player.\n", i+1);
+       printf(" (local player)");
+
+      printf("\n");
     }
   }
+#endif
 
   if (BorderElement == EL_EMPTY)
   {
@@ -4312,11 +4300,55 @@ void InitGame()
     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
   }
 
+#if NEW_TILESIZE
+
+  // printf("::: START-0: %d, %d\n", lev_fieldx, SCR_FIELDX);
+  // printf("::: START-1: %d, %d\n", SBX_Left, SBX_Right);
+
+#if 1
+  if (full_lev_fieldx <= SCR_FIELDX)
+    SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
+
+  if (full_lev_fieldy <= SCR_FIELDY)
+    SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
+#else
+  if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
+    SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
+
+  if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
+    SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
+#endif
+
+  /*
+  printf("::: START-2: %d, %d (%d)\n", SBX_Left, SBX_Right,
+        SBX_Right - SBX_Left + 1);
+  */
+
+#if 1
+  if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
+    SBX_Left--;
+  if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
+    SBY_Upper--;
+#else
+  if (EVEN(SCR_FIELDX))
+    SBX_Left--;
+  if (EVEN(SCR_FIELDY))
+    SBY_Upper--;
+#endif
+
+#if 0
+  printf("::: START-3: %d, %d\n", SBX_Left, SBX_Right);
+  printf("\n");
+#endif
+
+#else
+
   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
 
   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
+#endif
 
   /* if local player not found, look for custom element that might create
      the player (make some assumptions about the right custom element) */
@@ -4427,11 +4459,17 @@ void InitGame()
                local_player->jy - MIDPOSY);
   }
 
+#if 0
+  printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
+#endif
+
 #if 0
   /* do not use PLAYING mask for fading out from main screen */
   game_status = GAME_MODE_MAIN;
 #endif
 
+#if 0
+
   StopAnimation();
 
   if (!game.restart_level)
@@ -4456,6 +4494,8 @@ void InitGame()
     FadeOut(REDRAW_FIELD);
 #endif
 
+#endif
+
 #if 0
   game_status = GAME_MODE_PLAYING;
 #endif
@@ -4465,31 +4505,49 @@ void InitGame()
   {
     InitGameEngine_EM();
 
+#if 0
     /* blit playfield from scroll buffer to normal back buffer for fading in */
     BlitScreenToBitmap_EM(backbuffer);
+#endif
   }
   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
   {
     InitGameEngine_SP();
 
+#if 0
     /* blit playfield from scroll buffer to normal back buffer for fading in */
     BlitScreenToBitmap_SP(backbuffer);
+#endif
   }
   else
   {
-    DrawLevel();
+    DrawLevel(REDRAW_FIELD);
     DrawAllPlayers();
 
     /* after drawing the level, correct some elements */
     if (game.timegate_time_left == 0)
       CloseAllOpenTimegates();
 
+#if 0
     /* blit playfield from scroll buffer to normal back buffer for fading in */
+#if NEW_TILESIZE
+    BlitScreenToBitmap(backbuffer);
+#else
     if (setup.soft_scrolling)
       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
+#endif
+#endif
 
+#if 0
     redraw_mask |= REDRAW_FROM_BACKBUFFER;
+#endif
   }
+#if 1
+  /* blit playfield from scroll buffer to normal back buffer for fading in */
+  BlitScreenToBitmap(backbuffer);
+
+  redraw_mask |= REDRAW_FROM_BACKBUFFER;
+#endif
   /* !!! FIX THIS (END) !!! */
 
 #if 1
@@ -4504,8 +4562,24 @@ void InitGame()
   if (!game.restart_level)
   {
     /* copy default game door content to main double buffer */
+#if 1
+#if 1
+    /* !!! CHECK AGAIN !!! */
+    SetPanelBackground();
+    // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
+    DrawBackground(DX, DY, DXSIZE, DYSIZE);
+#else
+    struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
+
+    /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
+    ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
+    BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
+              MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
+#endif
+#else
     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
               DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
+#endif
   }
 
   SetPanelBackground();
@@ -4529,8 +4603,12 @@ void InitGame()
     MapTapeButtons();
 
     /* copy actual game door content to door double buffer for OpenDoor() */
+#if 1
+    BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
+#else
     BlitBitmap(drawto, bitmap_db_door,
               DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
+#endif
 
     OpenDoor(DOOR_OPEN_ALL);
 
@@ -4541,12 +4619,28 @@ void InitGame()
 
     KeyboardAutoRepeatOffUnlessAutoplay();
 
+#if DEBUG_INIT_PLAYER
     if (options.debug)
     {
+      printf("Player status (final):\n");
+
       for (i = 0; i < MAX_PLAYERS; i++)
-       printf("Player %d %sactive.\n",
-              i + 1, (stored_player[i].active ? "" : "not "));
+      {
+       struct PlayerInfo *player = &stored_player[i];
+
+       printf("- player %d: present == %d, connected == %d, active == %d",
+              i + 1,
+              player->present,
+              player->connected,
+              player->active);
+
+       if (local_player == player)
+         printf(" (local player)");
+
+       printf("\n");
+      }
     }
+#endif
   }
 
 #if 1
@@ -4556,6 +4650,17 @@ void InitGame()
   MapTapeButtons();
 #endif
 
+  if (!game.restart_level && !tape.playing)
+  {
+    LevelStats_incPlayed(level_nr);
+
+    SaveLevelSetup_SeriesInfo();
+
+#if 0
+    printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
+#endif
+  }
+
   game.restart_level = FALSE;
 }
 
@@ -4774,7 +4879,8 @@ static void PlayerWins(struct PlayerInfo *player)
   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
                         level.native_em_level->lev->score : player->score);
 
-  player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
+  player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
+                                     TimeLeft);
   player->LevelSolved_CountingScore = player->score_final;
 }
 
@@ -4799,6 +4905,17 @@ void GameWon()
     local_player->LevelSolved_SaveTape = tape.recording;
     local_player->LevelSolved_SaveScore = !tape.playing;
 
+    if (!tape.playing)
+    {
+      LevelStats_incSolved(level_nr);
+
+      SaveLevelSetup_SeriesInfo();
+
+#if 0
+      printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
+#endif
+    }
+
     if (tape.auto_play)                /* tape might already be stopped here */
       tape.auto_play_level_solved = TRUE;
 
@@ -4809,7 +4926,7 @@ void GameWon()
     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);
+    time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
     score = score_final = local_player->score_final;
 
     if (TimeLeft > 0)
@@ -4817,7 +4934,7 @@ void GameWon()
       time_final = 0;
       score_final += TimeLeft * level.score[SC_TIME_BONUS];
     }
-    else if (level.time == 0 && TimePlayed < 999)
+    else if (game.no_time_limit && TimePlayed < 999)
     {
       time_final = 999;
       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
@@ -5003,6 +5120,7 @@ void GameEnd()
   if (level_nr == leveldir_current->handicap_level)
   {
     leveldir_current->handicap_level++;
+
     SaveLevelSetup_SeriesInfo();
   }
 
@@ -5772,10 +5890,19 @@ void RelocatePlayer(int jx, int jy, int el_player_raw)
   Feld[jx][jy] = el_player;
   InitPlayerField(jx, jy, el_player, TRUE);
 
+  /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
+     possible that the relocation target field did not contain a player element,
+     but a walkable element, to which the new player was relocated -- in this
+     case, restore that (already initialized!) element on the player field */
   if (!ELEM_IS_PLAYER(element))        /* player may be set on walkable element */
   {
-    Feld[jx][jy] = element;
+    Feld[jx][jy] = element;    /* restore previously existing element */
+#if 0
+    /* !!! do not initialize already initialized element a second time !!! */
+    /* (this causes at least problems with "element creation" CE trigger for
+       already existing elements, and existing Sokoban fields counted twice) */
     InitField(jx, jy, FALSE);
+#endif
   }
 
   /* only visually relocate centered player */
@@ -6123,7 +6250,7 @@ void Explode(int ex, int ey, int phase, int mode)
     TestIfElementTouchesCustomElement(x, y);
 
     if (GFX_CRUMBLED(element))
-      TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
+      TEST_DrawLevelFieldCrumbledNeighbours(x, y);
 
     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
       StorePlayer[x][y] = 0;
@@ -6137,7 +6264,7 @@ void Explode(int ex, int ey, int phase, int mode)
     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
 
     if (phase == delay)
-      TEST_DrawLevelFieldCrumbledSand(x, y);
+      TEST_DrawLevelFieldCrumbled(x, y);
 
     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
     {
@@ -6595,7 +6722,7 @@ static void RedrawAllLightSwitchesAndInvisibleElements()
 
       /* uncrumble neighbour fields, if needed */
       if (element == EL_INVISIBLE_SAND)
-       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
+       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
     }
     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
             element == EL_INVISIBLE_WALL_ACTIVE ||
@@ -6608,7 +6735,7 @@ static void RedrawAllLightSwitchesAndInvisibleElements()
 
       /* re-crumble neighbour fields, if needed */
       if (element == EL_INVISIBLE_SAND)
-       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
+       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
     }
   }
 }
@@ -6644,7 +6771,7 @@ static void RedrawAllInvisibleElementsForLenses()
 
       /* uncrumble neighbour fields, if needed */
       if (element == EL_INVISIBLE_SAND)
-       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
+       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
     }
     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
             element == EL_INVISIBLE_WALL_ACTIVE ||
@@ -6657,7 +6784,7 @@ static void RedrawAllInvisibleElementsForLenses()
 
       /* re-crumble neighbour fields, if needed */
       if (element == EL_INVISIBLE_SAND)
-       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
+       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
     }
   }
 }
@@ -8071,7 +8198,7 @@ void StartMoving(int x, int y)
       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
       {
        if (!MovDelay[x][y])
-         MovDelay[x][y] = TILEY/4 + 1;
+         MovDelay[x][y] = TILEY / 4 + 1;
 
        if (MovDelay[x][y])
        {
@@ -8099,7 +8226,7 @@ void StartMoving(int x, int y)
       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
       {
        if (!MovDelay[x][y])
-         MovDelay[x][y] = TILEY/4 + 1;
+         MovDelay[x][y] = TILEY / 4 + 1;
 
        if (MovDelay[x][y])
        {
@@ -8127,7 +8254,7 @@ void StartMoving(int x, int y)
       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
       {
        if (!MovDelay[x][y])
-         MovDelay[x][y] = TILEY/4 + 1;
+         MovDelay[x][y] = TILEY / 4 + 1;
 
        if (MovDelay[x][y])
        {
@@ -8474,7 +8601,7 @@ void StartMoving(int x, int y)
 
            if (IN_SCR_FIELD(sx, sy))
            {
-             TEST_DrawLevelFieldCrumbledSand(xx, yy);
+             TEST_DrawLevelFieldCrumbled(xx, yy);
              DrawGraphic(sx, sy, flame_graphic, frame);
            }
          }
@@ -8977,7 +9104,7 @@ void ContinueMoving(int x, int y)
   {
     Feld[x][y] = EL_SAND;
 
-    TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
+    TEST_DrawLevelFieldCrumbledNeighbours(x, y);
   }
   else if (element == EL_QUICKSAND_FILLING)
   {
@@ -9144,7 +9271,7 @@ void ContinueMoving(int x, int y)
     InitField(x, y, FALSE);
 
     if (GFX_CRUMBLED(Feld[x][y]))
-      TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
+      TEST_DrawLevelFieldCrumbledNeighbours(x, y);
 
     if (ELEM_IS_PLAYER(move_leave_element))
       RelocatePlayer(x, y, move_leave_element);
@@ -9441,8 +9568,8 @@ void AmoebeUmwandelnBD(int ax, int ay, int new_element)
 
 void AmoebeWaechst(int x, int y)
 {
-  static unsigned long sound_delay = 0;
-  static unsigned long sound_delay_value = 0;
+  static unsigned int sound_delay = 0;
+  static unsigned int sound_delay_value = 0;
 
   if (!MovDelay[x][y])         /* start new growing cycle */
   {
@@ -9477,8 +9604,8 @@ void AmoebeWaechst(int x, int y)
 
 void AmoebaDisappearing(int x, int y)
 {
-  static unsigned long sound_delay = 0;
-  static unsigned long sound_delay_value = 0;
+  static unsigned int sound_delay = 0;
+  static unsigned int sound_delay_value = 0;
 
   if (!MovDelay[x][y])         /* start new shrinking cycle */
   {
@@ -10338,7 +10465,7 @@ static void ChangeActiveTrap(int x, int y)
 
   /* if new animation frame was drawn, correct crumbled sand border */
   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
-    TEST_DrawLevelFieldCrumbledSand(x, y);
+    TEST_DrawLevelFieldCrumbled(x, y);
 }
 
 static int getSpecialActionElement(int element, int number, int base_element)
@@ -11074,7 +11201,7 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change)
     TEST_DrawLevelField(x, y);
 
     if (GFX_CRUMBLED(new_element))
-      TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
+      TEST_DrawLevelFieldCrumbledNeighbours(x, y);
   }
 
 #if 1
@@ -11954,9 +12081,22 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
   }
 }
 
+static void CheckSingleStepMode(struct PlayerInfo *player)
+{
+  if (tape.single_step && tape.recording && !tape.pausing)
+  {
+    /* 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)
+    {
+      TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+      SnapField(player, 0, 0);                 /* stop snapping */
+    }
+  }
+}
+
 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
 {
-  boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
   int left     = player_action & JOY_LEFT;
   int right    = player_action & JOY_RIGHT;
   int up       = player_action & JOY_UP;
@@ -11972,23 +12112,16 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action)
   if (player_action)
   {
     if (button1)
-      snapped = SnapField(player, dx, dy);
+      SnapField(player, dx, dy);
     else
     {
       if (button2)
-       dropped = DropElement(player);
+       DropElement(player);
 
-      moved = MovePlayer(player, dx, dy);
+      MovePlayer(player, dx, dy);
     }
 
-    if (tape.single_step && tape.recording && !tape.pausing)
-    {
-      if (button1 || (dropped && !moved))
-      {
-       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
-       SnapField(player, 0, 0);                /* stop snapping */
-      }
-    }
+    CheckSingleStepMode(player);
 
     SetPlayerWaiting(player, FALSE);
 
@@ -12012,6 +12145,8 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action)
     player->is_dropping_pressed = FALSE;
     player->drop_pressed_delay = 0;
 
+    CheckSingleStepMode(player);
+
     return 0;
   }
 }
@@ -12020,6 +12155,7 @@ static void CheckLevelTime()
 {
   int i;
 
+  /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
   {
     if (level.native_em_level->lev->home == 0) /* all players at home */
@@ -12037,6 +12173,21 @@ static void CheckLevelTime()
        level.native_em_level->ply[3]->alive == 0)      /* all dead */
       AllPlayersGone = TRUE;
   }
+  else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+  {
+    if (game_sp.LevelSolved &&
+       !game_sp.GameOver)                              /* game won */
+    {
+      PlayerWins(local_player);
+
+      game_sp.GameOver = TRUE;
+
+      AllPlayersGone = TRUE;
+    }
+
+    if (game_sp.GameOver)                              /* game lost */
+      AllPlayersGone = TRUE;
+  }
 
   if (TimeFrames >= FRAMES_PER_SECOND)
   {
@@ -12068,9 +12219,13 @@ static void CheckLevelTime()
          PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
 
 #if 1
+       /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
+          is reset from other values in UpdateGameDoorValues() -- FIX THIS */
+
        game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
 
-       DisplayGameControlValues();
+       /* (already called by UpdateAndDisplayGameControlValues() below) */
+       // DisplayGameControlValues();
 #else
        DrawGameValue_Time(TimeLeft);
 #endif
@@ -12085,19 +12240,20 @@ static void CheckLevelTime()
        }
       }
 #if 1
-      else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
+      else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
       {
        game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
 
-       DisplayGameControlValues();
+       /* (already called by UpdateAndDisplayGameControlValues() below) */
+       // DisplayGameControlValues();
       }
 #else
-      else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
+      else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
        DrawGameValue_Time(TimePlayed);
 #endif
 
       level.native_em_level->lev->time =
-       (level.time == 0 ? TimePlayed : TimeLeft);
+       (game.no_time_limit ? TimePlayed : TimeLeft);
     }
 
     if (tape.recording || tape.playing)
@@ -12164,9 +12320,9 @@ void AdvanceFrameAndPlayerCounters(int player_nr)
 }
 
 void StartGameActions(boolean init_network_game, boolean record_tape,
-                     long random_seed)
+                     int random_seed)
 {
-  unsigned long new_random_seed = InitRND(random_seed);
+  unsigned int new_random_seed = InitRND(random_seed);
 
   if (record_tape)
     TapeStartRecording(new_random_seed);
@@ -12185,8 +12341,8 @@ void StartGameActions(boolean init_network_game, boolean record_tape,
 
 void GameActions()
 {
-  static unsigned long game_frame_delay = 0;
-  unsigned long game_frame_delay_value;
+  static unsigned int game_frame_delay = 0;
+  unsigned int game_frame_delay_value;
   byte *recorded_player_action;
   byte summarized_player_action = 0;
   byte tape_action[MAX_PLAYERS];
@@ -12195,9 +12351,9 @@ void GameActions()
   /* detect endless loops, caused by custom element programming */
   if (recursion_loop_detected && recursion_loop_depth == 0)
   {
-    char *message = getStringCat3("Internal Error ! Element ",
+    char *message = getStringCat3("Internal Error! Element ",
                                  EL_NAME(recursion_loop_element),
-                                 " caused endless loop ! Quit the game ?");
+                                 " caused endless loop! Quit the game?");
 
     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
          EL_NAME(recursion_loop_element));
@@ -12214,6 +12370,7 @@ void GameActions()
   if (game.restart_level)
     StartGameActions(options.network, setup.autorecord, level.random_seed);
 
+  /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
   {
     if (level.native_em_level->lev->home == 0) /* all players at home */
@@ -12231,6 +12388,21 @@ void GameActions()
        level.native_em_level->ply[3]->alive == 0)      /* all dead */
       AllPlayersGone = TRUE;
   }
+  else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+  {
+    if (game_sp.LevelSolved &&
+       !game_sp.GameOver)                              /* game won */
+    {
+      PlayerWins(local_player);
+
+      game_sp.GameOver = TRUE;
+
+      AllPlayersGone = TRUE;
+    }
+
+    if (game_sp.GameOver)                              /* game lost */
+      AllPlayersGone = TRUE;
+  }
 
   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
     GameWon();
@@ -12296,8 +12468,13 @@ void GameActions()
   {
     summarized_player_action |= stored_player[i].action;
 
+#if 1
+    if (!network_playing && (game.team_mode || tape.playing))
+      stored_player[i].effective_action = stored_player[i].action;
+#else
     if (!network_playing)
       stored_player[i].effective_action = stored_player[i].action;
+#endif
   }
 
 #if defined(NETWORK_AVALIABLE)
@@ -12305,10 +12482,13 @@ void GameActions()
     SendToServer_MovePlayer(summarized_player_action);
 #endif
 
-  if (!options.network && !setup.team_mode)
+  if (!options.network && !game.team_mode)
     local_player->effective_action = summarized_player_action;
 
-  if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
+  if (tape.recording &&
+      setup.team_mode &&
+      setup.input_on_focus &&
+      game.centered_player_nr != -1)
   {
     for (i = 0; i < MAX_PLAYERS; i++)
       stored_player[i].effective_action =
@@ -12323,9 +12503,19 @@ void GameActions()
   {
     tape_action[i] = stored_player[i].effective_action;
 
+#if 1
+    /* (this may happen in the RND game engine if a player was not present on
+       the playfield on level start, but appeared later from a custom element */
+    if (tape.recording &&
+       setup.team_mode &&
+       tape_action[i] &&
+       !tape.player_participates[i])
+      tape.player_participates[i] = TRUE;
+#else
     /* (this can only happen in the R'n'D game engine) */
     if (tape.recording && tape_action[i] && !tape.player_participates[i])
       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
+#endif
   }
 
   /* only record actions from input devices, but not programmed actions */
@@ -12333,16 +12523,76 @@ void GameActions()
     TapeRecordAction(tape_action);
 
 #if USE_NEW_PLAYER_ASSIGNMENTS
+#if 1
+  if (game.team_mode)
+#endif
   {
     byte mapped_action[MAX_PLAYERS];
 
+#if DEBUG_PLAYER_ACTIONS
+    printf(":::");
+    for (i = 0; i < MAX_PLAYERS; i++)
+      printf(" %d, ", stored_player[i].effective_action);
+#endif
+
     for (i = 0; i < MAX_PLAYERS; i++)
       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
 
     for (i = 0; i < MAX_PLAYERS; i++)
       stored_player[i].effective_action = mapped_action[i];
+
+#if DEBUG_PLAYER_ACTIONS
+    printf(" =>");
+    for (i = 0; i < MAX_PLAYERS; i++)
+      printf(" %d, ", stored_player[i].effective_action);
+    printf("\n");
+#endif
+  }
+#if DEBUG_PLAYER_ACTIONS
+  else
+  {
+    printf(":::");
+    for (i = 0; i < MAX_PLAYERS; i++)
+      printf(" %d, ", stored_player[i].effective_action);
+    printf("\n");
   }
 #endif
+#endif
+
+#if 0
+  printf("::: summarized_player_action == %d\n",
+        local_player->effective_action);
+#endif
+
+
+
+
+#if 0
+#if DEBUG_INIT_PLAYER
+    if (options.debug)
+    {
+      printf("Player status (final):\n");
+
+      for (i = 0; i < MAX_PLAYERS; i++)
+      {
+       struct PlayerInfo *player = &stored_player[i];
+
+       printf("- player %d: present == %d, connected == %d, active == %d",
+              i + 1,
+              player->present,
+              player->connected,
+              player->active);
+
+       if (local_player == player)
+         printf(" (local player)");
+
+       printf("\n");
+      }
+    }
+#endif
+#endif
+
+
 
   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
   {
@@ -12938,7 +13188,7 @@ void GameActions_RND()
   /* new experimental amoeba growth stuff */
   if (!(FrameCounter % 8))
   {
-    static unsigned long random = 1684108901;
+    static unsigned int random = 1684108901;
 
     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
     {
@@ -13102,10 +13352,10 @@ void GameActions_RND()
        DrawLevelField(x, y);
 
       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
-       DrawLevelFieldCrumbledSand(x, y);
+       DrawLevelFieldCrumbled(x, y);
 
       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
-       DrawLevelFieldCrumbledSandNeighbours(x, y);
+       DrawLevelFieldCrumbledNeighbours(x, y);
 
       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
        DrawTwinkleOnField(x, y);
@@ -13122,9 +13372,9 @@ void GameActions_RND()
 
   if (options.debug)                   /* calculate frames per second */
   {
-    static unsigned long fps_counter = 0;
+    static unsigned int fps_counter = 0;
     static int fps_frames = 0;
-    unsigned long fps_delay_ms = Counter() - fps_counter;
+    unsigned int fps_delay_ms = Counter() - fps_counter;
 
     fps_frames++;
 
@@ -13260,8 +13510,29 @@ void ScrollLevel(int dx, int dy)
 
 #else
 
+#if NEW_TILESIZE
+#if NEW_SCROLL
+  int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
+#else
+  int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
+#endif
+#else
+#if NEW_SCROLL
+  int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
+#else
   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
+#endif
+#endif
 
+#if NEW_TILESIZE
+  BlitBitmap(drawto_field, drawto_field,
+            FX + TILEX_VAR * (dx == -1) - softscroll_offset,
+            FY + TILEY_VAR * (dy == -1) - softscroll_offset,
+            SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
+            SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
+            FX + TILEX_VAR * (dx == 1) - softscroll_offset,
+            FY + TILEY_VAR * (dy == 1) - softscroll_offset);
+#else
   BlitBitmap(drawto_field, drawto_field,
             FX + TILEX * (dx == -1) - softscroll_offset,
             FY + TILEY * (dy == -1) - softscroll_offset,
@@ -13270,6 +13541,8 @@ void ScrollLevel(int dx, int dy)
             FX + TILEX * (dx == 1) - softscroll_offset,
             FY + TILEY * (dy == 1) - softscroll_offset);
 #endif
+
+#endif
 #endif
 
   if (dx != 0)
@@ -13532,7 +13805,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
     int original_move_delay_value = player->move_delay_value;
 
 #if DEBUG
-    printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
+    printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
           tape.counter);
 #endif
 
@@ -13914,14 +14187,14 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
            KillPlayer(&stored_player[i]);
       }
 #if 1
-      else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
+      else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
       {
        game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
 
        DisplayGameControlValues();
       }
 #else
-      else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
+      else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
        DrawGameValue_Time(TimePlayed);
 #endif
     }
@@ -13934,7 +14207,7 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
 
 void ScrollScreen(struct PlayerInfo *player, int mode)
 {
-  static unsigned long screen_frame_counter = 0;
+  static unsigned int screen_frame_counter = 0;
 
   if (mode == SCROLL_INIT)
   {
@@ -15348,8 +15621,13 @@ static int DigField(struct PlayerInfo *player,
        PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
                                    ACTION_FILLING);
 
+#if 1
+      if (local_player->sokobanfields_still_needed == 0 &&
+         (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
+#else
       if (local_player->sokobanfields_still_needed == 0 &&
          game.emulation == EMU_SOKOBAN)
+#endif
       {
        PlayerWins(player);
 
@@ -15472,6 +15750,7 @@ static int DigField(struct PlayerInfo *player,
       if (level.time > 0 || level.use_time_orb_bug)
       {
        TimeLeft += level.time_orb_time;
+       game.no_time_limit = FALSE;
 
 #if 1
        game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
@@ -15826,14 +16105,19 @@ static boolean DropElement(struct PlayerInfo *player)
   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
   {
-    int move_direction, nextx, nexty;
+#if 0
+    int move_direction;
+    int nextx, nexty;
+#endif
 
     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
       MovDir[dropx][dropy] = drop_direction;
 
+#if 0
     move_direction = MovDir[dropx][dropy];
     nextx = dropx + GET_DX_FROM_DIR(move_direction);
     nexty = dropy + GET_DY_FROM_DIR(move_direction);
+#endif
 
     ChangeCount[dropx][dropy] = 0;     /* allow at least one more change */
 
@@ -16135,30 +16419,20 @@ void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
   }
 }
 
-#if 0
-void ChangeTime(int value)
-{
-  int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
-
-  *time += value;
-
-  /* EMC game engine uses value from time counter of RND game engine */
-  level.native_em_level->lev->time = *time;
-
-  DrawGameValue_Time(*time);
-}
-
-void RaiseScore(int value)
+void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
 {
-  /* EMC game engine and RND game engine have separate score counters */
-  int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
-               &level.native_em_level->lev->score : &local_player->score);
+  int element = map_element_SP_to_RND(element_sp);
+  int action = map_action_SP_to_RND(action_sp);
+  int offset = (setup.sp_show_border_elements ? 0 : 1);
+  int x = xx - offset;
+  int y = yy - offset;
 
-  *score += value;
+#if 0
+  printf("::: %d -> %d\n", element_sp, action_sp);
+#endif
 
-  DrawGameValue_Score(*score);
+  PlayLevelSoundElementAction(x, y, element, action);
 }
-#endif
 
 void RaiseScore(int value)
 {
@@ -16257,6 +16531,12 @@ void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
 {
   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
   {
+#if 1
+    /* closing door required in case of envelope style request dialogs */
+    if (!skip_request)
+      CloseDoor(DOOR_CLOSE_1);
+#endif
+
 #if defined(NETWORK_AVALIABLE)
     if (options.network)
       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
@@ -16315,7 +16595,7 @@ void RequestQuitGame(boolean ask_if_really_quit)
   boolean skip_request = AllPlayersGone || quick_quit;
 
   RequestQuitGameExt(skip_request, quick_quit,
-                    "Do you really want to quit the game ?");
+                    "Do you really want to quit the game?");
 }
 
 
@@ -16323,7 +16603,7 @@ void RequestQuitGame(boolean ask_if_really_quit)
 /* random generator functions                                                */
 /* ------------------------------------------------------------------------- */
 
-unsigned int InitEngineRandom_RND(long seed)
+unsigned int InitEngineRandom_RND(int seed)
 {
   game.num_random_calls = 0;
 
@@ -16358,8 +16638,6 @@ unsigned int RND(int max)
 /* game engine snapshot handling functions                                   */
 /* ------------------------------------------------------------------------- */
 
-#define ARGS_ADDRESS_AND_SIZEOF(x)             (&(x)), (sizeof(x))
-
 struct EngineSnapshotInfo
 {
   /* runtime values for custom element collect score */
@@ -16369,32 +16647,14 @@ struct EngineSnapshotInfo
   int choice_pos[NUM_GROUP_ELEMENTS];
 
   /* runtime values for belt position animations */
-  int belt_graphic[4 * NUM_BELT_PARTS];
-  int belt_anim_mode[4 * NUM_BELT_PARTS];
-};
-
-struct EngineSnapshotNodeInfo
-{
-  void *buffer_orig;
-  void *buffer_copy;
-  int size;
+  int belt_graphic[4][NUM_BELT_PARTS];
+  int belt_anim_mode[4][NUM_BELT_PARTS];
 };
 
 static struct EngineSnapshotInfo engine_snapshot_rnd;
-static ListNode *engine_snapshot_list = NULL;
 static char *snapshot_level_identifier = NULL;
 static int snapshot_level_nr = -1;
 
-void FreeEngineSnapshot()
-{
-  while (engine_snapshot_list != NULL)
-    deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
-                      checked_free);
-
-  setString(&snapshot_level_identifier, NULL);
-  snapshot_level_nr = -1;
-}
-
 static void SaveEngineSnapshotValues_RND()
 {
   static int belt_base_active_element[4] =
@@ -16428,15 +16688,15 @@ static void SaveEngineSnapshotValues_RND()
       int graphic = el2img(element);
       int anim_mode = graphic_info[graphic].anim_mode;
 
-      engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
-      engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
+      engine_snapshot_rnd.belt_graphic[i][j] = graphic;
+      engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
     }
   }
 }
 
 static void LoadEngineSnapshotValues_RND()
 {
-  unsigned long num_random_calls = game.num_random_calls;
+  unsigned int num_random_calls = game.num_random_calls;
   int i, j;
 
   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
@@ -16457,8 +16717,8 @@ static void LoadEngineSnapshotValues_RND()
   {
     for (j = 0; j < NUM_BELT_PARTS; j++)
     {
-      int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
-      int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
+      int graphic = engine_snapshot_rnd.belt_graphic[i][j];
+      int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
 
       graphic_info[graphic].anim_mode = anim_mode;
     }
@@ -16480,36 +16740,54 @@ static void LoadEngineSnapshotValues_RND()
   }
 }
 
-static void SaveEngineSnapshotBuffer(void *buffer, int size)
+void FreeEngineSnapshot()
 {
-  struct EngineSnapshotNodeInfo *bi =
-    checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
-
-  bi->buffer_orig = buffer;
-  bi->buffer_copy = checked_malloc(size);
-  bi->size = size;
+  FreeEngineSnapshotBuffers();
 
-  memcpy(bi->buffer_copy, buffer, size);
-
-  addNodeToList(&engine_snapshot_list, NULL, bi);
+  setString(&snapshot_level_identifier, NULL);
+  snapshot_level_nr = -1;
 }
 
 void SaveEngineSnapshot()
 {
-  FreeEngineSnapshot();                /* free previous snapshot, if needed */
-
-  if (level_editor_test_game)  /* do not save snapshots from editor */
+  /* do not save snapshots from editor */
+  if (level_editor_test_game)
     return;
 
+  /* free previous snapshot buffers, if needed */
+  FreeEngineSnapshotBuffers();
+
+#if 1
+  /* copy some special values to a structure better suited for the snapshot */
+
+  if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
+    SaveEngineSnapshotValues_RND();
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+    SaveEngineSnapshotValues_EM();
+  if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+    SaveEngineSnapshotValues_SP();
+
+  /* save values stored in special snapshot structure */
+
+  if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
+    SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+    SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
+  if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+    SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
+#else
   /* copy some special values to a structure better suited for the snapshot */
 
   SaveEngineSnapshotValues_RND();
   SaveEngineSnapshotValues_EM();
+  SaveEngineSnapshotValues_SP();
 
   /* save values stored in special snapshot structure */
 
   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
+#endif
 
   /* save further RND engine values */
 
@@ -16583,7 +16861,7 @@ void SaveEngineSnapshot()
   snapshot_level_nr = level_nr;
 
 #if 0
-  ListNode *node = engine_snapshot_list;
+  ListNode *node = engine_snapshot_list_rnd;
   int num_bytes = 0;
 
   while (node != NULL)
@@ -16597,29 +16875,48 @@ void SaveEngineSnapshot()
 #endif
 }
 
-static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
-{
-  memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
-}
-
 void LoadEngineSnapshot()
 {
-  ListNode *node = engine_snapshot_list;
-
-  if (engine_snapshot_list == NULL)
-    return;
-
-  while (node != NULL)
-  {
-    LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
+  /* restore generically stored snapshot buffers */
 
-    node = node->next;
-  }
+  LoadEngineSnapshotBuffers();
 
   /* restore special values from snapshot structure */
 
+#if 1
+  if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
+    LoadEngineSnapshotValues_RND();
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+    LoadEngineSnapshotValues_EM();
+  if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+    LoadEngineSnapshotValues_SP();
+#else
   LoadEngineSnapshotValues_RND();
   LoadEngineSnapshotValues_EM();
+  LoadEngineSnapshotValues_SP();
+#endif
+
+#if 0
+  printf("::: %d, %d (LoadEngineSnapshot / 1)\n", scroll_x, scroll_y);
+#endif
+
+#if 0
+  // needed if tile size was different when saving and loading engine snapshot
+  if (local_player->present)
+  {
+    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);
+  }
+#endif
+
+#if 0
+  printf("::: %d, %d (LoadEngineSnapshot / 1)\n", scroll_x, scroll_y);
+#endif
 }
 
 boolean CheckEngineSnapshot()
@@ -16631,98 +16928,46 @@ boolean CheckEngineSnapshot()
 
 /* ---------- new game button stuff ---------------------------------------- */
 
-/* graphic position values for game buttons */
-#define GAME_BUTTON_XSIZE      30
-#define GAME_BUTTON_YSIZE      30
-#define GAME_BUTTON_XPOS       5
-#define GAME_BUTTON_YPOS       215
-#define SOUND_BUTTON_XPOS      5
-#define SOUND_BUTTON_YPOS      (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
-
-#define GAME_BUTTON_STOP_XPOS  (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
-#define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
-#define GAME_BUTTON_PLAY_XPOS  (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
-#define SOUND_BUTTON_MUSIC_XPOS        (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
-#define SOUND_BUTTON_LOOPS_XPOS        (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
-#define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
-
 static struct
 {
-  int *x, *y;
-  int gd_x, gd_y;
+  int graphic;
+  struct Rect *pos;
   int gadget_id;
   char *infotext;
 } gamebutton_info[NUM_GAME_BUTTONS] =
 {
-#if 1
-  {
-    &game.button.stop.x,       &game.button.stop.y,
-    GAME_BUTTON_STOP_XPOS,     GAME_BUTTON_YPOS,
-    GAME_CTRL_ID_STOP,
-    "stop game"
-  },
-  {
-    &game.button.pause.x,      &game.button.pause.y,
-    GAME_BUTTON_PAUSE_XPOS,    GAME_BUTTON_YPOS,
-    GAME_CTRL_ID_PAUSE,
-    "pause game"
-  },
-  {
-    &game.button.play.x,       &game.button.play.y,
-    GAME_BUTTON_PLAY_XPOS,     GAME_BUTTON_YPOS,
-    GAME_CTRL_ID_PLAY,
-    "play game"
-  },
   {
-    &game.button.sound_music.x,        &game.button.sound_music.y,
-    SOUND_BUTTON_MUSIC_XPOS,   SOUND_BUTTON_YPOS,
-    SOUND_CTRL_ID_MUSIC,
-    "background music on/off"
+    IMG_GAME_BUTTON_GFX_STOP,          &game.button.stop,
+    GAME_CTRL_ID_STOP,                 "stop game"
   },
   {
-    &game.button.sound_loops.x,        &game.button.sound_loops.y,
-    SOUND_BUTTON_LOOPS_XPOS,   SOUND_BUTTON_YPOS,
-    SOUND_CTRL_ID_LOOPS,
-    "sound loops on/off"
+    IMG_GAME_BUTTON_GFX_PAUSE,         &game.button.pause,
+    GAME_CTRL_ID_PAUSE,                        "pause game"
   },
   {
-    &game.button.sound_simple.x,&game.button.sound_simple.y,
-    SOUND_BUTTON_SIMPLE_XPOS,  SOUND_BUTTON_YPOS,
-    SOUND_CTRL_ID_SIMPLE,
-    "normal sounds on/off"
-  }
-#else
-  {
-    GAME_BUTTON_STOP_XPOS,     GAME_BUTTON_YPOS,
-    GAME_CTRL_ID_STOP,
-    "stop game"
+    IMG_GAME_BUTTON_GFX_PLAY,          &game.button.play,
+    GAME_CTRL_ID_PLAY,                 "play game"
   },
   {
-    GAME_BUTTON_PAUSE_XPOS,    GAME_BUTTON_YPOS,
-    GAME_CTRL_ID_PAUSE,
-    "pause game"
+    IMG_GAME_BUTTON_GFX_SOUND_MUSIC,   &game.button.sound_music,
+    SOUND_CTRL_ID_MUSIC,               "background music on/off"
   },
   {
-    GAME_BUTTON_PLAY_XPOS,     GAME_BUTTON_YPOS,
-    GAME_CTRL_ID_PLAY,
-    "play game"
+    IMG_GAME_BUTTON_GFX_SOUND_LOOPS,   &game.button.sound_loops,
+    SOUND_CTRL_ID_LOOPS,               "sound loops on/off"
   },
   {
-    SOUND_BUTTON_MUSIC_XPOS,   SOUND_BUTTON_YPOS,
-    SOUND_CTRL_ID_MUSIC,
-    "background music on/off"
+    IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,  &game.button.sound_simple,
+    SOUND_CTRL_ID_SIMPLE,              "normal sounds on/off"
   },
   {
-    SOUND_BUTTON_LOOPS_XPOS,   SOUND_BUTTON_YPOS,
-    SOUND_CTRL_ID_LOOPS,
-    "sound loops on/off"
+    IMG_GAME_BUTTON_GFX_SAVE,          &game.button.save,
+    GAME_CTRL_ID_SAVE,                 "save game"
   },
   {
-    SOUND_BUTTON_SIMPLE_XPOS,  SOUND_BUTTON_YPOS,
-    SOUND_CTRL_ID_SIMPLE,
-    "normal sounds on/off"
+    IMG_GAME_BUTTON_GFX_LOAD,          &game.button.load,
+    GAME_CTRL_ID_LOAD,                 "load game"
   }
-#endif
 };
 
 void CreateGameButtons()
@@ -16731,32 +16976,40 @@ void CreateGameButtons()
 
   for (i = 0; i < NUM_GAME_BUTTONS; i++)
   {
-    Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
+    struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
+    struct Rect *pos = gamebutton_info[i].pos;
     struct GadgetInfo *gi;
     int button_type;
     boolean checked;
-    unsigned long event_mask;
-    int x, y;
-    int gd_xoffset, gd_yoffset;
-    int gd_x1, gd_x2, gd_y1, gd_y2;
+    unsigned int event_mask;
+    int base_x = (tape.show_game_buttons ? VX : DX);
+    int base_y = (tape.show_game_buttons ? VY : DY);
+    int gd_x   = gfx->src_x;
+    int gd_y   = gfx->src_y;
+    int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
+    int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
+    int gd_xa  = gfx->src_x + gfx->active_xoffset;
+    int gd_ya  = gfx->src_y + gfx->active_yoffset;
+    int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
+    int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
     int id = i;
 
-    x = DX + *gamebutton_info[i].x;
-    y = DY + *gamebutton_info[i].y;
-    gd_xoffset = gamebutton_info[i].gd_x;
-    gd_yoffset = gamebutton_info[i].gd_y;
-    gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
-    gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
+    if (gfx->bitmap == NULL)
+    {
+      game_gadget[id] = NULL;
+
+      continue;
+    }
 
     if (id == GAME_CTRL_ID_STOP ||
        id == GAME_CTRL_ID_PAUSE ||
-       id == GAME_CTRL_ID_PLAY)
+       id == GAME_CTRL_ID_PLAY ||
+       id == GAME_CTRL_ID_SAVE ||
+       id == GAME_CTRL_ID_LOAD)
     {
       button_type = GD_TYPE_NORMAL_BUTTON;
       checked = FALSE;
       event_mask = GD_EVENT_RELEASED;
-      gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
-      gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
     }
     else
     {
@@ -16766,28 +17019,21 @@ void CreateGameButtons()
         (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
         (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
       event_mask = GD_EVENT_PRESSED;
-      gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
-      gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
     }
 
     gi = CreateGadget(GDI_CUSTOM_ID, id,
                      GDI_INFO_TEXT, gamebutton_info[i].infotext,
-#if 1
-                     GDI_X, x,
-                     GDI_Y, y,
-#else
-                     GDI_X, DX + gd_xoffset,
-                     GDI_Y, DY + gd_yoffset,
-#endif
-                     GDI_WIDTH, GAME_BUTTON_XSIZE,
-                     GDI_HEIGHT, GAME_BUTTON_YSIZE,
+                     GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
+                     GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
+                     GDI_WIDTH, gfx->width,
+                     GDI_HEIGHT, gfx->height,
                      GDI_TYPE, button_type,
                      GDI_STATE, GD_BUTTON_UNPRESSED,
                      GDI_CHECKED, checked,
-                     GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
-                     GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
-                     GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
-                     GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
+                     GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
+                     GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
+                     GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
+                     GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
                      GDI_DIRECT_DRAW, FALSE,
                      GDI_EVENT_MASK, event_mask,
                      GDI_CALLBACK_ACTION, HandleGameButtons,
@@ -16808,7 +17054,7 @@ void FreeGameButtons()
     FreeGadget(game_gadget[i]);
 }
 
-static void MapGameButtons()
+void MapGameButtons()
 {
   int i;
 
@@ -16830,26 +17076,35 @@ void RedrawGameButtons()
 
   for (i = 0; i < NUM_GAME_BUTTONS; i++)
     RedrawGadget(game_gadget[i]);
+
+  // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
+  redraw_mask &= ~REDRAW_ALL;
 }
 
-static void HandleGameButtons(struct GadgetInfo *gi)
+static void HandleGameButtonsExt(int id)
 {
-  int id = gi->custom_id;
+  boolean handle_game_buttons =
+    (game_status == GAME_MODE_PLAYING ||
+     (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
 
-  if (game_status != GAME_MODE_PLAYING)
+  if (!handle_game_buttons)
     return;
 
   switch (id)
   {
     case GAME_CTRL_ID_STOP:
+      if (game_status == GAME_MODE_MAIN)
+       break;
+
       if (tape.playing)
        TapeStop();
       else
        RequestQuitGame(TRUE);
+
       break;
 
     case GAME_CTRL_ID_PAUSE:
-      if (options.network)
+      if (options.network && game_status == GAME_MODE_PLAYING)
       {
 #if defined(NETWORK_AVALIABLE)
        if (tape.pausing)
@@ -16863,7 +17118,11 @@ static void HandleGameButtons(struct GadgetInfo *gi)
       break;
 
     case GAME_CTRL_ID_PLAY:
-      if (tape.pausing)
+      if (game_status == GAME_MODE_MAIN)
+      {
+        StartGameActions(options.network, setup.autorecord, level.random_seed);
+      }
+      else if (tape.pausing)
       {
 #if defined(NETWORK_AVALIABLE)
        if (options.network)
@@ -16881,6 +17140,7 @@ static void HandleGameButtons(struct GadgetInfo *gi)
       if (setup.sound_music)
       { 
        setup.sound_music = FALSE;
+
        FadeMusic();
       }
       else if (audio.music_available)
@@ -16899,6 +17159,7 @@ static void HandleGameButtons(struct GadgetInfo *gi)
       else if (audio.loops_available)
       {
        setup.sound = setup.sound_loops = TRUE;
+
        SetAudioMode(setup.sound);
       }
       break;
@@ -16909,11 +17170,44 @@ static void HandleGameButtons(struct GadgetInfo *gi)
       else if (audio.sound_available)
       {
        setup.sound = setup.sound_simple = TRUE;
+
        SetAudioMode(setup.sound);
       }
       break;
 
+    case GAME_CTRL_ID_SAVE:
+      TapeQuickSave();
+      break;
+
+    case GAME_CTRL_ID_LOAD:
+      TapeQuickLoad();
+      break;
+
     default:
       break;
   }
 }
+
+static void HandleGameButtons(struct GadgetInfo *gi)
+{
+  HandleGameButtonsExt(gi->custom_id);
+}
+
+void HandleSoundButtonKeys(Key key)
+{
+#if 1
+  if (key == setup.shortcut.sound_simple)
+    ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
+  else if (key == setup.shortcut.sound_loops)
+    ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
+  else if (key == setup.shortcut.sound_music)
+    ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
+#else
+  if (key == setup.shortcut.sound_simple)
+    HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
+  else if (key == setup.shortcut.sound_loops)
+    HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
+  else if (key == setup.shortcut.sound_music)
+    HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);
+#endif
+}