rnd-20040411-1-src
[rocksndiamonds.git] / src / game.c
index b1254556955778c3d660d44bc45a19d023a1c171..59ced41619717c82f6fc88b6e1a34d074651ccb7 100644 (file)
@@ -96,6 +96,9 @@
 /* values for other actions */
 #define MOVE_STEPSIZE_NORMAL   (TILEX / MOVE_DELAY_NORMAL_SPEED)
 
+#define GET_DX_FROM_DIR(d)     ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
+#define GET_DY_FROM_DIR(d)     ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
+
 #define        INIT_GFX_RANDOM()       (SimpleRND(1000000))
 
 #define GET_NEW_PUSH_DELAY(e)  (   (element_info[e].push_delay_fixed) + \
@@ -313,22 +316,22 @@ static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
 #define CheckTriggeredElementChange(x, y, e, ev)                       \
        CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY,      \
                                       CH_SIDE_ANY, -1)
-#define CheckTriggeredElementChangePlayer(x, y, e, ev, p, s)           \
+#define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)         \
        CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
-#define CheckTriggeredElementChangeSide(x, y, e, ev, s)                        \
+#define CheckTriggeredElementChangeBySide(x, y, e, ev, s)              \
        CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
-#define CheckTriggeredElementChangePage(x, y, e, ev, p)                        \
+#define CheckTriggeredElementChangeByPage(x, y, e, ev, p)              \
        CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY,      \
                                       CH_SIDE_ANY, p)
 
 static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
 #define CheckElementChange(x, y, e, te, ev)                            \
        CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
-#define CheckElementChangePlayer(x, y, e, ev, p, s)                    \
+#define CheckElementChangeByPlayer(x, y, e, ev, p, s)                  \
        CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, CH_PAGE_ANY)
-#define CheckElementChangeSide(x, y, e, te, ev, s)                     \
+#define CheckElementChangeBySide(x, y, e, te, ev, s)                   \
        CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, CH_PAGE_ANY)
-#define CheckElementChangePage(x, y, e, te, ev, p)                     \
+#define CheckElementChangeByPage(x, y, e, te, ev, p)                   \
        CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
 
 static void PlayLevelSound(int, int, int);
@@ -1633,6 +1636,7 @@ void InitGame()
       AmoebaNr[x][y] = 0;
       WasJustMoving[x][y] = 0;
       WasJustFalling[x][y] = 0;
+      CheckCollision[x][y] = 0;
       Stop[x][y] = FALSE;
       Pushed[x][y] = FALSE;
 
@@ -2518,6 +2522,10 @@ static void RemoveField(int x, int y)
   ChangePage[x][y] = -1;
   Pushed[x][y] = FALSE;
 
+#if 0
+  ExplodeField[x][y] = EX_TYPE_NONE;
+#endif
+
   GfxElement[x][y] = EL_UNDEFINED;
   GfxAction[x][y] = ACTION_DEFAULT;
   GfxDir[x][y] = MV_NO_MOVING;
@@ -3939,6 +3947,7 @@ void Impact(int x, int y)
     return;
   }
 
+  /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
   /* only reset graphic animation if graphic really changes after impact */
   if (impact &&
       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
@@ -3954,6 +3963,8 @@ void Impact(int x, int y)
   }
   else if (impact && element == EL_PEARL)
   {
+    ResetGfxAnimation(x, y);
+
     Feld[x][y] = EL_PEARL_BREAKING;
     PlayLevelSound(x, y, SND_PEARL_BREAKING);
     return;
@@ -4073,6 +4084,8 @@ void Impact(int x, int y)
        }
        else if (smashed == EL_PEARL)
        {
+         ResetGfxAnimation(x, y);
+
          Feld[x][y + 1] = EL_PEARL_BREAKING;
          PlayLevelSound(x, y, SND_PEARL_BREAKING);
          return;
@@ -4105,10 +4118,18 @@ void Impact(int x, int y)
 
          CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
 
-         CheckTriggeredElementChangeSide(x, y + 1, smashed,
-                                         CE_OTHER_IS_SWITCHING, CH_SIDE_TOP);
-         CheckElementChangeSide(x, y + 1, smashed, element,
-                                CE_SWITCHED, CH_SIDE_TOP);
+#if 1
+         /* !!! TEST ONLY !!! */
+         CheckElementChangeBySide(x, y + 1, smashed, element,
+                                  CE_SWITCHED, CH_SIDE_TOP);
+         CheckTriggeredElementChangeBySide(x, y + 1, smashed,
+                                           CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
+#else
+         CheckTriggeredElementChangeBySide(x, y + 1, smashed,
+                                           CE_OTHER_IS_SWITCHING,CH_SIDE_TOP);
+         CheckElementChangeBySide(x, y + 1, smashed, element,
+                                  CE_SWITCHED, CH_SIDE_TOP);
+#endif
        }
       }
       else
