rnd-20040309-1-src
[rocksndiamonds.git] / src / game.c
index 9a836f9f68a34a3e3e604bbe7eb4aa3efd4cebf7..6a797d86e7a0ff2545d3009c56cf9d776f79a93b 100644 (file)
@@ -241,18 +241,38 @@ int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
 static void InitBeltMovement(void);
 static void CloseAllOpenTimegates(void);
 static void CheckGravityMovement(struct PlayerInfo *);
+static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
 static void KillHeroUnlessEnemyProtected(int, int);
 static void KillHeroUnlessExplosionProtected(int, int);
 
 static void TestIfPlayerTouchesCustomElement(int, int);
 static void TestIfElementTouchesCustomElement(int, int);
 static void TestIfElementHitsCustomElement(int, int, int);
+#if 0
+static void TestIfElementSmashesCustomElement(int, int, int);
+#endif
 
 static void ChangeElement(int, int, int);
-static boolean CheckTriggeredElementSideChange(int, int, int, int, int);
-static boolean CheckTriggeredElementChange(int, int, int, int);
-static boolean CheckElementSideChange(int, int, int, int, int, int);
-static boolean CheckElementChange(int, int, int, int);
+
+static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
+#define CheckTriggeredElementChange(x, y, e, ev)                       \
+       CheckTriggeredElementChangeExt(x, y, e, ev, -1, CH_SIDE_ANY, -1)
+#define CheckTriggeredElementChangePlayer(x, y, e, ev, p, s)           \
+       CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
+#define CheckTriggeredElementChangeSide(x, y, e, ev, s)                        \
+       CheckTriggeredElementChangeExt(x, y, e, ev, -1, s, -1)
+#define CheckTriggeredElementChangePage(x, y, e, ev, p)                        \
+       CheckTriggeredElementChangeExt(x, y, e, ev, -1, CH_SIDE_ANY, p)
+
+static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
+#define CheckElementChange(x, y, e, ev)                                        \
+       CheckElementChangeExt(x, y, e, ev, -1, CH_SIDE_ANY, -1)
+#define CheckElementChangePlayer(x, y, e, ev, p, s)                    \
+       CheckElementChangeExt(x, y, e, ev, p, s, -1)
+#define CheckElementChangeSide(x, y, e, ev, s)                         \
+       CheckElementChangeExt(x, y, e, ev, -1, s, -1)
+#define CheckElementChangePage(x, y, e, ev, p)                         \
+       CheckElementChangeExt(x, y, e, ev, -1, CH_SIDE_ANY, p)
 
 static void PlayLevelSound(int, int, int);
 static void PlayLevelSoundNearest(int, int, int);
@@ -1336,6 +1356,7 @@ void InitGame()
     struct PlayerInfo *player = &stored_player[i];
 
     player->index_nr = i;
+    player->index_bit = (1 << i);
     player->element_nr = EL_PLAYER_1 + i;
 
     player->present = FALSE;
@@ -1454,6 +1475,7 @@ void InitGame()
     player->shield_normal_time_left = 0;
     player->shield_deadly_time_left = 0;
 
+    player->inventory_infinite_element = EL_UNDEFINED;
     player->inventory_size = 0;
 
     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
@@ -1477,6 +1499,7 @@ void InitGame()
   TimeFrames = 0;
   TimePlayed = 0;
   TimeLeft = level.time;
+  TapeTime = 0;
 
   ScreenMovDir = MV_NO_MOVING;
   ScreenMovPos = 0;
@@ -1611,6 +1634,10 @@ void InitGame()
          some_player->present = FALSE;
          some_player->active = FALSE;
 
+#if 0
+         player->element_nr = some_player->element_nr;
+#endif
+
          StorePlayer[jx][jy] = player->element_nr;
          player->jx = player->last_jx = jx;
          player->jy = player->last_jy = jy;
@@ -2518,6 +2545,9 @@ void RelocatePlayer(int x, int y, int element_raw)
   boolean no_delay = (tape.index_search);
   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
+#if 1
+  int old_jx, old_jy;
+#endif
 
   if (player->GameOver)                /* do not reanimate dead player */
     return;
@@ -2547,9 +2577,49 @@ void RelocatePlayer(int x, int y, int element_raw)
     player->is_moving = FALSE;
   }
 
+#if 1
+  old_jx = player->jx;
+  old_jy = player->jy;
+#endif
+
   Feld[x][y] = element;
   InitPlayerField(x, y, element, TRUE);
 
+#if 0
+  if (player == local_player)
+  {
+#if 1
+
+    scroll_x += (local_player->jx - old_jx);
+    scroll_y += (local_player->jy - old_jy);
+
+    /* don't scroll over playfield boundaries */
+    if (scroll_x < SBX_Left || scroll_x > SBX_Right)
+      scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
+
+    /* don't scroll over playfield boundaries */
+    if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
+      scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
+
+#else
+    scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
+               local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
+               local_player->jx - MIDPOSX);
+
+    scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
+               local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
+               local_player->jy - MIDPOSY);
+#endif
+
+    RedrawPlayfield(TRUE, 0,0,0,0);
+#if 0
+    DrawAllPlayers();
+    BackToFront();
+#endif
+  }
+
+#else
+
   if (player == local_player)
   {
     int scroll_xx = -999, scroll_yy = -999;
@@ -2589,6 +2659,7 @@ void RelocatePlayer(int x, int y, int element_raw)
       Delay(wait_delay_value);
     }
   }
+#endif
 }
 
 void Explode(int ex, int ey, int phase, int mode)
