Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package rocksndiamonds for openSUSE:Factory 
checked in at 2021-02-02 14:25:20
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/rocksndiamonds (Old)
 and      /work/SRC/openSUSE:Factory/.rocksndiamonds.new.28504 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "rocksndiamonds"

Tue Feb  2 14:25:20 2021 rev:34 rq:868416 version:4.2.2.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/rocksndiamonds/rocksndiamonds.changes    
2021-01-05 18:15:59.975493494 +0100
+++ /work/SRC/openSUSE:Factory/.rocksndiamonds.new.28504/rocksndiamonds.changes 
2021-02-02 14:25:29.939375700 +0100
@@ -1,0 +2,19 @@
+Sat Jan 23 01:27:33 UTC 2021 - Dirk M??ller <dmuel...@suse.com>
+
+- don't recreate games user - managed by systemd-users-game
+- update to 4.2.2.1:
+  * fixed bug with playing team mode tapes with changed visible playfield size
+  * added patch mode to patch tapes to force visible playfield size of 34 x 34
+  * added patch mode to automatically fix tapes for visible playfield size
+  * fixed single step mode for R???n???D game engine when used in team mode
+  * fixed triggering custom element actions by digging or collecting
+  * added option in level editor to use time score for 1 or 10 seconds left
+  * fixed time score for native Emerald Mine and Diamond Caves levels
+  * fixed bug with screen keyboard still being active during request dialogs
+  * fixed bug with screen keyboard causing overlay buttons to be disabled
+  * added option to disable warning about read-only levels when entering editor
+  * fixed bug with not updating game panel when leaving invisible warp mode
+  * fixed displaying new high score entry if new entry is not on the first page
+  * fixed graphical bug with envelope style request dialog after the game 
ended 
+  
+-------------------------------------------------------------------

Old:
----
  rocksndiamonds-4.2.2.0.tar.gz

New:
----
  rocksndiamonds-4.2.2.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ rocksndiamonds.spec ++++++
--- /var/tmp/diff_new_pack.RDc7H5/_old  2021-02-02 14:25:30.599376726 +0100
+++ /var/tmp/diff_new_pack.RDc7H5/_new  2021-02-02 14:25:30.603376733 +0100
@@ -17,7 +17,7 @@
 
 
 Name:           rocksndiamonds
-Version:        4.2.2.0
+Version:        4.2.2.1
 Release:        0
 Summary:        Colorful Boulderdash'n'Emerald Mine'n'Sokoban'n'Stuff
 License:        GPL-2.0-or-later
@@ -31,16 +31,13 @@
 BuildRequires:  fdupes
 BuildRequires:  hicolor-icon-theme
 BuildRequires:  pkgconfig
-#BuildRequires:  libsmpeg-devel
+BuildRequires:  system-user-games
 BuildRequires:  update-desktop-files
 BuildRequires:  pkgconfig(SDL2_image)
 BuildRequires:  pkgconfig(SDL2_mixer)
 BuildRequires:  pkgconfig(SDL2_net)
 BuildRequires:  pkgconfig(zlib)
 Requires:       %{name}-data
-Requires(pre):  %{_sbindir}/groupadd
-Requires(pre):  %{_sbindir}/useradd
-Requires(pre):  group(games)
 Requires(pre):  user(games)
 
 %description
@@ -90,10 +87,6 @@
 
 %fdupes -s %{buildroot}%{_prefix}
 
-%pre
-getent group games >/dev/null || groupadd -r games
-getent passwd games >/dev/null || useradd -r -g games -d 
%{_localstatedir}/games -s /sbin/nologin
-
 %files
 %license COPYING
 %doc CREDITS ChangeLog

++++++ rocksndiamonds-4.2.2.0.tar.gz -> rocksndiamonds-4.2.2.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rocksndiamonds-4.2.2.0/CREDITS 
new/rocksndiamonds-4.2.2.1/CREDITS
--- old/rocksndiamonds-4.2.2.0/CREDITS  2020-10-02 12:38:50.000000000 +0200
+++ new/rocksndiamonds-4.2.2.1/CREDITS  2021-01-15 17:16:52.000000000 +0100
@@ -51,5 +51,7 @@
 
 Thanks to Simon Forsberg for being the moderator of the R'n'D forum.
 
+Thanks to Rick Jansen for white X-mas.
+
 And not to forget: Many thanks to all those who contributed levels to this game
 since 1995!
Binary files old/rocksndiamonds-4.2.2.0/rocksndiamonds and 
new/rocksndiamonds-4.2.2.1/rocksndiamonds differ
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rocksndiamonds-4.2.2.0/src/confhash.h 
new/rocksndiamonds-4.2.2.1/src/confhash.h
--- old/rocksndiamonds-4.2.2.0/src/confhash.h   2020-12-23 13:04:59.000000000 
+0100
+++ new/rocksndiamonds-4.2.2.1/src/confhash.h   2021-01-15 17:17:14.000000000 
+0100
@@ -1 +1 @@
-#define SOURCE_HASH_STRING "ba02ca3"
+#define SOURCE_HASH_STRING "b2c4f6e"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rocksndiamonds-4.2.2.0/src/conftime.h 
new/rocksndiamonds-4.2.2.1/src/conftime.h
--- old/rocksndiamonds-4.2.2.0/src/conftime.h   2020-12-23 13:04:59.000000000 
+0100
+++ new/rocksndiamonds-4.2.2.1/src/conftime.h   2021-01-15 17:17:14.000000000 
+0100
@@ -1 +1 @@
-#define SOURCE_DATE_STRING "2020-12-23 12:03"
+#define SOURCE_DATE_STRING "2021-01-10 00:08"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rocksndiamonds-4.2.2.0/src/editor.c 
new/rocksndiamonds-4.2.2.1/src/editor.c
--- old/rocksndiamonds-4.2.2.0/src/editor.c     2020-12-14 02:15:48.000000000 
+0100
+++ new/rocksndiamonds-4.2.2.1/src/editor.c     2021-01-15 17:16:52.000000000 
+0100
@@ -559,6 +559,7 @@
   // selectbox identifiers
 
   GADGET_ID_TIME_OR_STEPS,
+  GADGET_ID_TIME_SCORE_BASE,
   GADGET_ID_GAME_ENGINE_TYPE,
   GADGET_ID_LEVELSET_SAVE_MODE,
   GADGET_ID_WIND_DIRECTION,
@@ -648,6 +649,7 @@
   GADGET_ID_SB_OBJECTS_NEEDED,
   GADGET_ID_AUTO_EXIT_SOKOBAN,
   GADGET_ID_SOLVED_BY_ONE_PLAYER,