@@ -4488,7 +4509,13 @@ inline static void TurnRoundExt(int x, int y)
       }
     }
 
+#if 1
+    if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
+       (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
+        game.engine_version < VERSION_IDENT(3,1,0,0)))
+#else
     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0)
+#endif
     {
       attr_x = ZX;
       attr_y = ZY;
@@ -5032,12 +5059,16 @@ void StartMoving(int x, int y)
 #endif
     }
 #if 1
-    else if ((game.engine_version < VERSION_IDENT(2,2,0,7) &&
-             CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
-             (Feld[x][y + 1] == EL_BLOCKED)) ||
+    else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
+             CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
+
             (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
              CAN_SMASH(element) && WasJustFalling[x][y] &&
-             (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))))
+             (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
+
+            (game.engine_version < VERSION_IDENT(2,2,0,7) &&
+             CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
+             (Feld[x][y + 1] == EL_BLOCKED)))
 
 #else
 #if 1
@@ -5199,9 +5230,15 @@ void StartMoving(int x, int y)
 #endif
 
 #if 1
+
+#if 1
+    if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
+       CheckCollision[x][y] && IN_LEV_FIELD_AND_NOT_FREE(newx, newy))
+#else
     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
        WasJustMoving[x][y] && IN_LEV_FIELD(newx, newy) &&
        (Feld[newx][newy] == EL_BLOCKED || IS_PLAYER(newx, newy)))
+#endif
     {
 #if 0
       printf("::: element %d '%s' WasJustMoving %d [%d, %d, %d, %d]\n",
@@ -5589,6 +5626,9 @@ void StartMoving(int x, int y)
          DrawLevelField(newx, newy);
        }
 
+       /* if digged element was about to explode, prevent the explosion */
+       ExplodeField[newx][newy] = EX_TYPE_NONE;
+
        PlayLevelSoundAction(x, y, action);
       }
 
@@ -6045,10 +6085,16 @@ void ContinueMoving(int x, int y)
 
   if (!pushed_by_player)
   {
+    int nextx = newx + dx, nexty = newy + dy;
+    boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
+
     WasJustMoving[newx][newy] = 3;
 
     if (CAN_FALL(element) && direction == MV_DOWN)
       WasJustFalling[newx][newy] = 3;
+
+    if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
+      CheckCollision[newx][newy] = 2;
   }
 
   if (DONT_TOUCH(element))     /* object may be nasty to player or others */
@@ -6066,6 +6112,26 @@ void ContinueMoving(int x, int y)
       (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
     Impact(x, newy);
 
+#if 1
+  if (pushed_by_player)
+  {
+    static int trigger_sides[4] =
+    {
+      CH_SIDE_RIGHT,   /* moving left  */
+      CH_SIDE_LEFT,    /* moving right */
+      CH_SIDE_BOTTOM,  /* moving up    */
+      CH_SIDE_TOP,     /* moving down  */
+    };
+    int dig_side = trigger_sides[MV_DIR_BIT(direction)];
+    struct PlayerInfo *player = PLAYERINFO(x, y);
+
+    CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
+                              player->index_bit, dig_side);
+    CheckTriggeredElementChangeByPlayer(newx,newy,element,CE_OTHER_GETS_PUSHED,
+                                       player->index_bit, dig_side);
+  }
+#endif
+
 #if 1
   TestIfElementTouchesCustomElement(x, y);     /* empty or new element */
 #endif
@@ -6086,8 +6152,8 @@ void ContinueMoving(int x, int y)
     int hitting_element = Feld[newx][newy];
 
     /* !!! fix side (direction) orientation here and elsewhere !!! */
-    CheckElementChangeSide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
-                          direction);
+    CheckElementChangeBySide(newx, newy, hitting_element, CE_HITTING_SOMETHING,
+                            direction);
 
 #if 0
     if (IN_LEV_FIELD(nextx, nexty))
@@ -6104,8 +6170,8 @@ void ContinueMoving(int x, int y)
       {
        int i;
 
-       CheckElementChangeSide(nextx, nexty, touched_element,
-                              CE_HIT_BY_SOMETHING, opposite_direction);
+       CheckElementChangeBySide(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))
@@ -6120,8 +6186,8 @@ void ContinueMoving(int x, int y)
                change->trigger_side & touched_side &&
                change->trigger_element == touched_element)
            {
-             CheckElementChangePage(newx, newy, hitting_element,
-                                    touched_element, CE_OTHER_IS_HITTING, i);
+             CheckElementChangeByPage(newx, newy, hitting_element,
+                                      touched_element, CE_OTHER_IS_HITTING,i);
              break;
            }
          }