@@ -2620,6 +2691,10 @@ void Explode(int ex, int ey, int phase, int mode)
   {
     int center_element = Feld[ex][ey];
 
+#if 0
+    printf("::: start explosion %d,%d [%d]\n", ex, ey, FrameCounter);
+#endif
+
 #if 0
     /* --- This is only really needed (and now handled) in "Impact()". --- */
     /* do not explode moving elements that left the explode field in time */
@@ -2653,10 +2728,15 @@ void Explode(int ex, int ey, int phase, int mode)
       int yy = y - ey + 1;
       int element;
 
+#if 1
+      if (!IN_LEV_FIELD(x, y) || (mode != EX_NORMAL && (x != ex || y != ey)))
+       continue;
+#else
       if (!IN_LEV_FIELD(x, y) ||
          ((mode != EX_NORMAL || center_element == EL_AMOEBA_TO_DIAMOND) &&
           (x != ex || y != ey)))
        continue;
+#endif
 
       element = Feld[x][y];
 
@@ -2955,6 +3035,10 @@ void Explode(int ex, int ey, int phase, int mode)
   {
     int element;
 
+#if 0
+    printf("::: explosion %d,%d done [%d]\n", x, y, FrameCounter);
+#endif
+
     element = Feld[x][y] = Store[x][y];
     Store[x][y] = Store2[x][y] = 0;
     GfxElement[x][y] = EL_UNDEFINED;
@@ -3164,6 +3248,9 @@ void Bang(int x, int y)
     case EL_PENGUIN:
     case EL_LAMP:
     case EL_LAMP_ACTIVE:
+#if 1
+    case EL_AMOEBA_TO_DIAMOND:
+#endif
       if (IS_PLAYER(x, y))
        Explode(x, y, EX_PHASE_START, EX_NORMAL);
       else
@@ -3760,12 +3847,15 @@ void Impact(int x, int y)
        }
        else
        {
+#if 0
+         TestIfElementSmashesCustomElement(x, y, MV_DOWN);
+#endif
+
          CheckElementChange(x, y + 1, smashed, CE_SMASHED);
 
-         CheckTriggeredElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
-                                         CE_OTHER_IS_SWITCHING);
-         CheckElementSideChange(x, y + 1, smashed, CH_SIDE_TOP,
-                                CE_SWITCHED, -1);
+         CheckTriggeredElementChangeSide(x, y + 1, smashed,
+                                         CE_OTHER_IS_SWITCHING, CH_SIDE_TOP);
+         CheckElementChangeSide(x, y + 1, smashed, CE_SWITCHED, CH_SIDE_TOP);
        }
       }
       else
@@ -5622,8 +5712,8 @@ void ContinueMoving(int x, int y)
     int hitting_element = Feld[newx][newy];
 
     /* !!! fix side (direction) orientation here and elsewhere !!! */
-    CheckElementSideChange(newx, newy, hitting_element,
-                          direction, CE_HITTING_SOMETHING, -1);
+    CheckElementChangeSide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
+                          direction);
 
 #if 0
     if (IN_LEV_FIELD(nextx, nexty))