+  GADGET_ID_FINISH_DIG_COLLECT,
   GADGET_ID_CONTINUOUS_SNAPPING,
   GADGET_ID_BLOCK_SNAP_FIELD,
   GADGET_ID_BLOCK_LAST_FIELD,
@@ -839,6 +841,7 @@
 enum
 {
   ED_SELECTBOX_ID_TIME_OR_STEPS,
+  ED_SELECTBOX_ID_TIME_SCORE_BASE,
   ED_SELECTBOX_ID_GAME_ENGINE_TYPE,
   ED_SELECTBOX_ID_LEVELSET_SAVE_MODE,
   ED_SELECTBOX_ID_WIND_DIRECTION,
@@ -953,6 +956,7 @@
   ED_CHECKBUTTON_ID_SB_OBJECTS_NEEDED,
   ED_CHECKBUTTON_ID_AUTO_EXIT_SOKOBAN,
   ED_CHECKBUTTON_ID_SOLVED_BY_ONE_PLAYER,
+  ED_CHECKBUTTON_ID_FINISH_DIG_COLLECT,
   ED_CHECKBUTTON_ID_CONTINUOUS_SNAPPING,
   ED_CHECKBUTTON_ID_BLOCK_SNAP_FIELD,
   ED_CHECKBUTTON_ID_BLOCK_LAST_FIELD,
@@ -1408,7 +1412,7 @@
     GADGET_ID_LEVEL_TIMESCORE_DOWN,    GADGET_ID_LEVEL_TIMESCORE_UP,
     GADGET_ID_LEVEL_TIMESCORE_TEXT,    GADGET_ID_NONE,
     &level.score[SC_TIME_BONUS],
-    "score for each second/step left:",        NULL, NULL
+    "score for time or steps left:",   NULL, NULL
   },
   {
     ED_LEVEL_SETTINGS_XPOS(0),         ED_LEVEL_SETTINGS_YPOS(12),
@@ -1731,6 +1735,14 @@
   { -1,                                NULL                            }
 };
 
+static struct ValueTextInfo options_time_score_base[] =
+{
+  { 1,                         "per second/step"               },
+  { 10,                                "per 10 seconds/steps"          },
+
+  { -1,                                NULL                            }
+};
+
 static struct ValueTextInfo options_game_engine_type[] =
 {
   { GAME_ENGINE_TYPE_RND,      "Rocks'n'Diamonds"              },
@@ -2466,6 +2478,14 @@
     NULL, NULL, "(0 => no limit)",     "time or step limit"
   },
   {
+    -1,                                        ED_LEVEL_SETTINGS_YPOS(10),
+    GADGET_ID_TIME_SCORE_BASE,         GADGET_ID_LEVEL_TIMESCORE_UP,
+    -1,
+    options_time_score_base,
+    &level.time_score_base,
+    NULL, NULL, NULL,                  "time score for 1 or 10 seconds/steps"
+  },
+  {
     ED_LEVEL_SETTINGS_XPOS(0),         ED_LEVEL_SETTINGS_YPOS(11),
     GADGET_ID_GAME_ENGINE_TYPE,                GADGET_ID_NONE,
     -1,
@@ -3111,6 +3131,13 @@
     "only one player must enter exit", "level solved by first player in exit"
   },
   {
+    ED_ELEMENT_SETTINGS_XPOS(0),       ED_ELEMENT_SETTINGS_YPOS(3),
+    GADGET_ID_FINISH_DIG_COLLECT,      GADGET_ID_NONE,
+    &level.finish_dig_collect,
+    NULL, NULL,
+    "CE action on finished dig/collect", "only finished dig/collect triggers 
CE"
+  },
+  {
     ED_ELEMENT_SETTINGS_XPOS(0),       ED_ELEMENT_SETTINGS_YPOS(9),
     GADGET_ID_CONTINUOUS_SNAPPING,     GADGET_ID_NONE,
     &level.continuous_snapping,
@@ -7722,7 +7749,7 @@
     return TRUE;
   }
 
-  if (!Request("This level is read only! "
+  if (!Request("This level is read-only! "
               "Save into personal level set?", REQ_ASK))
     return FALSE;
 
@@ -9983,6 +10010,7 @@
 
       // draw checkbutton gadgets
       MapCheckbuttonGadget(ED_CHECKBUTTON_ID_USE_INITIAL_INVENTORY);
+      MapCheckbuttonGadget(ED_CHECKBUTTON_ID_FINISH_DIG_COLLECT);
 
       // draw counter gadgets
       MapCounterButtons(ED_COUNTER_ID_INVENTORY_SIZE);
@@ -13382,7 +13410,7 @@
     if (levelset_save_mode == LEVELSET_SAVE_MODE_UPDATE &&
        leveldir_current->readonly)
     {
-      Request("This level set is read only!", REQ_CONFIRM);
+      Request("This level set is read-only!", REQ_CONFIRM);
 
       return;
     }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rocksndiamonds-4.2.2.0/src/engines.h 
new/rocksndiamonds-4.2.2.1/src/engines.h
--- old/rocksndiamonds-4.2.2.0/src/engines.h    2020-12-14 02:15:48.000000000 
+0100
+++ new/rocksndiamonds-4.2.2.1/src/engines.h    2021-01-15 17:16:52.000000000 
+0100
@@ -31,6 +31,9 @@
 boolean getTeamMode_EM(void);
 boolean isActivePlayer_EM(int);
 
+int getScreenFieldSizeX(void);
+int getScreenFieldSizeY(void);
+
 void PlayLevelSound_EM(int, int, int, int);
 void InitGraphicInfo_EM(void);
 boolean CheckSingleStepMode_EM(byte action[], int, boolean, boolean, boolean);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rocksndiamonds-4.2.2.0/src/events.c 
new/rocksndiamonds-4.2.2.1/src/events.c
--- old/rocksndiamonds-4.2.2.0/src/events.c     2020-12-23 13:04:38.000000000 
+0100
+++ new/rocksndiamonds-4.2.2.1/src/events.c     2021-01-15 17:16:52.000000000 
+0100
@@ -1476,7 +1476,7 @@
     if (key_status == KEY_PRESSED)
       SetOverlayEnabled(!GetOverlayEnabled());
   }
-  else
+  else if (!textinput_status)
   {
     // for any other "real" key event, disable virtual buttons
     SetOverlayEnabled(FALSE);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rocksndiamonds-4.2.2.0/src/files.c 
new/rocksndiamonds-4.2.2.1/src/files.c
--- old/rocksndiamonds-4.2.2.0/src/files.c      2020-12-23 13:04:38.000000000 
+0100
+++ new/rocksndiamonds-4.2.2.1/src/files.c      2021-01-15 17:16:52.000000000 
+0100
@@ -59,6 +59,7 @@
 #define TAPE_CHUNK_VERS_SIZE   8       // size of file version chunk
 #define TAPE_CHUNK_HEAD_SIZE   20      // size of tape file header
 #define TAPE_CHUNK_HEAD_UNUSED 1       // unused tape header bytes
+#define TAPE_CHUNK_SCRN_SIZE   2       // size of screen size chunk
 
 #define LEVEL_CHUNK_CNT3_SIZE(x)        (LEVEL_CHUNK_CNT3_HEADER + (x))
 #define LEVEL_CHUNK_CUS3_SIZE(x)        (2 + (x) * LEVEL_CPART_CUS3_SIZE)
@@ -259,6 +260,12 @@
 
   {
     -1,                                        -1,
+    TYPE_INTEGER,                      CONF_VALUE_8_BIT(12),
+    &li.time_score_base,               1
+  },
+
+  {
+    -1,                                        -1,
     -1,                                        -1,
     NULL,                              -1
   }
@@ -307,6 +314,11 @@
     TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(15),
     &li.lazy_relocation,               FALSE
   },
+  {
+    EL_PLAYER_1,                       -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(16),
+    &li.finish_dig_collect,            TRUE
+  },
 
   // (these values are different for each player)
   {
@@ -3710,6 +3722,9 @@
     if (jx != -1 && jy != -1)
       level->field[jx][jy] = EL_PLAYER_1 + nr;
   }
+
+  // time score is counted for each 10 seconds left in Emerald Mine levels
+  level->time_score_base = 10;
 }
 
 