@@ -6140,8 +6206,8 @@ void ContinueMoving(int x, int y)
                change->trigger_side & hitting_side &&
                change->trigger_element == hitting_element)
            {
-             CheckElementChangePage(nextx, nexty, touched_element,
-                                    hitting_element, CE_OTHER_GETS_HIT, i);
+             CheckElementChangeByPage(nextx, nexty, touched_element,
+                                      hitting_element, CE_OTHER_GETS_HIT, i);
              break;
            }
          }
@@ -6517,7 +6583,11 @@ void AmoebeAbleger(int ax, int ay)
 
     if (newax == ax && neway == ay)            /* amoeba cannot grow */
     {
+#if 1
+      if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
+#else
       if (i == 4 && (!waiting_for_player || game.emulation == EMU_BOULDERDASH))
+#endif
       {
        Feld[ax][ay] = EL_AMOEBA_DEAD;
        DrawLevelField(ax, ay);
@@ -6701,7 +6771,12 @@ static void StopRobotWheel(int x, int y)
 
 static void InitTimegateWheel(int x, int y)
 {
+#if 1
+  ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
+#else
+  /* another brainless, "type style" bug ... :-( */
   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
+#endif
 }
 
 static void RunTimegateWheel(int x, int y)
@@ -7157,7 +7232,7 @@ static boolean ChangeElementNow(int x, int y, int element, int page)
 
   Changed[x][y] |= ChangeEvent[x][y];  /* ignore same changes in this frame */
 
-  CheckTriggeredElementChangePage(x,y, Feld[x][y], CE_OTHER_IS_CHANGING, page);
+  CheckTriggeredElementChangeByPage(x,y,Feld[x][y], CE_OTHER_IS_CHANGING,page);
 
   if (change->explode)
   {
@@ -7176,6 +7251,8 @@ static boolean ChangeElementNow(int x, int y, int element, int page)
     {
       boolean is_empty;
       boolean is_diggable;
+      boolean is_collectible;
+      boolean is_removable;
       boolean is_destructible;
       int ex = x + xx - 1;
       int ey = y + yy - 1;
@@ -7208,14 +7285,24 @@ static boolean ChangeElementNow(int x, int y, int element, int page)
        e = MovingOrBlocked2Element(ex, ey);
 
 #if 1
+
+#if 1
+      is_empty = (IS_FREE(ex, ey) || (IS_FREE_OR_PLAYER(ex, ey) &&
+                                     IS_WALKABLE(content_element)));
+#else
       is_empty = (IS_FREE(ex, ey) || (IS_PLAYER(ex, ey) &&
                                      IS_WALKABLE(content_element)));
+#endif
       is_diggable = (is_empty || IS_DIGGABLE(e));
+      is_collectible = (is_empty || IS_COLLECTIBLE(e));
+      is_removable = (is_diggable || is_collectible);
       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
 
       can_replace[xx][yy] =
-       ((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
-        (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
+       ((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
+        (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
+        (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
+        (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
         (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible));
 
       if (!can_replace[xx][yy])
@@ -8130,6 +8217,8 @@ void GameActions()
       WasJustMoving[x][y]--;
     if (WasJustFalling[x][y] > 0)
       WasJustFalling[x][y]--;
+    if (CheckCollision[x][y] > 0)
+      CheckCollision[x][y]--;
 
     GfxFrame[x][y]++;
 
@@ -8985,17 +9074,17 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
 #if 0
   if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
   {
-    CheckTriggeredElementChangeSide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
-                                   leave_side);
-    CheckElementChangeSide(jx, jy, Feld[jx][jy], CE_LEFT_BY_PLAYER,leave_side);
+    CheckTriggeredElementChangeBySide(jx, jy, Feld[jx][jy], CE_OTHER_GETS_LEFT,
+                                     leave_side);
+    CheckElementChangeBySide(jx,jy, Feld[jx][jy],CE_LEFT_BY_PLAYER,leave_side);
   }
 
   if (IS_CUSTOM_ELEMENT(Feld[new_jx][new_jy]))
   {
-    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);
+    CheckTriggeredElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
+                                     CE_OTHER_GETS_ENTERED, enter_side);
+    CheckElementChangeBySide(new_jx, new_jy, Feld[new_jx][new_jy],
+                            CE_ENTERED_BY_PLAYER, enter_side);
   }
 #endif
 
@@ -9198,6 +9287,10 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
 
 #if 0
     /* !!! ENABLE THIS FOR OLD VERSIONS !!! */
+
+#if 1
+    if (game.engine_version < VERSION_IDENT(3,1,0,0))
+#endif
     {
       static int trigger_sides[4][2] =
       {
@@ -9212,22 +9305,23 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
       int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
 
 #if 1
-      CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
-                                       CE_OTHER_GETS_LEFT,
-                                       player->index_bit, leave_side);
-
+      /* !!! TEST ONLY !!! */
       if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
-       CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
-                                CE_LEFT_BY_PLAYER,
-                                player->index_bit, leave_side);
+       CheckElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
+                                  CE_LEFT_BY_PLAYER,
+                                  player->index_bit, leave_side);
 
-      CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
-                                       CE_OTHER_GETS_ENTERED,
-                                       player->index_bit, enter_side);
+      CheckTriggeredElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
+                                         CE_OTHER_GETS_LEFT,
+                                         player->index_bit, leave_side);
 
       if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
-       CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
-                                player->index_bit, enter_side);
+       CheckElementChangeByPlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
+                                  player->index_bit, enter_side);
+
+      CheckTriggeredElementChangeByPlayer(jx, jy, Feld[jx][jy],
+                                         CE_OTHER_GETS_ENTERED,
+                                         player->index_bit, enter_side);
 #endif
 
     }
@@ -9347,6 +9441,9 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
 #if 1
     /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
     /* this breaks one level: "machine", level 000 */
+#if 0
+    if (game.engine_version >= VERSION_IDENT(3,1,0,0))
+#endif
     {
       static int trigger_sides[4][2] =
       {
@@ -9363,22 +9460,23 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
       int old_jy = last_jy;
 
 #if 1
-      CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
-                                       CE_OTHER_GETS_LEFT,
-                                       player->index_bit, leave_side);
-
+      /* !!! TEST ONLY !!! */
       if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
-       CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
-                                CE_LEFT_BY_PLAYER,
-                                player->index_bit, leave_side);
+       CheckElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
+                                  CE_LEFT_BY_PLAYER,
+                                  player->index_bit, leave_side);
 
-      CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
-                                       CE_OTHER_GETS_ENTERED,
-                                       player->index_bit, enter_side);
+      CheckTriggeredElementChangeByPlayer(old_jx, old_jy, Feld[old_jx][old_jy],
+                                         CE_OTHER_GETS_LEFT,
+                                         player->index_bit, leave_side);
 
       if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