@@ -5640,8 +5730,8 @@ void ContinueMoving(int x, int y)
       {
        int i;
 
-       CheckElementSideChange(nextx, nexty, touched_element,
-                              opposite_direction, CE_HIT_BY_SOMETHING, -1);
+       CheckElementChangeSide(nextx, nexty, touched_element,
+                              CE_HIT_BY_SOMETHING, opposite_direction);
 
        if (IS_CUSTOM_ELEMENT(hitting_element) &&
            HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
@@ -5653,11 +5743,11 @@ void ContinueMoving(int x, int y)
 
            if (change->can_change &&
                change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
-               change->sides & touched_side &&
+               change->trigger_side & touched_side &&
                change->trigger_element == touched_element)
            {
-             CheckElementSideChange(newx, newy, hitting_element,
-                                    CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
+             CheckElementChangePage(newx, newy, hitting_element,
+                                    CE_OTHER_IS_HITTING, i);
              break;
            }
          }
@@ -5673,11 +5763,11 @@ void ContinueMoving(int x, int y)
 
            if (change->can_change &&
                change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
-               change->sides & hitting_side &&
+               change->trigger_side & hitting_side &&
                change->trigger_element == hitting_element)
            {
-             CheckElementSideChange(nextx, nexty, touched_element,
-                                    CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
+             CheckElementChangePage(nextx, nexty, touched_element,
+                                    CE_OTHER_GETS_HIT, i);
              break;
            }
          }
@@ -6649,7 +6739,7 @@ static boolean ChangeElementNow(int x, int y, int element, int page)
 
   Changed[x][y] |= ChangeEvent[x][y];  /* ignore same changes in this frame */
 
-  CheckTriggeredElementChange(x, y, Feld[x][y], CE_OTHER_IS_CHANGING);
+  CheckTriggeredElementChangePage(x,y, Feld[x][y], CE_OTHER_IS_CHANGING, page);
 
   if (change->explode)
   {
@@ -6823,10 +6913,12 @@ static void ChangeElement(int x, int y, int page)
   }
 }
 
-static boolean CheckTriggeredElementSideChange(int lx, int ly,
-                                              int trigger_element,
-                                              int trigger_side,
-                                              int trigger_event)
+static boolean CheckTriggeredElementChangeExt(int lx, int ly,
+                                             int trigger_element,
+                                             int trigger_event,
+                                             int trigger_player,
+                                             int trigger_side,
+                                             int trigger_page)
 {
   int i, j, x, y;
 
@@ -6848,16 +6940,11 @@ static boolean CheckTriggeredElementSideChange(int lx, int ly,
       struct ElementChangeInfo *change = &element_info[element].change_page[j];
 
       if (change->can_change &&
-#if 1
          change->events & CH_EVENT_BIT(trigger_event) &&
-#endif
-         change->sides & trigger_side &&
-#if 1
-         IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)
-#else
-         change->trigger_element == trigger_element
-#endif
-         )
+         change->trigger_side & trigger_side &&
+         change->trigger_player & trigger_player &&
+         change->trigger_page & (1 << trigger_page) &&
+         IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
       {
 #if 0
        if (!(change->events & CH_EVENT_BIT(trigger_event)))
@@ -6894,15 +6981,12 @@ static boolean CheckTriggeredElementSideChange(int lx, int ly,
   return TRUE;
 }
 
-static boolean CheckTriggeredElementChange(int lx, int ly, int trigger_element,
-                                          int trigger_event)
-{
-  return CheckTriggeredElementSideChange(lx, ly, trigger_element, CH_SIDE_ANY,
-                                        trigger_event);
-}
-
-static boolean CheckElementSideChange(int x, int y, int element, int side,
-                                     int trigger_event, int page)
+static boolean CheckElementChangeExt(int x, int y,
+                                    int element,
+                                    int trigger_event,
+                                    int trigger_player,
+                                    int trigger_side,
+                                    int trigger_page)
 {
   if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
     return FALSE;
@@ -6914,7 +6998,7 @@ static boolean CheckElementSideChange(int x, int y, int element, int side,
   }
 
 #if 1
-  if (page < 0)
+  if (trigger_page < 0)
   {
     boolean change_element = FALSE;
     int i;
@@ -6925,10 +7009,11 @@ static boolean CheckElementSideChange(int x, int y, int element, int side,
 
       if (change->can_change &&
          change->events & CH_EVENT_BIT(trigger_event) &&
-         change->sides & side)
+         change->trigger_side & trigger_side &&
+         change->trigger_player & trigger_player)
       {
        change_element = TRUE;
-       page = i;
+       trigger_page = i;
 
        break;
       }
@@ -6942,25 +7027,20 @@ static boolean CheckElementSideChange(int x, int y, int element, int side,
 
   /* !!! this check misses pages with same event, but different side !!! */
 
-  if (page < 0)
-    page = element_info[element].event_page_nr[trigger_event];
+  if (trigger_page < 0)
+    trigger_page = element_info[element].event_page_nr[trigger_event];
 
-  if (!(element_info[element].change_page[page].sides & side))
+  if (!(element_info[element].change_page[trigger_page].trigger_side & trigger_side))
     return FALSE;
 #endif
 
   ChangeDelay[x][y] = 1;
   ChangeEvent[x][y] = CH_EVENT_BIT(trigger_event);
-  ChangeElement(x, y, page);
+  ChangeElement(x, y, trigger_page);
 
   return TRUE;
 }
 
-static boolean CheckElementChange(int x, int y, int element, int trigger_event)
-{
-  return CheckElementSideChange(x, y, element, CH_SIDE_ANY, trigger_event, -1);
-}
-
 static void PlayPlayerSound(struct PlayerInfo *player)
 {
   int jx = player->jx, jy = player->jy;
@@ -7163,6 +7243,11 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action)
     printf("::: player %d acts [%d]\n", player->index_nr, FrameCounter);
 #endif
 
+#if 0
+    /* !!! TEST !!! */
+    if (player->MovPos == 0)
+      CheckGravityMovement(player);
+#endif
     if (button1)
       snapped = SnapField(player, dx, dy);
     else
@@ -7200,7 +7285,7 @@ static byte PlayerActions(struct PlayerInfo *player, byte player_action)
 
     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
     SnapField(player, 0, 0);
-    CheckGravityMovement(player);
+    CheckGravityMovementWhenNotMoving(player);
 
     if (player->MovPos == 0)
       SetPlayerWaiting(player, TRUE);
@@ -7281,7 +7366,7 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action)
 
     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
     SnapField(player, 0, 0);
-    CheckGravityMovement(player);
+    CheckGravityMovementWhenNotMoving(player);
 
     if (player->MovPos == 0)
       InitPlayerGfxAnimation(player, ACTION_DEFAULT, player->MovDir);
@@ -7361,6 +7446,12 @@ void GameActions()
 
   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
 
+#if 1
+  if (recorded_player_action != NULL)
+    for (i = 0; i < MAX_PLAYERS; i++)
+      stored_player[i].action = recorded_player_action[i];
+#endif
+
   for (i = 0; i < MAX_PLAYERS; i++)
   {
     summarized_player_action |= stored_player[i].action;
@@ -7381,11 +7472,43 @@ void GameActions()
   {
     int actual_player_action = stored_player[i].effective_action;
 
+#if 1
+    /* !!! TEST !!! */
+    if (stored_player[i].MovPos == 0)
+      CheckGravityMovement(&stored_player[i]);
+#endif
+
+#if 1
+    /* overwrite programmed action with tape action */
     if (stored_player[i].programmed_action)
       actual_player_action = stored_player[i].programmed_action;
+#endif
 
     if (recorded_player_action)
+    {
+#if 0
+      if (stored_player[i].programmed_action &&
+         stored_player[i].programmed_action != recorded_player_action[i])
+       printf("::: %d: %d <-> %d\n", i,
+              stored_player[i].programmed_action, recorded_player_action[i]);
+#endif
+
+#if 0
       actual_player_action = recorded_player_action[i];
+#endif
+    }
+
+#if 0
+    /* overwrite tape action with programmed action */
+    if (stored_player[i].programmed_action)
+      actual_player_action = stored_player[i].programmed_action;
+#endif
+
+#if 0
+    if (i == 0)
+      printf("::: action: %d: %x [%d]\n",
+            stored_player[i].MovPos, actual_player_action, FrameCounter);
+#endif
 
     tape_action[i] = PlayerActions(&stored_player[i], actual_player_action);
 
@@ -7819,39 +7942,44 @@ void GameActions()
   if (TimeFrames >= FRAMES_PER_SECOND)
   {
     TimeFrames = 0;
-    TimePlayed++;
+    TapeTime++;
 
-    for (i = 0; i < MAX_PLAYERS; i++)
+    if (!level.use_step_counter)
     {
-      struct PlayerInfo *player = &stored_player[i];
+      TimePlayed++;
 
-      if (SHIELD_ON(player))
+      for (i = 0; i < MAX_PLAYERS; i++)
       {
-       player->shield_normal_time_left--;
+       struct PlayerInfo *player = &stored_player[i];
 
-       if (player->shield_deadly_time_left > 0)
-         player->shield_deadly_time_left--;
-      }
-    }
+       if (SHIELD_ON(player))
+       {
+         player->shield_normal_time_left--;
 
-    if (tape.recording || tape.playing)
-      DrawVideoDisplay(VIDEO_STATE_TIME_ON, TimePlayed);
+         if (player->shield_deadly_time_left > 0)
+           player->shield_deadly_time_left--;
+       }
+      }
 
-    if (TimeLeft > 0)
-    {
-      TimeLeft--;
+      if (TimeLeft > 0)
+      {
+       TimeLeft--;
 
-      if (TimeLeft <= 10 && setup.time_limit)
-       PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
+       if (TimeLeft <= 10 && setup.time_limit)
+         PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
 
-      DrawGameValue_Time(TimeLeft);
+       DrawGameValue_Time(TimeLeft);
 
-      if (!TimeLeft && setup.time_limit)
-       for (i = 0; i < MAX_PLAYERS; i++)
-         KillHero(&stored_player[i]);
+       if (!TimeLeft && setup.time_limit)
+         for (i = 0; i < MAX_PLAYERS; i++)
+           KillHero(&stored_player[i]);
+      }
+      else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
+       DrawGameValue_Time(TimePlayed);
     }
-    else if (level.time == 0 && !AllPlayersGone) /* level without time limit */
-      DrawGameValue_Time(TimePlayed);
+
+    if (tape.recording || tape.playing)
+      DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
   }
 
   DrawAllPlayers();
@@ -8021,20 +8149,33 @@ static void CheckGravityMovement(struct PlayerInfo *player)
 {
   if (game.gravity && !player->programmed_action)
   {
-    int move_dir_vertical = player->action & (MV_UP | MV_DOWN);
-    int move_dir_horizontal = player->action & (MV_LEFT | MV_RIGHT);
+    int move_dir_horizontal = player->action & MV_HORIZONTAL;
+    int move_dir_vertical   = player->action & MV_VERTICAL;
     int move_dir =
-      (player->last_move_dir & (MV_LEFT | MV_RIGHT) ?
+      (player->last_move_dir & MV_HORIZONTAL ?
        (move_dir_vertical ? move_dir_vertical : move_dir_horizontal) :
        (move_dir_horizontal ? move_dir_horizontal : move_dir_vertical));
     int jx = player->jx, jy = player->jy;
     int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
     int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
     int new_jx = jx + dx, new_jy = jy + dy;
-    boolean field_under_player_is_free =
-      (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
+    boolean player_is_snapping = player->action & JOY_BUTTON_1;
+#if 1
+    boolean player_can_fall_down =
+      (IN_LEV_FIELD(jx, jy + 1) &&
+       (IS_FREE(jx, jy + 1) ||
+       (Feld[jx][jy + 1] == EL_ACID && level.player_can_fall_into_acid)));
+#else
+    boolean player_can_fall_down =
+      (IN_LEV_FIELD(jx, jy + 1) &&
+       (IS_FREE(jx, jy + 1)));
+#endif
     boolean player_is_moving_to_valid_field =
-      (IN_LEV_FIELD(new_jx, new_jy) &&
+      (
+#if 1
+       !player_is_snapping &&
+#endif
+       IN_LEV_FIELD(new_jx, new_jy) &&
        (Feld[new_jx][new_jy] == EL_SP_BASE ||
        Feld[new_jx][new_jy] == EL_SAND ||
        (IS_SP_PORT(Feld[new_jx][new_jy]) &&
@@ -8046,9 +8187,45 @@ static void CheckGravityMovement(struct PlayerInfo *player)
        (IS_WALKABLE(Feld[jx][jy]) &&
        !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
 
-    if (field_under_player_is_free &&
+#if 0
+    printf("::: checking gravity NOW [%d, %d, %d] [%d] ...\n",
+          player_can_fall_down,
+          player_is_standing_on_valid_field,
+          player_is_moving_to_valid_field,
+          (player_is_moving_to_valid_field ? Feld[new_jx][new_jy] : -1));
+#endif
+
+    if (player_can_fall_down &&
        !player_is_standing_on_valid_field &&
        !player_is_moving_to_valid_field)
+    {
+#if 0
+      printf("::: setting programmed_action to MV_DOWN [%d,%d - %d] ...\n",
+            jx, jy, FrameCounter);
+#endif
+
+      player->programmed_action = MV_DOWN;
+    }
+  }
+}
+
+static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
+{
+#if 1
+  return CheckGravityMovement(player);
+#endif
+
+  if (game.gravity && !player->programmed_action)
+  {
+    int jx = player->jx, jy = player->jy;
+    boolean field_under_player_is_free =
+      (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
+    boolean player_is_standing_on_valid_field =
+      (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
+       (IS_WALKABLE(Feld[jx][jy]) &&
+       !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
+
+    if (field_under_player_is_free && !player_is_standing_on_valid_field)
       player->programmed_action = MV_DOWN;
   }
 }
@@ -8064,7 +8241,7 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
                          int dx, int dy, int real_dx, int real_dy)
 {
 #if 0
-  static int change_sides[4][2] =
+  static int trigger_sides[4][2] =
   {
     /* enter side        leave side */
     { CH_SIDE_RIGHT,   CH_SIDE_LEFT    },      /* moving left  */
@@ -8076,8 +8253,8 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
                        dx == +1 ? MV_RIGHT :
                        dy == -1 ? MV_UP :
                        dy == +1 ? MV_DOWN : MV_NO_MOVING);
-  int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
-  int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
+  int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
+  int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
 #endif
   int jx = player->jx, jy = player->jy;
   int new_jx = jx + dx, new_jy = jy + dy;
@@ -8150,18 +8327,17 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
 #if 0
   if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
   {
-    CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy], leave_side,
-                                   CE_OTHER_GETS_LEFT);
-    CheckElementSideChange(jx, jy, Feld[jx][jy], leave_side,
-                          CE_LEFT_BY_PLAYER, -1);
+    CheckTriggeredElementChangeSide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
+                                   leave_side);
+    CheckElementChangeSide(jx, jy, Feld[jx][jy], CE_LEFT_BY_PLAYER,leave_side);
   }
 
   if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
   {
-    CheckTriggeredElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy],
-                                   enter_side, CE_OTHER_GETS_ENTERED);
-    CheckElementSideChange(new_jx, new_jy, Feld[new_jx][new_jy], enter_side,
-                          CE_ENTERED_BY_PLAYER, -1);
+    CheckTriggeredElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
+                                   CE_OTHER_GETS_ENTERED, enter_side);
+    CheckElementChangeSide(new_jx, new_jy, Feld[new_jx][new_jy],
+                          CE_ENTERED_BY_PLAYER, enter_side);
   }
 #endif
 
@@ -8201,9 +8377,16 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
       !tape.playing)
     return FALSE;
 #else
+
+#if 1
+  if (!FrameReached(&player->move_delay, player->move_delay_value))
+    return FALSE;
+#else
   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
       !(tape.playing && tape.file_version < FILE_VERSION_2_0))
     return FALSE;
+#endif
+
 #endif
 
   /* remove the last programmed player action */
@@ -8236,7 +8419,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
     player->move_delay_value = original_move_delay_value;
   }
 
-  if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
+  if (player->last_move_dir & MV_HORIZONTAL)
   {
     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
@@ -8354,7 +8537,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
 
 #if 1
     {
-      static int change_sides[4][2] =
+      static int trigger_sides[4][2] =
       {
        /* enter side           leave side */
        { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
@@ -8363,24 +8546,27 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
        { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
       };
       int move_direction = player->MovDir;
-      int enter_side = change_sides[MV_DIR_BIT(move_direction)][0];
-      int leave_side = change_sides[MV_DIR_BIT(move_direction)][1];
+      int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
+      int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
 
 #if 1
       if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
       {
-       CheckTriggeredElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
-                                       leave_side, CE_OTHER_GETS_LEFT);
-       CheckElementSideChange(old_jx, old_jy, Feld[old_jx][old_jy],
-                              leave_side, CE_LEFT_BY_PLAYER, -1);
+       CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
+                                         CE_OTHER_GETS_LEFT,
+                                         player->index_bit, leave_side);
+       CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
+                                CE_LEFT_BY_PLAYER,
+                                player->index_bit, leave_side);
       }
 
       if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
       {
-       CheckTriggeredElementSideChange(jx, jy, Feld[jx][jy],
-                                       enter_side, CE_OTHER_GETS_ENTERED);
-       CheckElementSideChange(jx, jy, Feld[jx][jy],
-                              enter_side, CE_ENTERED_BY_PLAYER, -1);
+       CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
+                                         CE_OTHER_GETS_ENTERED,
+                                         player->index_bit, enter_side);
+       CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
+                                player->index_bit, enter_side);
       }
 #endif
 
@@ -8391,7 +8577,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
   }
   else
   {
-    CheckGravityMovement(player);
+    CheckGravityMovementWhenNotMoving(player);
 
     /*
     player->last_move_dir = MV_NO_MOVING;
@@ -8498,6 +8684,47 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
        player->LevelSolved = player->GameOver = TRUE;
     }
 
+#if 0
+    /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
+    {
+      static int trigger_sides[4][2] =
+      {
+       /* enter side           leave side */
+       { CH_SIDE_RIGHT,        CH_SIDE_LEFT    },      /* moving left  */
+       { CH_SIDE_LEFT,         CH_SIDE_RIGHT   },      /* moving right */
+       { CH_SIDE_BOTTOM,       CH_SIDE_TOP     },      /* moving up    */
+       { CH_SIDE_TOP,          CH_SIDE_BOTTOM  }       /* moving down  */
+      };
+      int move_direction = player->MovDir;
+      int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
+      int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
+      int old_jx = last_jx;
+      int old_jy = last_jy;
+
+#if 1
+      if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
+      {
+       CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
+                                         CE_OTHER_GETS_LEFT,
+                                         player->index_bit, leave_side);
+       CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
+                                CE_LEFT_BY_PLAYER,
+                                player->index_bit, leave_side);
+      }
+
+      if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
+      {
+       CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
+                                         CE_OTHER_GETS_ENTERED,
+                                         player->index_bit, enter_side);
+       CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
+                                player->index_bit, enter_side);
+      }
+#endif
+
+    }
+#endif
+
     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
     {
       TestIfHeroTouchesBadThing(jx, jy);
@@ -8510,6 +8737,42 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
        RemoveHero(player);
     }
 
