updated contact info in source file headers
[rocksndiamonds.git] / src / game.c
index 556c22b29aa87abd23b7a6975809761c739c2389..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
 
 #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
@@ -876,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
@@ -1049,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 */
@@ -1117,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);
@@ -1780,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;
 
@@ -2076,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 :
@@ -2229,7 +2193,7 @@ void UpdateGameControlValues()
              level.native_em_level->lev->time :
              level.game_engine_type == GAME_ENGINE_TYPE_SP ?
              level.native_sp_level->game_sp->time_played :
-             level.time == 0 ? TimePlayed : TimeLeft);
+             game.no_time_limit ? TimePlayed : TimeLeft);
   int score = (local_player->LevelSolved ?
               local_player->LevelSolved_CountingScore :
               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
@@ -2539,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
@@ -2781,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;
 
@@ -2804,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;
 
@@ -2827,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;
 
@@ -2854,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);
 
@@ -2904,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);
 
@@ -2934,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
@@ -3002,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;
 
@@ -3016,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)
 {
@@ -3173,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);
@@ -3231,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 */
   /* ---------------------------------------------------------------------- */
@@ -3705,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 */
@@ -3718,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();
@@ -3907,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;
@@ -3950,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];
@@ -4083,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) */
@@ -4103,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 */
@@ -4122,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++)
@@ -4142,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;
@@ -4177,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
       }
     }
@@ -4188,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 */
@@ -4238,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 &&
@@ -4246,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 &&
@@ -4267,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 */
 
@@ -4307,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)
   {
@@ -4338,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) */
@@ -4453,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)
@@ -4482,6 +4494,8 @@ void InitGame()
     FadeOut(REDRAW_FIELD);
 #endif
 
+#endif
+
 #if 0
   game_status = GAME_MODE_PLAYING;
 #endif
@@ -4491,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
@@ -4530,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();
@@ -4555,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);
 
@@ -4567,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
@@ -4582,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;
 }
 
@@ -4800,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;
 }
 
@@ -4825,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;
 
@@ -4835,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)
@@ -4843,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];
@@ -5029,6 +5120,7 @@ void GameEnd()
   if (level_nr == leveldir_current->handicap_level)
   {
     leveldir_current->handicap_level++;
+
     SaveLevelSetup_SeriesInfo();
   }
 
@@ -8106,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])
        {
@@ -8134,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])
        {
@@ -8162,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])
        {
@@ -9476,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 */
   {
@@ -9512,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 */
   {
@@ -11989,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;
@@ -12007,32 +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 1
-      /* 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)
-#else
-      /* this is buggy: there are quite some cases where the single step mode
-        does not return to pause mode (like pushing things that don't move
-        or simply by trying to run against a wall) */
-      if (button1 || (dropped && !moved))
-#endif
-      {
-       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
-       SnapField(player, 0, 0);                /* stop snapping */
-      }
-    }
+    CheckSingleStepMode(player);
 
     SetPlayerWaiting(player, FALSE);
 
@@ -12056,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;
   }
 }
@@ -12128,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
@@ -12145,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)
@@ -12224,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);
@@ -12245,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];
@@ -12255,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));
@@ -12372,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)
@@ -12381,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 =
@@ -12399,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 */
@@ -12409,17 +12523,77 @@ 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)
   {
     GameActions_EM_Main();
@@ -13014,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++)
     {
@@ -13198,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++;
 
@@ -13336,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,
@@ -13346,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)
@@ -13608,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
 
@@ -13990,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
     }
@@ -14010,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)
   {
@@ -15553,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;
@@ -15907,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 */
 
@@ -16231,31 +16434,6 @@ void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
   PlayLevelSoundElementAction(x, y, element, action);
 }
 
-#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)
-{
-  /* 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);
-
-  *score += value;
-
-  DrawGameValue_Score(*score);
-}
-#endif
-
 void RaiseScore(int value)
 {
   local_player->score += value;
@@ -16353,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);
@@ -16411,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?");
 }
 
 
@@ -16419,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;
 
@@ -16512,7 +16696,7 @@ static void SaveEngineSnapshotValues_RND()
 
 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++)
@@ -16556,6 +16740,14 @@ static void LoadEngineSnapshotValues_RND()
   }
 }
 
+void FreeEngineSnapshot()
+{
+  FreeEngineSnapshotBuffers();
+
+  setString(&snapshot_level_identifier, NULL);
+  snapshot_level_nr = -1;
+}
+
 void SaveEngineSnapshot()
 {
   /* do not save snapshots from editor */
@@ -16565,6 +16757,25 @@ void SaveEngineSnapshot()
   /* 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();
@@ -16576,6 +16787,7 @@ void SaveEngineSnapshot()
   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 */
 
@@ -16671,9 +16883,40 @@ void LoadEngineSnapshot()
 
   /* 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()
@@ -16685,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()
@@ -16785,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
     {
@@ -16820,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,
@@ -16862,7 +17054,7 @@ void FreeGameButtons()
     FreeGadget(game_gadget[i]);
 }
 
-static void MapGameButtons()
+void MapGameButtons()
 {
   int i;
 
@@ -16884,24 +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 HandleGameButtonsExt(int id)
 {
-  if (game_status != GAME_MODE_PLAYING)
+  boolean handle_game_buttons =
+    (game_status == GAME_MODE_PLAYING ||
+     (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
+
+  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)
@@ -16915,7 +17118,11 @@ static void HandleGameButtonsExt(int id)
       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)
@@ -16968,6 +17175,14 @@ static void HandleGameButtonsExt(int id)
       }
       break;
 
+    case GAME_CTRL_ID_SAVE:
+      TapeQuickSave();
+      break;
+
+    case GAME_CTRL_ID_LOAD:
+      TapeQuickLoad();
+      break;
+
     default:
       break;
   }