-       CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
-                                player->index_bit, enter_side);
+       CheckElementChangeByPlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
+                                  player->index_bit, enter_side);
+
+      CheckTriggeredElementChangeByPlayer(jx, jy, Feld[jx][jy],
+                                         CE_OTHER_GETS_ENTERED,
+                                         player->index_bit, enter_side);
 #endif
 
     }
@@ -9517,11 +9615,20 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
       else
        continue;               /* center and border element do not touch */
 
-      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);
+#if 1
+      /* !!! TEST ONLY !!! */
+      CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
+                                player->index_bit, border_side);
+      CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
+                                         CE_OTHER_GETS_TOUCHED,
+                                         player->index_bit, border_side);
+#else
+      CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
+                                         CE_OTHER_GETS_TOUCHED,
+                                         player->index_bit, border_side);
+      CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
+                                player->index_bit, border_side);
+#endif
     }
     else if (IS_PLAYER(xx, yy))
     {
@@ -9533,11 +9640,20 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
          continue;             /* center and border element do not touch */
       }
 
-      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);
+#if 1
+      /* !!! TEST ONLY !!! */
+      CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
+                                player->index_bit, center_side);
+      CheckTriggeredElementChangeByPlayer(x, y, center_element,
+                                         CE_OTHER_GETS_TOUCHED,
+                                         player->index_bit, center_side);
+#else
+      CheckTriggeredElementChangeByPlayer(x, y, center_element,
+                                         CE_OTHER_GETS_TOUCHED,
+                                         player->index_bit, center_side);
+      CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
+                                player->index_bit, center_side);
+#endif
 
       break;
     }
@@ -9646,8 +9762,8 @@ void TestIfElementTouchesCustomElement(int x, int y)
          printf("::: border_element %d, %d\n", x, y);
 #endif
 