+    if (level.use_step_counter)
+    {
+      int i;
+
+      TimePlayed++;
+
+      for (i = 0; i < MAX_PLAYERS; i++)
+      {
+       struct PlayerInfo *player = &stored_player[i];
+
+       if (SHIELD_ON(player))
+       {
+         player->shield_normal_time_left--;
+
+         if (player->shield_deadly_time_left > 0)
+           player->shield_deadly_time_left--;
+       }
+      }
+
+      if (TimeLeft > 0)
+      {
+       TimeLeft--;
+
+       if (TimeLeft <= 10 && setup.time_limit)
+         PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
+
+       DrawGameValue_Time(TimeLeft);
+
+       if (!TimeLeft && setup.time_limit)
+         for (i = 0; i < MAX_PLAYERS; i++)
+           KillHero(&stored_player[i]);
+      }
+      else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
+       DrawGameValue_Time(TimePlayed);
+    }
+
     if (tape.single_step && tape.recording && !tape.pausing &&
        !player->programmed_action)
       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
@@ -8553,7 +8816,7 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
     { +1, 0 },
     { 0, +1 }
   };
-  static int change_sides[4][2] =
+  static int trigger_sides[4][2] =
   {
     /* center side       border side */
     { CH_SIDE_TOP,     CH_SIDE_BOTTOM  },      /* check top    */
@@ -8575,8 +8838,8 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
   {
     int xx = x + xy[i][0];
     int yy = y + xy[i][1];
-    int center_side = change_sides[i][0];
-    int border_side = change_sides[i][1];
+    int center_side = trigger_sides[i][0];
+    int border_side = trigger_sides[i][1];
     int border_element;
 
     if (!IN_LEV_FIELD(xx, yy))
@@ -8584,6 +8847,8 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
 
     if (IS_PLAYER(x, y))
     {
+      struct PlayerInfo *player = PLAYERINFO(x, y);
+
       if (game.engine_version < VERSION_IDENT(3,0,7,0))
        border_element = Feld[xx][yy];          /* may be moving! */
       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
@@ -8593,25 +8858,27 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
       else
        continue;               /* center and border element do not touch */
 
-      CheckTriggeredElementSideChange(xx, yy, border_element, border_side,
-                                     CE_OTHER_GETS_TOUCHED);
-      CheckElementSideChange(xx, yy, border_element, border_side,
-                            CE_TOUCHED_BY_PLAYER, -1);
+      CheckTriggeredElementChangePlayer(xx, yy, border_element,
+                                       CE_OTHER_GETS_TOUCHED,
+                                       player->index_bit, border_side);
+      CheckElementChangePlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
+                              player->index_bit, border_side);
     }
     else if (IS_PLAYER(xx, yy))
     {
+      struct PlayerInfo *player = PLAYERINFO(xx, yy);
+
       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
       {
-       struct PlayerInfo *player = PLAYERINFO(xx, yy);
-
        if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
          continue;             /* center and border element do not touch */
       }
 
-      CheckTriggeredElementSideChange(x, y, center_element, center_side,
-                                     CE_OTHER_GETS_TOUCHED);
-      CheckElementSideChange(x, y, center_element, center_side,
-                            CE_TOUCHED_BY_PLAYER, -1);
+      CheckTriggeredElementChangePlayer(x, y, center_element,
+                                       CE_OTHER_GETS_TOUCHED,
+                                       player->index_bit, center_side);
+      CheckElementChangePlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
+                              player->index_bit, center_side);
 
       break;
     }