@@ -5727,6 +5742,9 @@
   // Diamond Caves has the same (strange) behaviour as Emerald Mine that gems
   // can slip down from flat walls, like normal walls and steel walls
   level->em_slippery_gems = TRUE;
+
+  // time score is counted for each 10 seconds left in Diamond Caves levels
+  level->time_score_base = 10;
 }
 
 static void LoadLevelFromFileInfo_DC(struct LevelInfo *level,
@@ -6250,7 +6268,7 @@
   if (level->game_version < VERSION_IDENT(3,2,0,5))
   {
     // time bonus score was given for 10 s instead of 1 s before 3.2.0-5
-    level->score[SC_TIME_BONUS] /= 10;
+    level->time_score_base = 10;
   }
 
   if (leveldir_current->latest_engine)
@@ -6422,6 +6440,10 @@
   // only Sokoban fields (but not objects) had to be solved before 4.1.1.1
   if (level->game_version < VERSION_IDENT(4,1,1,1))
     level->sb_objects_needed = FALSE;
+
+  // CE actions were triggered by unfinished digging/collecting up to 4.2.2.0
+  if (level->game_version <= VERSION_IDENT(4,2,2,0))
+    level->finish_dig_collect = FALSE;
 }
 
 static void LoadLevel_InitStandardElements(struct LevelInfo *level)
@@ -7628,6 +7650,9 @@
   tape.playing = FALSE;
   tape.pausing = FALSE;
 
+  tape.scr_fieldx = SCR_FIELDX_DEFAULT;
+  tape.scr_fieldy = SCR_FIELDY_DEFAULT;
+
   tape.no_valid_file = FALSE;
 }
 
@@ -7718,6 +7743,14 @@
   return chunk_size;
 }
 
+static int LoadTape_SCRN(File *file, int chunk_size, struct TapeInfo *tape)
+{
+  tape->scr_fieldx = getFile8Bit(file);
+  tape->scr_fieldy = getFile8Bit(file);
+
+  return chunk_size;
+}
+
 static int LoadTape_INFO(File *file, int chunk_size, struct TapeInfo *tape)
 {
   int level_identifier_size;
@@ -8008,6 +8041,7 @@
     {
       { "VERS", TAPE_CHUNK_VERS_SIZE,  LoadTape_VERS },
       { "HEAD", TAPE_CHUNK_HEAD_SIZE,  LoadTape_HEAD },
+      { "SCRN", TAPE_CHUNK_SCRN_SIZE,  LoadTape_SCRN },
       { "INFO", -1,                    LoadTape_INFO },
       { "BODY", -1,                    LoadTape_BODY },
       {  NULL,  0,                     NULL }
@@ -8088,6 +8122,14 @@
     CopyNativeTape_SP_to_RND(&level);
 }
 
+static boolean checkSaveTape_SCRN(struct TapeInfo *tape)
+{
+  // chunk required for team mode tapes with non-default screen size
+  return (tape->num_participating_players > 1 &&
+         (tape->scr_fieldx != SCR_FIELDX_DEFAULT ||
+          tape->scr_fieldy != SCR_FIELDY_DEFAULT));
+}
+
 static void SaveTape_VERS(FILE *file, struct TapeInfo *tape)
 {
   putFileVersion(file, tape->file_version);
@@ -8120,6 +8162,12 @@
   putFileVersion(file, tape->engine_version);
 }
 
+static void SaveTape_SCRN(FILE *file, struct TapeInfo *tape)
+{
+  putFile8Bit(file, tape->scr_fieldx);
+  putFile8Bit(file, tape->scr_fieldy);
+}
+
 static void SaveTape_INFO(FILE *file, struct TapeInfo *tape)
 {
   int level_identifier_size = strlen(tape->level_identifier) + 1;
@@ -8185,6 +8233,12 @@
   putFileChunkBE(file, "HEAD", TAPE_CHUNK_HEAD_SIZE);
   SaveTape_HEAD(file, &tape);
 
+  if (checkSaveTape_SCRN(&tape))
+  {
+    putFileChunkBE(file, "SCRN", TAPE_CHUNK_SCRN_SIZE);
+    SaveTape_SCRN(file, &tape);
+  }
+
   putFileChunkBE(file, "INFO", info_chunk_size);
   SaveTape_INFO(file, &tape);
 
@@ -8693,6 +8747,10 @@
     TYPE_SWITCH,
     &setup.editor.show_element_token,          "editor.show_element_token"
   },
+  {
+    TYPE_SWITCH,
+    &setup.editor.show_read_only_warning,      "editor.show_read_only_warning"
+  },
 };
 
 static struct TokenInfo editor_cascade_setup_tokens[] =
@@ -9324,6 +9382,8 @@
 
   si->editor.show_element_token                = FALSE;
 
+  si->editor.show_read_only_warning    = TRUE;
+
   si->editor.use_template_for_new_levels = TRUE;
 
   si->shortcut.save_game       = DEFAULT_KEY_SAVE_GAME;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rocksndiamonds-4.2.2.0/src/game.c 