-         CheckElementChangePage(xx, yy, border_element, center_element,
-                                CE_OTHER_IS_TOUCHING, j);
+         CheckElementChangeByPage(xx, yy, border_element, center_element,
+                                  CE_OTHER_IS_TOUCHING, j);
          break;
        }
       }
@@ -9660,8 +9776,8 @@ void TestIfElementTouchesCustomElement(int x, int y)
     printf("::: center_element %d, %d\n", x, y);
 #endif
 
-    CheckElementChangePage(x, y, center_element, border_trigger_element,
-                          CE_OTHER_IS_TOUCHING, center_element_change_page);
+    CheckElementChangeByPage(x, y, center_element, border_trigger_element,
+                            CE_OTHER_IS_TOUCHING, center_element_change_page);
   }
 }
 
@@ -9691,8 +9807,8 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
   touched_element = (IN_LEV_FIELD(hitx, hity) ?
                     MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
 
-  CheckElementChangeSide(x, y, hitting_element, touched_element,
-                        CE_HITTING_SOMETHING, direction);
+  CheckElementChangeBySide(x, y, hitting_element, touched_element,
+                          CE_HITTING_SOMETHING, direction);
 
   if (IN_LEV_FIELD(hitx, hity))
   {
@@ -9714,8 +9830,8 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
     {
       int i;
 
-      CheckElementChangeSide(hitx, hity, touched_element, hitting_element,
-                            CE_HIT_BY_SOMETHING, opposite_direction);
+      CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
+                              CE_HIT_BY_SOMETHING, opposite_direction);
 
       if (IS_CUSTOM_ELEMENT(hitting_element) &&
          HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_HITTING))
@@ -9736,8 +9852,8 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
 #endif
              )
          {
-           CheckElementChangePage(x, y, hitting_element, touched_element,
-                                  CE_OTHER_IS_HITTING, i);
+           CheckElementChangeByPage(x, y, hitting_element, touched_element,
+                                    CE_OTHER_IS_HITTING, i);
            break;
          }
        }
@@ -9761,8 +9877,8 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
 #endif
              )
          {
-           CheckElementChangePage(hitx, hity, touched_element,
-                                  hitting_element, CE_OTHER_GETS_HIT, i);
+           CheckElementChangeByPage(hitx, hity, touched_element,
+                                    hitting_element, CE_OTHER_GETS_HIT, i);
            break;
          }
        }
@@ -9798,8 +9914,8 @@ void TestIfElementSmashesCustomElement(int x, int y, int direction)
   touched_element = (IN_LEV_FIELD(hitx, hity) ?
                     MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
 
-  CheckElementChangeSide(x, y, hitting_element, touched_element,
-                        EP_CAN_SMASH_EVERYTHING, direction);
+  CheckElementChangeBySide(x, y, hitting_element, touched_element,
+                          EP_CAN_SMASH_EVERYTHING, direction);
 
   if (IN_LEV_FIELD(hitx, hity))
   {
@@ -9821,8 +9937,8 @@ void TestIfElementSmashesCustomElement(int x, int y, int direction)
     {
       int i;
 
-      CheckElementChangeSide(hitx, hity, touched_element, hitting_element,
-                            CE_SMASHED_BY_SOMETHING, opposite_direction);
+      CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
+                              CE_SMASHED_BY_SOMETHING, opposite_direction);
 
       if (IS_CUSTOM_ELEMENT(hitting_element) &&
          HAS_ANY_CHANGE_EVENT(hitting_element, CE_OTHER_IS_SMASHING))
@@ -9843,8 +9959,8 @@ void TestIfElementSmashesCustomElement(int x, int y, int direction)
 #endif
              )
          {
-           CheckElementChangePage(x, y, hitting_element, touched_element,
-                                  CE_OTHER_IS_SMASHING, i);
+           CheckElementChangeByPage(x, y, hitting_element, touched_element,
+                                    CE_OTHER_IS_SMASHING, i);
            break;
          }
        }
@@ -9868,8 +9984,8 @@ void TestIfElementSmashesCustomElement(int x, int y, int direction)
 #endif
              )
          {
-           CheckElementChangePage(hitx, hity, touched_element,
-                                  hitting_element, CE_OTHER_GETS_SMASHED, i);
+           CheckElementChangeByPage(hitx, hity, touched_element,
+                                    hitting_element, CE_OTHER_GETS_SMASHED,i);
            break;
          }
        }