@@ -8627,7 +8894,7 @@ void TestIfElementTouchesCustomElement(int x, int y)
     { +1, 0 },
     { 0, +1 }
   };
-  static int change_sides[4][2] =
+  static int trigger_sides[4][2] =
   {
     /* center side     border side */
     { CH_SIDE_TOP,     CH_SIDE_BOTTOM  },      /* check top    */
@@ -8651,8 +8918,8 @@ void TestIfElementTouchesCustomElement(int x, int y)
   {
     int xx = x + xy[i][0];
     int yy = y + xy[i][1];
-    int center_side = change_sides[i][0];
-    int border_side = change_sides[i][1];
+    int center_side = trigger_sides[i][0];
+    int border_side = trigger_sides[i][1];
     int border_element;
 
     if (!IN_LEV_FIELD(xx, yy))
@@ -8679,7 +8946,7 @@ void TestIfElementTouchesCustomElement(int x, int y)
 
        if (change->can_change &&
            change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
-           change->sides & border_side &&
+           change->trigger_side & border_side &&
 #if 1
            IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
 #else
@@ -8706,7 +8973,7 @@ void TestIfElementTouchesCustomElement(int x, int y)
 
        if (change->can_change &&
            change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
-           change->sides & center_side &&
+           change->trigger_side & center_side &&
 #if 1
            IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
 #else
@@ -8714,8 +8981,8 @@ void TestIfElementTouchesCustomElement(int x, int y)
 #endif
            )
        {
-         CheckElementSideChange(xx, yy, border_element, CH_SIDE_ANY,
-                                CE_OTHER_IS_TOUCHING, j);
+         CheckElementChangePage(xx, yy, border_element, CE_OTHER_IS_TOUCHING,
+                                j);
          break;
        }
       }
@@ -8723,8 +8990,8 @@ void TestIfElementTouchesCustomElement(int x, int y)
   }
 
   if (change_center_element)
-    CheckElementSideChange(x, y, center_element, CH_SIDE_ANY,
-                          CE_OTHER_IS_TOUCHING, center_element_change_page);
+    CheckElementChangePage(x, y, center_element, CE_OTHER_IS_TOUCHING,
+                          center_element_change_page);
 }
 
 void TestIfElementHitsCustomElement(int x, int y, int direction)