new/rocksndiamonds-4.2.2.1/src/game.c
--- old/rocksndiamonds-4.2.2.0/src/game.c       2020-12-23 13:04:38.000000000 
+0100
+++ new/rocksndiamonds-4.2.2.1/src/game.c       2021-01-15 17:16:52.000000000 
+0100
@@ -1119,6 +1119,8 @@
 static int getInvisibleActiveFromInvisibleElement(int);
 static int getInvisibleFromInvisibleActiveElement(int);
 
+static void TestFieldAfterSnapping(int, int, int, int, int);
+
 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
 
 // for detection of endless loops, caused by custom element programming
@@ -2867,12 +2869,10 @@
   DisplayGameControlValues();
 }
 
-#if 0
-static void UpdateGameDoorValues(void)
+void UpdateGameDoorValues(void)
 {
   UpdateGameControlValues();
 }
-#endif
 
 void DrawGameDoorValues(void)
 {
@@ -3576,11 +3576,15 @@
   InitGameEngine();
   InitGameControlValues();
 
-  // initialize tape actions from game when recording tape
   if (tape.recording)
   {
+    // initialize tape actions from game when recording tape
     tape.use_key_actions   = game.use_key_actions;
     tape.use_mouse_actions = game.use_mouse_actions;
+
+    // initialize visible playfield size when recording tape (for team mode)
+    tape.scr_fieldx = SCR_FIELDX;
+    tape.scr_fieldy = SCR_FIELDY;
   }
 
   // don't play tapes over network
@@ -3713,6 +3717,8 @@
     player->shield_normal_time_left = 0;
     player->shield_deadly_time_left = 0;
 
+    player->last_removed_element = EL_UNDEFINED;
+
     player->inventory_infinite_element = EL_UNDEFINED;
     player->inventory_size = 0;
 
@@ -4710,7 +4716,7 @@
 {
   static int time_count_steps;
   static int time, time_final;
-  static int score, score_final;
+  static float score, score_final; // needed for time score < 10 for 10 seconds
   static int health, health_final;
   static int game_over_delay_1 = 0;
   static int game_over_delay_2 = 0;
@@ -4718,6 +4724,8 @@
   int game_over_delay_value_1 = 50;
   int game_over_delay_value_2 = 25;
   int game_over_delay_value_3 = 50;
+  int time_score_base = MIN(MAX(1, level.time_score_base), 10);
+  float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
 
   if (!game.LevelSolved_GameWon)
   {
@@ -4751,19 +4759,23 @@
     score = score_final = game.score_final;
     health = health_final = game.health_final;
 
-    if (level.score[SC_TIME_BONUS] > 0)
+    if (time_score > 0)
     {
+      int time_frames = 0;
+
       if (TimeLeft > 0)
       {
        time_final = 0;
-       score_final += TimeLeft * level.score[SC_TIME_BONUS];
+       time_frames = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
       }
       else if (game.no_time_limit && TimePlayed < 999)
       {
        time_final = 999;
-       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
+       time_frames = (999 - TimePlayed) * FRAMES_PER_SECOND - TimeFrames;
       }
 
+      score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
+
       time_count_steps = MAX(1, ABS(time_final - time) / 100);
 
       game_over_delay_1 = game_over_delay_value_1;
@@ -4771,7 +4783,7 @@
       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
       {
        health_final = 0;
-       score_final += health * level.score[SC_TIME_BONUS];
+       score_final += health * time_score;
 
        game_over_delay_2 = game_over_delay_value_2;
       }
@@ -4860,7 +4872,11 @@
       time_count_steps = 1;
 
     time  += time_count_steps * time_count_dir;
-    score += time_count_steps * level.score[SC_TIME_BONUS];
+    score += time_count_steps * time_score;
+
+    // set final score to correct rounding differences after counting score
+    if (time == time_final)
+      score = score_final;
 
     game.LevelSolved_CountingTime = time;
     game.LevelSolved_CountingScore = score;
@@ -4892,7 +4908,7 @@
     int health_count_dir = (health < health_final ? +1 : -1);
 
     health += health_count_dir;
-    score  += level.score[SC_TIME_BONUS];
+    score  += time_score;
 
     game.LevelSolved_CountingHealth = health;
     game.LevelSolved_CountingScore = score;
@@ -11282,13 +11298,14 @@
 {
   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 &&
-       !player->is_dropping_pressed &&
-       !player->effective_mouse_action.button)
-      TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+    // 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)
+    // (reverse logic needed here in case single step mode used in team mode)
+    if (player->is_moving ||
+       player->is_pushing ||
+       player->is_dropping_pressed ||
+       player->effective_mouse_action.button)
+      game.enter_single_step_mode = FALSE;
   }
 
   CheckSaveEngineSnapshot(player);
@@ -11951,6 +11968,10 @@
     DrawGameDoorValues();
   }
 
+  // check single step mode (set flag and clear again if any player is active)
+  game.enter_single_step_mode =
+    (tape.single_step && tape.recording && !tape.pausing);
+
   for (i = 0; i < MAX_PLAYERS; i++)
   {
     int actual_player_action = stored_player[i].effective_action;
@@ -11975,6 +11996,10 @@
     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
   }
 
+  // single step pause mode may already have been toggled by "ScrollPlayer()"
+  if (game.enter_single_step_mode && !tape.pausing)
+    TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+
   ScrollScreen(NULL, SCROLL_GO_ON);
 
   /* for backwards compatibility, the following code emulates a fixed bug that
@@ -12027,10 +12052,17 @@
       MovDelay[x][y]--;
       if (MovDelay[x][y] <= 0)
       {
+       int element = Store[x][y];
+       int move_direction = MovDir[x][y];
+       int player_index_bit = Store2[x][y];
+
+       Store[x][y] = 0;
+       Store2[x][y] = 0;
+
        RemoveField(x, y);
        TEST_DrawLevelField(x, y);
 
-       TestIfElementTouchesCustomElement(x, y);        // for empty space
+       TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
       }
     }
 
@@ -12448,6 +12480,8 @@
 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
 {
   int min_x = x, min_y = y, max_x = x, max_y = y;
+  int scr_fieldx = getScreenFieldSizeX();
+  int scr_fieldy = getScreenFieldSizeY();
   int i;
 
   for (i = 0; i < MAX_PLAYERS; i++)
@@ -12463,7 +12497,7 @@
     max_y = MAX(max_y, jy);
   }
 
-  return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
+  return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
 }
 
 static boolean AllPlayersInVisibleScreen(void)
@@ -13029,6 +13063,21 @@
       if (!player->is_pushing)
        TestIfElementTouchesCustomElement(jx, jy);      // for empty space
 
+      if (level.finish_dig_collect &&
+         (player->is_digging || player->is_collecting))
+      {
+       int last_element = player->last_removed_element;
+       int move_direction = player->MovDir;
+       int enter_side = MV_DIR_OPPOSITE(move_direction);
+       int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
+                           CE_PLAYER_COLLECTS_X);
+
+       CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
+                                           player->index_bit, enter_side);
+
+       player->last_removed_element = EL_UNDEFINED;
+      }
+
       if (!player->active)
        RemovePlayer(player);
     }
@@ -13763,7 +13812,8 @@
     game.players_still_needed--;
 }
 
-static void setFieldForSnapping(int x, int y, int element, int direction)
+static void SetFieldForSnapping(int x, int y, int element, int direction,
+                               int player_index_bit)
 {
   struct ElementInfo *ei = &element_info[element];
   int direction_bit = MV_DIR_TO_BIT(direction);
@@ -13773,6 +13823,9 @@
 
   Tile[x][y] = EL_ELEMENT_SNAPPING;
   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
+  MovDir[x][y] = direction;
+  Store[x][y] = element;
+  Store2[x][y] = player_index_bit;
 
   ResetGfxAnimation(x, y);
 
@@ -13782,6 +13835,20 @@
   GfxFrame[x][y] = -1;
 }
 
+static void TestFieldAfterSnapping(int x, int y, int element, int direction,
+                                  int player_index_bit)
+{
+  TestIfElementTouchesCustomElement(x, y);     // for empty space
+
+  if (level.finish_dig_collect)
+  {
+    int dig_side = MV_DIR_OPPOSITE(direction);
+
+    CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
+                                       player_index_bit, dig_side);
+  }
+}
+
 /*
   =============================================================================
   checkDiagonalPushing()
@@ -14061,22 +14128,28 @@
 
     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
 
-    CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
-                                       player->index_bit, dig_side);
+    // use old behaviour for old levels (digging)
+    if (!level.finish_dig_collect)
+    {
+      CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
+                                         player->index_bit, dig_side);
 
-    // if digging triggered player relocation, finish digging tile
-    if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
-      setFieldForSnapping(x, y, element, move_direction);
+      // if digging triggered player relocation, finish digging tile
+      if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
+       SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
+    }
 
     if (mode == DF_SNAP)
     {
       if (level.block_snap_field)
-       setFieldForSnapping(x, y, element, move_direction);
+       SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
       else
-       TestIfElementTouchesCustomElement(x, y);        // for empty space
+       TestFieldAfterSnapping(x, y, element, move_direction, 
player->index_bit);
 
-      CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
-                                         player->index_bit, dig_side);
+      // use old behaviour for old levels (snapping)
+      if (!level.finish_dig_collect)
+       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
+                                           player->index_bit, dig_side);
     }
   }
   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
@@ -14188,25 +14261,28 @@
     RaiseScoreElement(element);
     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
 
-    if (is_player)
+    // use old behaviour for old levels (collecting)
+    if (!level.finish_dig_collect && is_player)
     {
       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
                                          player->index_bit, dig_side);
 
       // if collecting triggered player relocation, finish collecting tile
       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
-       setFieldForSnapping(x, y, element, move_direction);
+       SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
     }
 
     if (mode == DF_SNAP)
     {
       if (level.block_snap_field)
-       setFieldForSnapping(x, y, element, move_direction);
+       SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
       else
-       TestIfElementTouchesCustomElement(x, y);        // for empty space
+       TestFieldAfterSnapping(x, y, element, move_direction, 
player->index_bit);
 
-      CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
-                                         player->index_bit, dig_side);
+      // use old behaviour for old levels (snapping)
+      if (!level.finish_dig_collect)
+       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
+                                           player->index_bit, dig_side);
     }
   }
   else if (player_can_move_or_snap && IS_PUSHABLE(element))
@@ -14544,6 +14620,8 @@
     {
       player->is_collecting = !player->is_digging;
       player->is_active = TRUE;
+
+      player->last_removed_element = element;
     }
   }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rocksndiamonds-4.2.2.0/src/game.h 
new/rocksndiamonds-4.2.2.1/src/game.h
--- old/rocksndiamonds-4.2.2.0/src/game.h       2020-12-23 13:04:38.000000000 
+0100
+++ new/rocksndiamonds-4.2.2.1/src/game.h       2021-01-15 17:16:52.000000000 
+0100
@@ -244,6 +244,9 @@
   boolean set_centered_player;
   boolean set_centered_player_wrap;
 
+  // values for single step mode control
+  boolean enter_single_step_mode;
+
   // values for random number generator initialization after snapshot
   unsigned int num_random_calls;
 
@@ -378,6 +381,8 @@
   int shield_normal_time_left;
   int shield_deadly_time_left;
 
+  int last_removed_element;
+
   int inventory_element[MAX_INVENTORY_SIZE];
   int inventory_infinite_element;
   int inventory_size;
@@ -396,7 +401,7 @@
 
 int getPlayerInventorySize(int);
 
-void DrawGameValue_Time(int);
+void UpdateGameDoorValues(void);
 void DrawGameDoorValues(void);
 
 void UpdateAndDisplayGameControlValues(void);
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rocksndiamonds-4.2.2.0/src/game_em/graphics.c 
new/rocksndiamonds-4.2.2.1/src/game_em/graphics.c
--- old/rocksndiamonds-4.2.2.0/src/game_em/graphics.c   2020-12-05 
10:47:39.000000000 +0100
+++ new/rocksndiamonds-4.2.2.1/src/game_em/graphics.c   2021-01-15 
17:16:52.000000000 +0100
@@ -553,11 +553,13 @@
 boolean checkIfAllPlayersFitToScreen(void)
 {
   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
+  int scr_fieldx = getScreenFieldSizeX();
+  int scr_fieldy = getScreenFieldSizeY();
 
   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
 
-  return (sx2 - sx1 <= SCR_FIELDX * TILEX &&
-         sy2 - sy1 <= SCR_FIELDY * TILEY);
+  return (sx2 - sx1 <= scr_fieldx * TILEX &&
+         sy2 - sy1 <= scr_fieldy * TILEY);
 }
 
 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rocksndiamonds-4.2.2.0/src/game_em/reademc.c 
new/rocksndiamonds-4.2.2.1/src/game_em/reademc.c
--- old/rocksndiamonds-4.2.2.0/src/game_em/reademc.c    2020-10-02 
12:38:50.000000000 +0200
+++ new/rocksndiamonds-4.2.2.1/src/game_em/reademc.c    2021-01-15 
17:16:52.000000000 +0100
@@ -312,7 +312,7 @@
   cav.nut_score                = src[2090];
   cav.dynamite_score   = src[2091];
   cav.key_score                = src[2092];
-  cav.exit_score       = src[2093] * 8 / 5;
+  cav.exit_score       = src[2093];
 
   cav.lenses_score     = src[2151];
   cav.magnify_score    = src[2152];
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rocksndiamonds-4.2.2.0/src/game_mm/mm_tools.c 
new/rocksndiamonds-4.2.2.1/src/game_mm/mm_tools.c
--- old/rocksndiamonds-4.2.2.0/src/game_mm/mm_tools.c   2020-12-14 
02:15:48.000000000 +0100
+++ new/rocksndiamonds-4.2.2.1/src/game_mm/mm_tools.c   2021-01-15 
17:16:52.000000000 +0100
@@ -648,6 +648,7 @@
 #define XSN_CHANGE_DELAY       30
 #define XSN_CHANGE_FACTOR      3
 #define XSN_ALPHA_DEFAULT      XSN_ALPHA_VALUE(95)
+#define XSN_ALPHA_VISIBLE      XSN_ALPHA_VALUE(50)
 #define XSN_DEBUG_STEPS                5
 
 static byte xsn_bits_0[] = { 0x05, 0x02, 0x05 };
@@ -707,6 +708,8 @@
   struct XsnItem items[XSN_MAX_ITEMS];
 
   Bitmap *bitmap;
+
+  int alpha;
 };
 
 static struct Xsn xsn = { 0 };
@@ -847,7 +850,7 @@
       BlitBitmapMasked(xsn.bitmap, xsn.bitmap, xpos1, xsn.max_height,
                       xsize, xsn.max_height, xpos1, 0);
 
-      SDLSetAlpha(surface_masked, TRUE, XSN_ALPHA_DEFAULT);
+      SDLSetAlpha(surface_masked, TRUE, xsn.alpha);
 
       for (i = xpos1; i < xpos2; i++)
        xsn.height[i] = MIN(xsn.height[i] + shrink, xsn.area_ysize - 1);
@@ -1015,6 +1018,8 @@
     xsn.change_type  = 0;
     xsn.change_dir   = 0;
 
+    xsn.alpha = XSN_ALPHA_DEFAULT;
+
     for (i = 0; i < xsn.max_items; i++)
       xsn_init_item(i);
   }
@@ -1045,8 +1050,8 @@
     SDL_SetColorKey(surface_masked, SET_TRANSPARENT_PIXEL,
                    SDL_MapRGB(surface_masked->format, 0x00, 0x00, 0x00));
 
-    SDLSetAlpha(surface, TRUE, XSN_ALPHA_DEFAULT);
-    SDLSetAlpha(surface_masked, TRUE, XSN_ALPHA_DEFAULT);
+    SDLSetAlpha(surface, TRUE, xsn.alpha);
+    SDLSetAlpha(surface_masked, TRUE, xsn.alpha);
 
     SDLCreateBitmapTextures(xsn.bitmap);
 
@@ -1103,6 +1108,20 @@
     change_delay_value = xsn.change_delay * 1000;
   }
 
+  int xsn_alpha_dx = (gfx.mouse_y > xsn.area_ysize - xsn.max_height ?
+                     (xsn.alpha > XSN_ALPHA_VISIBLE ? -1 : 0) :
+                     (xsn.alpha < XSN_ALPHA_DEFAULT ? +1 : 0));
+
+  if (xsn_alpha_dx != 0)
+  {
+    xsn.alpha += xsn_alpha_dx;
+
+    SDLSetAlpha(xsn.bitmap->surface_masked, TRUE, xsn.alpha);
+
+    SDLFreeBitmapTextures(xsn.bitmap);
+    SDLCreateBitmapTextures(xsn.bitmap);
+  }
+
   BlitToScreenMasked(xsn.bitmap, 0, 0, xsn.area_xsize, xsn.max_height,
                     0, xsn.area_ysize - xsn.max_height);
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rocksndiamonds-4.2.2.0/src/libgame/system.h 
new/rocksndiamonds-4.2.2.1/src/libgame/system.h
--- old/rocksndiamonds-4.2.2.0/src/libgame/system.h     2020-12-23 
13:04:38.000000000 +0100
+++ new/rocksndiamonds-4.2.2.1/src/libgame/system.h     2021-01-15 
17:16:52.000000000 +0100
@@ -1284,6 +1284,8 @@
 
   boolean show_element_token;
 
+  boolean show_read_only_warning;
+
   boolean use_template_for_new_levels;
 };
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rocksndiamonds-4.2.2.0/src/main.h 
new/rocksndiamonds-4.2.2.1/src/main.h
--- old/rocksndiamonds-4.2.2.0/src/main.h       2020-12-23 13:04:38.000000000 
+0100
+++ new/rocksndiamonds-4.2.2.1/src/main.h       2021-01-15 17:16:52.000000000 
+0100
@@ -2571,7 +2571,7 @@
 #define PROGRAM_VERSION_SUPER          4
 #define PROGRAM_VERSION_MAJOR          2
 #define PROGRAM_VERSION_MINOR          2
-#define PROGRAM_VERSION_PATCH          0
+#define PROGRAM_VERSION_PATCH          1
 #define PROGRAM_VERSION_EXTRA          ""
 
 #define PROGRAM_TITLE_STRING           "Rocks'n'Diamonds"
@@ -3186,6 +3186,7 @@
   boolean sb_objects_needed;   // all Sokoban objects must be solved
   boolean auto_exit_sokoban;   // automatically finish solved Sokoban levels
   boolean solved_by_one_player;        // level is solved if one player enters 
exit
+  boolean finish_dig_collect;  // only finished dig/collect triggers ce action
 
   boolean continuous_snapping; // repeated snapping without releasing key
   boolean block_snap_field;    // snapping blocks field to show animation
@@ -3203,6 +3204,8 @@
   // ('int' instead of 'boolean' because used as selectbox value in editor)
   int use_step_counter;                // count steps instead of seconds for 
level
 
+  int time_score_base;         // use time score for 1 or 10 seconds/steps
+
   short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY];
 
   boolean use_custom_template; // use custom properties from template file
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rocksndiamonds-4.2.2.0/src/screens.c 
new/rocksndiamonds-4.2.2.1/src/screens.c
--- old/rocksndiamonds-4.2.2.0/src/screens.c    2020-12-23 13:04:39.000000000 
+0100
+++ new/rocksndiamonds-4.2.2.1/src/screens.c    2021-01-15 17:16:52.000000000 
+0100
@@ -2168,10 +2168,10 @@
       else if (pos == MAIN_CONTROL_EDITOR)
       {
        if (leveldir_current->readonly &&
-           !strEqual(setup.player_name, "Artsoft"))
-         Request("This level is read only!", REQ_CONFIRM);
+           setup.editor.show_read_only_warning)
+         Request("This level is read-only!", REQ_CONFIRM | REQ_STAY_OPEN);
 
-       CloseDoor(DOOR_CLOSE_2);
+       CloseDoor(DOOR_CLOSE_ALL);
 
        SetGameStatus(GAME_MODE_EDITOR);
 
@@ -4258,15 +4258,13 @@
 
     name[xpos] = 0;
   }
-  else if (key == KSYM_Return)
+  else if (key == KSYM_Return || key == KSYM_Escape)
   {
-    setTypeNameValues(name, pos, TRUE);
+    boolean changed = (key == KSYM_Return);
 
-    active = FALSE;
-  }
-  else if (key == KSYM_Escape)
-  {
-    setTypeNameValues(name, pos, FALSE);
+    StopTextInput();
+
+    setTypeNameValues(name, pos, changed);
 
     active = FALSE;
   }
@@ -4275,8 +4273,6 @@
 
   if (!active)
   {
-    StopTextInput();
-
     SetGameStatus(game_status_last_screen);
 
     if (game_status == GAME_MODE_MAIN)
@@ -5084,9 +5080,15 @@
   if (button == MB_MENU_INITIALIZE)
   {
     level_nr = mx;
-    first_entry = 0;
     highlight_position = my;
 
+    first_entry = highlight_position - (NUM_MENU_ENTRIES_ON_SCREEN + 1) / 2 + 
1;
+
+    if (first_entry < 0)
+      first_entry = 0;
+    else if (first_entry + NUM_MENU_ENTRIES_ON_SCREEN > MAX_SCORE_ENTRIES)
+      first_entry = MAX(0, MAX_SCORE_ENTRIES - NUM_MENU_ENTRIES_ON_SCREEN);
+
     drawHallOfFameList(level_nr, first_entry, highlight_position);
 
     return;
@@ -6733,6 +6735,8 @@
 #endif
   { TYPE_SWITCH, &setup.editor.show_element_token,     "Show element token:" },
   { TYPE_EMPTY,                NULL,                   ""                      
},
+  { TYPE_SWITCH, &setup.editor.show_read_only_warning, "Show read-only 
warning:" },
+  { TYPE_EMPTY,                NULL,                   ""                      
},
   { TYPE_LEAVE_MENU,   execSetupMain,          "Back"                  },
 
   { 0,                 NULL,                   NULL                    }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rocksndiamonds-4.2.2.0/src/tape.c 
new/rocksndiamonds-4.2.2.1/src/tape.c
--- old/rocksndiamonds-4.2.2.0/src/tape.c       2020-12-14 02:15:48.000000000 
+0100
+++ new/rocksndiamonds-4.2.2.1/src/tape.c       2021-01-15 17:16:52.000000000 
+0100
@@ -459,6 +459,8 @@
   if (redraw_display)
   {
     RedrawPlayfield();
+
+    UpdateGameDoorValues();
     DrawGameDoorValues();
   }
 }
@@ -1237,6 +1239,7 @@
   {
     "original tape",
     "em_random_bug",
+    "screen_34x34",
 
     NULL
   };
@@ -1244,6 +1247,7 @@
   {
     VERSION_IDENT(0,0,0,0),
     VERSION_IDENT(3,3,1,0),
+    VERSION_IDENT(0,0,0,0),
 
     -1
   };
@@ -1251,6 +1255,7 @@
   {
     VERSION_IDENT(9,9,9,9),
     VERSION_IDENT(4,0,1,1),
+    VERSION_IDENT(4,2,2,0),
 
     -1
   };
@@ -1258,6 +1263,7 @@
   {
     TAPE_PROPERTY_NONE,
     TAPE_PROPERTY_EM_RANDOM_BUG,
+    TAPE_PROPERTY_NONE,
 
     -1
   };
@@ -1408,6 +1414,8 @@
 
     if (global.autoplay_mode == AUTOPLAY_MODE_FIX)
     {
+      boolean skip_patch = FALSE;
+
       if (tape.engine_version < patch_version_first[patch_nr] ||
          tape.engine_version > patch_version_last[patch_nr])
       {
@@ -1420,6 +1428,22 @@
              (tape.engine_version / 100    ) % 100,
              (tape.engine_version          ) % 100);
 
+       skip_patch = TRUE;
+      }
+
+      if (strEqual(patch_name[patch_nr], "screen_34x34") &&
+         tape.num_participating_players == 1)
+      {
+       Print("Tape %03d %s[%02d:%02d]: (%s) - skipped.\n",
+             level_nr,  tape_patch_info,
+             tape.length_seconds / 60, tape.length_seconds % 60,
+             "not suitable for single player tapes");
+
+       skip_patch = TRUE;
+      }
+
+      if (skip_patch)
+      {
        if (patch_name[patch_nr + 1] != NULL)
        {
          // continue with next patch
@@ -1434,7 +1458,15 @@
        continue;
       }
 
-      tape.property_bits |= patch_property_bit[patch_nr];
+      if (strEqual(patch_name[patch_nr], "screen_34x34"))
+      {
+       tape.scr_fieldx = SCR_FIELDX_DEFAULT * 2;
+       tape.scr_fieldy = SCR_FIELDY_DEFAULT * 2;
+      }
+      else
+      {
+       tape.property_bits |= patch_property_bit[patch_nr];
+      }
     }
 
     InitCounter();
@@ -1493,14 +1525,14 @@
     return FALSE;
   }
 
-  byte property_bits = tape->property_bits;
+  boolean unpatch_tape = FALSE;
+  boolean use_property_bit = FALSE;
   byte property_bitmask = 0;
-  boolean set_property_bit = TRUE;
 
   if (strSuffix(mode, ":0") ||
       strSuffix(mode, ":off") ||
       strSuffix(mode, ":clear"))
-    set_property_bit = FALSE;
+    unpatch_tape = TRUE;
 
   if (strEqual(mode, "em_random_bug") || strPrefix(mode, "em_random_bug:"))
   {
@@ -1514,6 +1546,41 @@
     }
 
     property_bitmask = TAPE_PROPERTY_EM_RANDOM_BUG;
+
+    use_property_bit = TRUE;
+  }
+  else if (strEqual(mode, "screen_34x34") || strPrefix(mode, "screen_34x34:"))
+  {
+    // this bug only affects team mode tapes
+    if (tape->num_participating_players == 1)
+    {
+      Print("Only team mode tapes can be patched against screen size bug!\n");
+
+      return FALSE;
+    }
+
+    // this bug (that always existed before) was fixed in version 4.2.2.1
+    if (tape->engine_version >= VERSION_IDENT(4,2,2,1))
+    {
+      Print("This tape version cannot be patched against screen size bug!\n");
+
+      return FALSE;
+    }
+
+    int factor = (unpatch_tape ? 1 : 2);
+    int scr_fieldx_new = SCR_FIELDX_DEFAULT * factor;
+    int scr_fieldy_new = SCR_FIELDY_DEFAULT * factor;
+
+    if (scr_fieldx_new == tape->scr_fieldx &&
+       scr_fieldy_new == tape->scr_fieldy)
+    {
+      Print("Tape already patched for '%s'!\n", mode);
+
+      return FALSE;
+    }
+
+    tape->scr_fieldx = scr_fieldx_new;
+    tape->scr_fieldy = scr_fieldy_new;
   }
   else
   {
@@ -1522,22 +1589,29 @@
     return FALSE;
   }
 
-  if (set_property_bit)
-    property_bits |= property_bitmask;
-  else
-    property_bits &= ~property_bitmask;
-
-  if (property_bits == tape->property_bits)
+  // patching tapes using property bits may be used for several patch modes
+  if (use_property_bit)
   {
-    Print("Tape already patched for '%s'!\n", mode);
+    byte property_bits = tape->property_bits;
+    boolean set_property_bit = (unpatch_tape ? FALSE : TRUE);
 
-    return FALSE;
+    if (set_property_bit)
+      property_bits |= property_bitmask;
+    else
+      property_bits &= ~property_bitmask;
+
+    if (property_bits == tape->property_bits)
+    {
+      Print("Tape already patched for '%s'!\n", mode);
+
+      return FALSE;
+    }
+
+    tape->property_bits = property_bits;
   }
 
   Print("Patching for '%s' ... ", mode);
 
-  tape->property_bits = property_bits;
-
   return TRUE;
 }
 
@@ -1554,6 +1628,7 @@
     PrintLine("=", 79);
     Print("Supported patch modes:\n");
     Print("- \"em_random_bug\"   - use 64-bit random value bug for EM 
engine\n");
+    Print("- \"screen_34x34\"    - force visible playfield size of 34 x 34\n");
     PrintLine("-", 79);
     Print("Supported modifiers:\n");
     Print("- add \":0\", \":off\" or \":clear\" to patch mode to un-patch tape 
file\n");
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rocksndiamonds-4.2.2.0/src/tape.h 
new/rocksndiamonds-4.2.2.1/src/tape.h
--- old/rocksndiamonds-4.2.2.0/src/tape.h       2020-11-11 21:13:14.000000000 
+0100
+++ new/rocksndiamonds-4.2.2.1/src/tape.h       2021-01-15 17:16:52.000000000 
+0100
@@ -211,6 +211,10 @@
   // bits to indicate which tape properties are stored in this tape
   byte property_bits;
 
+  // visible playfield size when recording this tape (for team mode)
+  int scr_fieldx;
+  int scr_fieldy;
+
   struct
   {
     byte action[MAX_TAPE_ACTIONS];
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rocksndiamonds-4.2.2.0/src/tools.c 
new/rocksndiamonds-4.2.2.1/src/tools.c
--- old/rocksndiamonds-4.2.2.0/src/tools.c      2020-12-23 13:04:39.000000000 
+0100
+++ new/rocksndiamonds-4.2.2.1/src/tools.c      2021-01-15 17:16:52.000000000 
+0100
@@ -391,6 +391,16 @@
     return getLevelFromScreenY_RND(y);
 }
 
+int getScreenFieldSizeX(void)
+{
+  return (tape.playing ? tape.scr_fieldx : SCR_FIELDX);
+}
+
+int getScreenFieldSizeY(void)
+{
+  return (tape.playing ? tape.scr_fieldy : SCR_FIELDY);
+}
+
 void DumpTile(int x, int y)
 {
   int sx = SCREENX(x);
@@ -4264,7 +4274,7 @@
 #define MAX_REQUEST_LINE_FONT1_LEN     7
 #define MAX_REQUEST_LINE_FONT2_LEN     10
 
-static int RequestHandleEvents(unsigned int req_state)
+static int RequestHandleEvents(unsigned int req_state, int draw_buffer_game)
 {
   boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
                             checkGameEnded());
@@ -4293,7 +4303,7 @@
 
     if (game_just_ended)
     {
-      SetDrawtoField(draw_buffer_last);
+      SetDrawtoField(draw_buffer_game);
 
       HandleGameActions();
 
@@ -4594,6 +4604,7 @@
 
 static boolean RequestDoor(char *text, unsigned int req_state)
 {
+  int draw_buffer_last = GetDrawtoField();
   unsigned int old_door_state;
   int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
   int font_nr = FONT_TEXT_2;
@@ -4735,7 +4746,7 @@
   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
 
   // ---------- handle request buttons ----------
-  result = RequestHandleEvents(req_state);
+  result = RequestHandleEvents(req_state, draw_buffer_last);
 
   UnmapToolButtons();
 
@@ -4776,6 +4787,7 @@
 
 static boolean RequestEnvelope(char *text, unsigned int req_state)
 {
+  int draw_buffer_last = GetDrawtoField();
   int result;
 
   if (game_status == GAME_MODE_PLAYING)
@@ -4827,7 +4839,7 @@
   SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
 
   // ---------- handle request buttons ----------
-  result = RequestHandleEvents(req_state);
+  result = RequestHandleEvents(req_state, draw_buffer_last);
 
   UnmapToolButtons();
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/rocksndiamonds-4.2.2.0/src/tools.h 
new/rocksndiamonds-4.2.2.1/src/tools.h
--- old/rocksndiamonds-4.2.2.0/src/tools.h      2020-12-23 13:04:39.000000000 
+0100
+++ new/rocksndiamonds-4.2.2.1/src/tools.h      2021-01-15 17:16:52.000000000 
+0100
@@ -72,6 +72,9 @@
 int getLevelFromScreenX(int);
 int getLevelFromScreenY(int);
 
+int getScreenFieldSizeX(void);
+int getScreenFieldSizeY(void);
+
 void DumpTile(int, int);
 void DumpTileFromScreen(int, int);
 

Reply via email to