@@ -9882,6 +9998,7 @@ void TestIfElementSmashesCustomElement(int x, int y, int direction)
 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
 {
   int i, kill_x = -1, kill_y = -1;
+  int bad_element = -1;
   static int test_xy[4][2] =
   {
     { 0, -1 },
@@ -9903,6 +10020,7 @@ void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
 
     test_x = good_x + test_xy[i][0];
     test_y = good_y + test_xy[i][1];
+
     if (!IN_LEV_FIELD(test_x, test_y))
       continue;
 
@@ -9923,6 +10041,8 @@ void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
     {
       kill_x = test_x;
       kill_y = test_y;
+      bad_element = test_element;
+
       break;
     }
   }
@@ -9933,10 +10053,18 @@ void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
     {
       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
 
+#if 1
+      if (player->shield_deadly_time_left > 0 &&
+         !IS_INDESTRUCTIBLE(bad_element))
+       Bang(kill_x, kill_y);
+      else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
+       KillHero(player);
+#else
       if (player->shield_deadly_time_left > 0)
        Bang(kill_x, kill_y);
       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
        KillHero(player);
+#endif
     }
     else
       Bang(good_x, good_y);
@@ -10025,10 +10153,18 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
     {
       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
 
+#if 1
+      if (player->shield_deadly_time_left > 0 &&
+         !IS_INDESTRUCTIBLE(bad_element))
+       Bang(bad_x, bad_y);
+      else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
+       KillHero(player);
+#else
       if (player->shield_deadly_time_left > 0)
        Bang(bad_x, bad_y);
       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
        KillHero(player);
+#endif
     }
     else
       Bang(kill_x, kill_y);
@@ -10428,12 +10564,18 @@ int DigField(struct PlayerInfo *player,
 
     default:
 
+#if 1
+      if (IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
+#else
       if (IS_WALKABLE(element))
+#endif
       {
        int sound_action = ACTION_WALKING;
 
+#if 0
        if (!ACCESS_FROM(element, opposite_direction))
          return MF_NO_ACTION;  /* field not accessible from this direction */
+#endif
 
 #if 1
        if (element == EL_EMPTY_SPACE &&
@@ -10471,13 +10613,18 @@ int DigField(struct PlayerInfo *player,
 
        break;
       }
+#if 1
+      else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
+#else
       else if (IS_PASSABLE(element))
+#endif
       {
-#if 1
+#if 0
        if (!canPassField(x, y, move_direction))
          return MF_NO_ACTION;
 #else
 
+#if 0
 #if 1
        if (!IN_LEV_FIELD(nextx, nexty) || IS_PLAYER(nextx, nexty) ||
            !IS_WALKABLE_FROM(Feld[nextx][nexty], move_direction) ||
@@ -10487,6 +10634,7 @@ int DigField(struct PlayerInfo *player,
        if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
          return MF_NO_ACTION;
 #endif
+#endif
 
 #if 1
        if (!ACCESS_FROM(element, opposite_direction))
@@ -10559,8 +10707,8 @@ int DigField(struct PlayerInfo *player,
 
        PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
 
-       CheckTriggeredElementChangePlayer(x, y, element, CE_OTHER_GETS_DIGGED,
-                                         player->index_bit, dig_side);
+       CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_DIGGED,
+                                           player->index_bit, dig_side);
 
 #if 1
        if (mode == DF_SNAP)
@@ -10632,7 +10780,8 @@ int DigField(struct PlayerInfo *player,
          ShowEnvelope(element - EL_ENVELOPE_1);
 #endif
        }
-       else if (IS_DROPPABLE(element)) /* can be collected and dropped */
+       else if (IS_DROPPABLE(element) ||
+                IS_THROWABLE(element)) /* can be collected and dropped */
        {
          int i;
 
@@ -10658,9 +10807,9 @@ int DigField(struct PlayerInfo *player,
        RaiseScoreElement(element);
        PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
 
-       CheckTriggeredElementChangePlayer(x, y, element,
-                                         CE_OTHER_GETS_COLLECTED,
-                                         player->index_bit, dig_side);
+       CheckTriggeredElementChangeByPlayer(x, y, element,
+                                           CE_OTHER_GETS_COLLECTED,
+                                           player->index_bit, dig_side);
 
 #if 1
        if (mode == DF_SNAP)
@@ -10816,10 +10965,23 @@ int DigField(struct PlayerInfo *player,
        else
          player->push_delay_value = -1;        /* get new value later */
 
-       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);
+#if 1
+       /* check for element change _after_ element has been pushed! */
+#else
+
+#if 1
+      /* !!! TEST ONLY !!! */
+       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
+                                  player->index_bit, dig_side);
+       CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
+                                           player->index_bit, dig_side);
+#else
+       CheckTriggeredElementChangeByPlayer(x, y, element,CE_OTHER_GETS_PUSHED,
+                                           player->index_bit, dig_side);
+       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
+                                  player->index_bit, dig_side);
+#endif
+#endif
 
        break;
       }
@@ -10923,17 +11085,34 @@ int DigField(struct PlayerInfo *player,
          player->switch_x = x;
          player->switch_y = y;
 
-         CheckTriggeredElementChangePlayer(x, y, element,
-                                           CE_OTHER_IS_SWITCHING,
-                                           player->index_bit, dig_side);
-         CheckElementChangePlayer(x, y, element, CE_SWITCHED,
-                                  player->index_bit, dig_side);
+#if 1
+         /* !!! TEST ONLY !!! */
+         CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
+                                    player->index_bit, dig_side);
+         CheckTriggeredElementChangeByPlayer(x, y, element,
+                                             CE_OTHER_IS_SWITCHING,
+                                             player->index_bit, dig_side);
+#else
+         CheckTriggeredElementChangeByPlayer(x, y, element,
+                                             CE_OTHER_IS_SWITCHING,
+                                             player->index_bit, dig_side);
+         CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
+                                    player->index_bit, dig_side);
+#endif
        }
 
-       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);
+#if 1
+       /* !!! TEST ONLY !!! (this breaks "machine", level 000) */
+       CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
+                                  player->index_bit, dig_side);
+       CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
+                                           player->index_bit, dig_side);
+#else
+       CheckTriggeredElementChangeByPlayer(x,y, element,CE_OTHER_GETS_PRESSED,
+                                           player->index_bit, dig_side);
+       CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
+                                  player->index_bit, dig_side);
+#endif
       }
 
       return MF_NO_ACTION;