@@ -8749,8 +9016,8 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
     return;
 #endif
 
-  CheckElementSideChange(x, y, hitting_element,
-                        direction, CE_HITTING_SOMETHING, -1);
+  CheckElementChangeSide(x, y, hitting_element, CE_HITTING_SOMETHING,
+                        direction);
 
   if (IN_LEV_FIELD(hitx, hity))
   {
@@ -8770,8 +9037,8 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
     {
       int i;
 
-      CheckElementSideChange(hitx, hity, touched_element,
-                            opposite_direction, CE_HIT_BY_SOMETHING, -1);
+      CheckElementChangeSide(hitx, hity, touched_element, CE_HIT_BY_SOMETHING,
+                            opposite_direction);
 
       if (IS_CUSTOM_ELEMENT(hitting_element) &&
          HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
@@ -8783,7 +9050,7 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
 
          if (change->can_change &&
              change->events & CH_EVENT_BIT(CE_OTHER_IS_HITTING) &&
-             change->sides & touched_side &&
+             change->trigger_side & touched_side &&
          
 #if 1
              IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
@@ -8792,8 +9059,8 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
 #endif
              )
          {
-           CheckElementSideChange(x, y, hitting_element,
-                                  CH_SIDE_ANY, CE_OTHER_IS_HITTING, i);
+           CheckElementChangePage(x, y, hitting_element, CE_OTHER_IS_HITTING,
+                                  i);
            break;
          }
        }
@@ -8809,7 +9076,7 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
 
          if (change->can_change &&
              change->events & CH_EVENT_BIT(CE_OTHER_GETS_HIT) &&
-             change->sides & hitting_side &&
+             change->trigger_side & hitting_side &&
 #if 1
              IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
 #else
@@ -8817,8 +9084,8 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
 #endif
              )
          {
-           CheckElementSideChange(hitx, hity, touched_element,
-                                  CH_SIDE_ANY, CE_OTHER_GETS_HIT, i);
+           CheckElementChangePage(hitx, hity, touched_element,
+                                  CE_OTHER_GETS_HIT, i);
            break;
          }
        }
@@ -8827,6 +9094,108 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
   }
 }
 
+#if 0
+void TestIfElementSmashesCustomElement(int x, int y, int direction)
+{
+  int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
+  int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
+  int hitx = x + dx, hity = y + dy;
+  int hitting_element = Feld[x][y];
+#if 0
+  boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
+                       !IS_FREE(hitx, hity) &&
+                       (!IS_MOVING(hitx, hity) ||
+                        MovDir[hitx][hity] != direction ||
+                        ABS(MovPos[hitx][hity]) <= TILEY / 2));
+#endif
+
+  if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
+    return;
+
+#if 0
+  if (IN_LEV_FIELD(hitx, hity) && !object_hit)
+    return;
+#endif
+
+  CheckElementChangeSide(x, y, hitting_element, EP_CAN_SMASH_EVERYTHING,
+                        direction);
+
+  if (IN_LEV_FIELD(hitx, hity))
+  {
+    int opposite_direction = MV_DIR_OPPOSITE(direction);
+    int hitting_side = direction;
+    int touched_side = opposite_direction;
+    int touched_element = MovingOrBlocked2Element(hitx, hity);
+#if 1
+    boolean object_hit = (!IS_MOVING(hitx, hity) ||
+                         MovDir[hitx][hity] != direction ||
+                         ABS(MovPos[hitx][hity]) <= TILEY / 2);
+
+    object_hit = TRUE;
+#endif
+
+    if (object_hit)
+    {
+      int i;
+
+      CheckElementChangeSide(hitx, hity, touched_element,
+                            CE_SMASHED_BY_SOMETHING, opposite_direction);
+
+      if (IS_CUSTOM_ELEMENT(hitting_element) &&
+         HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
+      {
+       for (i = 0; i < element_info[hitting_element].num_change_pages; i++)
+       {
+         struct ElementChangeInfo *change =
+           &element_info[hitting_element].change_page[i];
+
+         if (change->can_change &&
+             change->events & CH_EVENT_BIT(CE_OTHER_IS_SMASHING) &&
+             change->trigger_side & touched_side &&
+         
+#if 1
+             IS_EQUAL_OR_IN_GROUP(touched_element, change->trigger_element)
+#else
+             change->trigger_element == touched_element
+#endif
+             )
+         {
+           CheckElementChangePage(x, y, hitting_element, CE_OTHER_IS_SMASHING,
+                                  i);
+           break;
+         }
+       }
+      }
+
+      if (IS_CUSTOM_ELEMENT(touched_element) &&
+         HAS_ANY_CHANGE_EVENT(touched_element, CE_OTHER_GETS_SMASHED))
+      {
+       for (i = 0; i < element_info[touched_element].num_change_pages; i++)
+       {
+         struct ElementChangeInfo *change =
+           &element_info[touched_element].change_page[i];
+
+         if (change->can_change &&
+             change->events & CH_EVENT_BIT(CE_OTHER_GETS_SMASHED) &&
+             change->trigger_side & hitting_side &&
+#if 1
+             IS_EQUAL_OR_IN_GROUP(hitting_element, change->trigger_element)
+#else
+             change->trigger_element == hitting_element
+#endif
+             )
+         {
+           CheckElementChangePage(hitx, hity, touched_element,
+                                  CE_OTHER_GETS_SMASHED, i);
+           break;
+         }
+       }
+      }
+    }
+  }
+}
+#endif
+
 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
 {
   int i, kill_x = -1, kill_y = -1;
@@ -9158,7 +9527,7 @@ int DigField(struct PlayerInfo *player,
             int oldx, int oldy, int x, int y,
             int real_dx, int real_dy, int mode)
 {
-  static int change_sides[4] =
+  static int trigger_sides[4] =
   {
     CH_SIDE_RIGHT,     /* moving left  */
     CH_SIDE_LEFT,      /* moving right */
@@ -9176,7 +9545,7 @@ int DigField(struct PlayerInfo *player,
                        dy == -1 ? MV_UP :
                        dy == +1 ? MV_DOWN : MV_NO_MOVING);
   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
-  int dig_side = change_sides[MV_DIR_BIT(move_direction)];
+  int dig_side = trigger_sides[MV_DIR_BIT(move_direction)];
   int old_element = Feld[jx][jy];
   int element;
 
@@ -9318,6 +9687,10 @@ int DigField(struct PlayerInfo *player,
       DOUBLE_PLAYER_SPEED(player);
 #endif
 
+#if 0
+      printf("::: passing port %d,%d [%d]\n", x, y, FrameCounter);
+#endif
+
       PlayLevelSound(x, y, SND_CLASS_SP_PORT_PASSING);
       break;
 
@@ -9465,7 +9838,8 @@ int DigField(struct PlayerInfo *player,
 
        PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
 
-       CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_DIGGED);
+       CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_DIGGED,
+                                         player->index_bit, CH_SIDE_ANY);
 
 #if 1
        if (mode == DF_SNAP)
@@ -9541,9 +9915,12 @@ int DigField(struct PlayerInfo *player,
        {
          int i;
 
-         for (i = 0; i < element_info[element].collect_count; i++)
-           if (player->inventory_size < MAX_INVENTORY_SIZE)
-             player->inventory_element[player->inventory_size++] = element;
+         if (element_info[element].collect_count == 0)
+           player->inventory_infinite_element = element;
+         else
+           for (i = 0; i < element_info[element].collect_count; i++)
+             if (player->inventory_size < MAX_INVENTORY_SIZE)
+               player->inventory_element[player->inventory_size++] = element;
 
          DrawGameValue_Dynamite(local_player->inventory_size);
        }
@@ -9560,7 +9937,9 @@ int DigField(struct PlayerInfo *player,
        RaiseScoreElement(element);
        PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
 
-       CheckTriggeredElementChange(x, y, element, CE_OTHER_GETS_COLLECTED);
+       CheckTriggeredElementChangePlayer(x, y, element,
+                                         CE_OTHER_GETS_COLLECTED,
+                                         player->index_bit, CH_SIDE_ANY);
 
 #if 1
        if (mode == DF_SNAP)
@@ -9716,10 +10095,10 @@ int DigField(struct PlayerInfo *player,
        else
          player->push_delay_value = -1;        /* get new value later */
 
-       CheckTriggeredElementSideChange(x, y, element, dig_side,
-                                       CE_OTHER_GETS_PUSHED);
-       CheckElementSideChange(x, y, element, dig_side,
-                              CE_PUSHED_BY_PLAYER, -1);
+       CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PUSHED,
+                                         player->index_bit, dig_side);
+       CheckElementChangePlayer(x, y, element, CE_PUSHED_BY_PLAYER,
+                                player->index_bit, dig_side);
 
        break;
       }
@@ -9823,15 +10202,17 @@ int DigField(struct PlayerInfo *player,
          player->switch_x = x;
          player->switch_y = y;
 
-         CheckTriggeredElementSideChange(x, y, element, dig_side,
-                                         CE_OTHER_IS_SWITCHING);
-         CheckElementSideChange(x, y, element, dig_side, CE_SWITCHED, -1);
+         CheckTriggeredElementChangePlayer(x, y, element,
+                                           CE_OTHER_IS_SWITCHING,
+                                           player->index_bit, dig_side);
+         CheckElementChangePlayer(x, y, element, CE_SWITCHED,
+                                  player->index_bit, dig_side);
        }
 
-       CheckTriggeredElementSideChange(x, y, element, dig_side,
-                                       CE_OTHER_GETS_PRESSED);
-       CheckElementSideChange(x, y, element, dig_side,
-                              CE_PRESSED_BY_PLAYER, -1);
+       CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_PRESSED,
+                                         player->index_bit, dig_side);
+       CheckElementChangePlayer(x, y, element, CE_PRESSED_BY_PLAYER,
+                                player->index_bit, dig_side);
       }
 
       return MF_NO_ACTION;
@@ -9854,8 +10235,13 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
                        dy == -1 ? MV_UP :
                        dy == +1 ? MV_DOWN : MV_NO_MOVING);
 
+#if 0
+  if (player->MovPos)
+    return FALSE;
+#else
   if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0,0))
     return FALSE;