@@ -11032,10 +11211,10 @@ boolean DropElement(struct PlayerInfo *player)
     CH_SIDE_TOP,       /* dropping up    */
     CH_SIDE_BOTTOM,    /* dropping down  */
   };
-  int jx = player->jx, jy = player->jy;
+  int old_element, new_element;
+  int dropx = player->jx, dropy = player->jy;
   int drop_direction = player->MovDir;
   int drop_side = trigger_sides[MV_DIR_BIT(drop_direction)];
-  int old_element = Feld[jx][jy];
   int drop_element = (player->inventory_size > 0 ?
                      player->inventory_element[player->inventory_size - 1] :
                      player->inventory_infinite_element != EL_UNDEFINED ?
@@ -11043,7 +11222,18 @@ boolean DropElement(struct PlayerInfo *player)
                      player->dynabombs_left > 0 ?
                      EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
                      EL_UNDEFINED);
-  int new_element = drop_element;      /* default: element does not change */
+
+  if (IS_THROWABLE(drop_element))
+  {
+    dropx += GET_DX_FROM_DIR(drop_direction);
+    dropy += GET_DY_FROM_DIR(drop_direction);
+
+    if (!IN_LEV_FIELD(dropx, dropy))
+      return FALSE;
+  }
+
+  old_element = Feld[dropx][dropy];    /* old element at dropping position */
+  new_element = drop_element;          /* default: no change when dropping */
 
   /* check if player is active, not moving and ready to drop */
   if (!player->active || player->MovPos || player->drop_delay > 0)
@@ -11076,10 +11266,10 @@ boolean DropElement(struct PlayerInfo *player)
 #endif
 
   if (old_element != EL_EMPTY)
-    Back[jx][jy] = old_element;                /* store old element on this field */
+    Back[dropx][dropy] = old_element;  /* store old element on this field */
 
-  ResetGfxAnimation(jx, jy);
-  ResetRandomAnimationValue(jx, jy);
+  ResetGfxAnimation(dropx, dropy);
+  ResetRandomAnimationValue(dropx, dropy);
 
   if (player->inventory_size > 0 ||
       player->inventory_infinite_element != EL_UNDEFINED)
@@ -11100,34 +11290,35 @@ boolean DropElement(struct PlayerInfo *player)
        new_element = EL_SP_DISK_RED_ACTIVE;
     }
 
-    Feld[jx][jy] = new_element;
+    Feld[dropx][dropy] = new_element;
 
-    if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
-      DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
+    if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
+      DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
+                         el2img(Feld[dropx][dropy]), 0);
 
-    PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
+    PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
 
 #if 1
     /* needed if previous element just changed to "empty" in the last frame */
-    Changed[jx][jy] = 0;               /* allow another change */
+    Changed[dropx][dropy] = 0;         /* allow another change */
 #endif
 
 #if 1
     /* !!! TEST ONLY !!! */