+#endif
 
   if (!player->active || !IN_LEV_FIELD(x, y))
     return FALSE;
@@ -9920,25 +10306,43 @@ boolean DropElement(struct PlayerInfo *player)
 {
   int jx = player->jx, jy = player->jy;
   int old_element = Feld[jx][jy];
-  int new_element;
+  int new_element = (player->inventory_size > 0 ?
+                    player->inventory_element[player->inventory_size - 1] :
+                    player->inventory_infinite_element != EL_UNDEFINED ?
+                    player->inventory_infinite_element :
+                    player->dynabombs_left > 0 ?
+                    EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
+                    EL_UNDEFINED);
 
   /* check if player is active, not moving and ready to drop */
   if (!player->active || player->MovPos || player->drop_delay > 0)
     return FALSE;
 
   /* check if player has anything that can be dropped */
-  if (player->inventory_size == 0 && player->dynabombs_left == 0)
+#if 1
+  if (new_element == EL_UNDEFINED)
+    return FALSE;
+#else
+  if (player->inventory_size == 0 &&
+      player->inventory_infinite_element == EL_UNDEFINED &&
+      player->dynabombs_left == 0)
     return FALSE;
+#endif
 
   /* check if anything can be dropped at the current position */
   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
     return FALSE;
 
   /* collected custom elements can only be dropped on empty fields */
+#if 1
+  if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
+    return FALSE;
+#else
   if (player->inventory_size > 0 &&
       IS_CUSTOM_ELEMENT(player->inventory_element[player->inventory_size - 1])
       && old_element != EL_EMPTY)
     return FALSE;
+#endif
 
   if (old_element != EL_EMPTY)
     Back[jx][jy] = old_element;                /* store old element on this field */
@@ -9946,19 +10350,26 @@ boolean DropElement(struct PlayerInfo *player)
   ResetGfxAnimation(jx, jy);
   ResetRandomAnimationValue(jx, jy);
 
-  if (player->inventory_size > 0)
+  if (player->inventory_size > 0 ||
+      player->inventory_infinite_element != EL_UNDEFINED)
   {
-    player->inventory_size--;
-    new_element = player->inventory_element[player->inventory_size];
+    if (player->inventory_size > 0)
+    {
+      player->inventory_size--;
+
+#if 0
+      new_element = player->inventory_element[player->inventory_size];
+#endif
 
-    if (new_element == EL_DYNAMITE)
-      new_element = EL_DYNAMITE_ACTIVE;
-    else if (new_element == EL_SP_DISK_RED)
-      new_element = EL_SP_DISK_RED_ACTIVE;
+      DrawGameValue_Dynamite(local_player->inventory_size);
 
-    Feld[jx][jy] = new_element;
+      if (new_element == EL_DYNAMITE)
+       new_element = EL_DYNAMITE_ACTIVE;
+      else if (new_element == EL_SP_DISK_RED)
+       new_element = EL_SP_DISK_RED_ACTIVE;
+    }
 
-    DrawGameValue_Dynamite(local_player->inventory_size);
+    Feld[jx][jy] = new_element;
 
     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
       DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
@@ -9970,15 +10381,21 @@ boolean DropElement(struct PlayerInfo *player)
     Changed[jx][jy] = 0;               /* allow another change */
 #endif
 
-    CheckTriggeredElementChange(jx, jy, new_element, CE_OTHER_GETS_DROPPED);
-    CheckElementChange(jx, jy, new_element, CE_DROPPED_BY_PLAYER);
+    CheckTriggeredElementChangePlayer(jx, jy, new_element,
+                                     CE_OTHER_GETS_DROPPED,
+                                     player->index_bit, CH_SIDE_ANY);
+    CheckElementChangePlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER,
+                            player->index_bit, CH_SIDE_ANY);
 
     TestIfElementTouchesCustomElement(jx, jy);
   }
   else         /* player is dropping a dyna bomb */
   {
     player->dynabombs_left--;
+
+#if 0
     new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
+#endif
 
     Feld[jx][jy] = new_element;
 
@@ -10036,8 +10453,8 @@ boolean DropElement(struct PlayerInfo *player)
 #if 1
       TestIfElementHitsCustomElement(jx, jy, direction);
 #else
-      CheckElementSideChange(jx, jy, new_element,
-                            direction, CE_HITTING_SOMETHING, -1);
+      CheckElementChangeSide(jx, jy, new_element, CE_HITTING_SOMETHING,
+                            direction);
 #endif
     }
 
@@ -10271,7 +10688,25 @@ void RequestQuitGame(boolean ask_if_really_quit)
   }
   else
   {
+
+#if 1
+    if (tape.playing && tape.index_search)
+    {
+      SetDrawDeactivationMask(REDRAW_NONE);
+      audio.sound_deactivated = FALSE;
+    }
+#endif
+
     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
+
+#if 1
+    if (tape.playing && tape.index_search)
+    {
+      SetDrawDeactivationMask(REDRAW_FIELD);
+      audio.sound_deactivated = TRUE;
+    }
+#endif
+
   }
 }