-    CheckElementChangePlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER,
-                            player->index_bit, drop_side);
-    CheckTriggeredElementChangePlayer(jx, jy, new_element,
-                                     CE_OTHER_GETS_DROPPED,
-                                     player->index_bit, drop_side);
+    CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
+                              player->index_bit, drop_side);
+    CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
+                                       CE_OTHER_GETS_DROPPED,
+                                       player->index_bit, drop_side);
 #else
-    CheckTriggeredElementChangePlayer(jx, jy, new_element,
-                                     CE_OTHER_GETS_DROPPED,
-                                     player->index_bit, drop_side);
-    CheckElementChangePlayer(jx, jy, new_element, CE_DROPPED_BY_PLAYER,
-                            player->index_bit, drop_side);
+    CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
+                                       CE_OTHER_GETS_DROPPED,
+                                       player->index_bit, drop_side);
+    CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
+                              player->index_bit, drop_side);
 #endif
 
-    TestIfElementTouchesCustomElement(jx, jy);
+    TestIfElementTouchesCustomElement(dropx, dropy);
   }
   else         /* player is dropping a dyna bomb */
   {
@@ -11137,30 +11328,31 @@ boolean DropElement(struct PlayerInfo *player)
     new_element = EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr;
 #endif
 
-    Feld[jx][jy] = new_element;
+    Feld[dropx][dropy] = new_element;
 
-    if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
-      DrawGraphicThruMask(SCREENX(jx), SCREENY(jy), el2img(Feld[jx][jy]), 0);
+    if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
+      DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
+                         el2img(Feld[dropx][dropy]), 0);
 
-    PlayLevelSoundAction(jx, jy, ACTION_DROPPING);
+    PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
   }
 
 
 
 #if 1
 
-  if (Feld[jx][jy] == new_element)     /* uninitialized unless CE change */
+  if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
   {
 #if 1
-    InitField_WithBug1(jx, jy, FALSE);
+    InitField_WithBug1(dropx, dropy, FALSE);
 #else
-    InitField(jx, jy, FALSE);
-    if (CAN_MOVE(Feld[jx][jy]))
-      InitMovDir(jx, jy);
+    InitField(dropx, dropy, FALSE);
+    if (CAN_MOVE(Feld[dropx][dropy]))
+      InitMovDir(dropx, dropy);
 #endif
   }
 
-  new_element = Feld[jx][jy];          /* element might have changed */
+  new_element = Feld[dropx][dropy];    /* element might have changed */
 
   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
@@ -11168,37 +11360,46 @@ boolean DropElement(struct PlayerInfo *player)
 #if 0
     int move_stepsize = element_info[new_element].move_stepsize;
 #endif
-    int direction, dx, dy, nextx, nexty;
+    int move_direction, nextx, nexty;
 
     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
-      MovDir[jx][jy] = player->MovDir;
+      MovDir[dropx][dropy] = drop_direction;
 
-    direction = MovDir[jx][jy];
-    dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
-    dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
-    nextx = jx + dx;
-    nexty = jy + dy;
+    move_direction = MovDir[dropx][dropy];
+    nextx = dropx + GET_DX_FROM_DIR(move_direction);
+    nexty = dropy + GET_DY_FROM_DIR(move_direction);
+
+#if 1
+      Changed[dropx][dropy] = 0;       /* allow another change */
+      CheckCollision[dropx][dropy] = 2;
+#else
 
     if (IN_LEV_FIELD(nextx, nexty) && IS_FREE(nextx, nexty))
     {
 #if 0
-      WasJustMoving[jx][jy] = 3;
+      WasJustMoving[dropx][dropy] = 3;
 #else
-      InitMovingField(jx, jy, direction);
-      ContinueMoving(jx, jy);
+#if 1
+      InitMovingField(dropx, dropy, move_direction);
+      ContinueMoving(dropx, dropy);
+#endif
 #endif
     }
+#if 1
     else
     {
-      Changed[jx][jy] = 0;             /* allow another change */
+      Changed[dropx][dropy] = 0;       /* allow another change */
 
 #if 1
-      TestIfElementHitsCustomElement(jx, jy, direction);
+      TestIfElementHitsCustomElement(dropx, dropy, move_direction);
 #else
-      CheckElementChangeSide(jx, jy, new_element, touched_element,
-                            CE_HITTING_SOMETHING, direction);
+      CheckElementChangeBySide(dropx, dropy, new_element, touched_element,
+                              CE_HITTING_SOMETHING, move_direction);
 #endif
     }
+#endif
+
+#endif
 
 #if 0
     player->drop_delay = 2 * TILEX / move_stepsize + 1;