rnd-20030624-1-src
[rocksndiamonds.git] / src / game.c
index 770f34bc1e5eaf84c754d87bc39243c49b3b183a..d0f7751bbbca9183bf1fed4ec03edb5008671678 100644 (file)
 #define GET_NEW_MOVE_DELAY(e)  (   (element_info[e].move_delay_fixed) + \
                                 RND(element_info[e].move_delay_random))
 
+#define ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, condition)            \
+               (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
+                                       (condition) ||                  \
+                                       (DONT_COLLIDE_WITH(e) &&        \
+                                        IS_FREE_OR_PLAYER(x, y))))
+
+#define ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, condition)             \
+               (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
+                                       (condition)))
+
+#define ELEMENT_CAN_ENTER_FIELD(e, x, y)                               \
+       ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 1)
+
+#define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y)                       \
+       ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID))
+
+#define ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(x, y)                                \
+       ELEMENT_CAN_ENTER_FIELD_GENERIC_2(x, y, (Feld[x][y] == EL_ACID))
+
+#define ENEMY_CAN_ENTER_FIELD(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
+
+#define YAMYAM_CAN_ENTER_FIELD(x, y)                                   \
+               (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
+                                       Feld[x][y] == EL_DIAMOND))
+
+#define DARK_YAMYAM_CAN_ENTER_FIELD(x, y)                              \
+               (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
+                                       IS_FOOD_DARK_YAMYAM(Feld[x][y])))
+
+#define PACMAN_CAN_ENTER_FIELD(x, y)                                   \
+               (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
+                                       IS_AMOEBOID(Feld[x][y])))
+
+#define PIG_CAN_ENTER_FIELD(x, y)                                      \
+               (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
+                                       IS_FOOD_PIG(Feld[x][y])))
+
+#define PENGUIN_CAN_ENTER_FIELD(x, y)                                  \
+               (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
+                                       IS_FOOD_PENGUIN(Feld[x][y]) ||  \
+                                       Feld[x][y] == EL_EXIT_OPEN ||   \
+                                       Feld[x][y] == EL_ACID))
+
+#define MOLE_CAN_ENTER_FIELD(x, y, condition)                          \
+               (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || (condition)))
+
+#define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
+#define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
+
 /* game button identifiers */
 #define GAME_CTRL_ID_STOP              0
 #define GAME_CTRL_ID_PAUSE             1
@@ -114,6 +163,8 @@ static void CloseAllOpenTimegates(void);
 static void CheckGravityMovement(struct PlayerInfo *);
 static void KillHeroUnlessProtected(int, int);
 
+static void CheckTriggeredElementChange(int, int);
+static void CheckPlayerElementChange(int, int, int, int);
 static void ChangeElementDoIt(int, int, int);
 
 static void PlaySoundLevel(int, int, int);
@@ -777,7 +828,6 @@ void InitGame()
     player->actual_frame_counter = 0;
 
     player->last_move_dir = MV_NO_MOVING;
-    player->is_moving = FALSE;
 
     player->is_moving = FALSE;
     player->is_waiting = FALSE;
@@ -1454,8 +1504,10 @@ static void ResetGfxAnimation(int x, int y)
 void InitMovingField(int x, int y, int direction)
 {
   int element = Feld[x][y];
-  int newx = x + (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
-  int newy = y + (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
+  int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
+  int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
+  int newx = x + dx;
+  int newy = y + dy;
 
   if (!JustStopped[x][y] || direction != MovDir[x][y])
     ResetGfxAnimation(x, y);
@@ -2023,6 +2075,8 @@ void Bang(int x, int y)
       Explode(x, y, EX_PHASE_START, EX_NORMAL);
       break;
   }
+
+  CheckTriggeredElementChange(element, CE_OTHER_EXPLODING);
 }
 
 void SplashAcid(int x, int y)
@@ -2387,14 +2441,7 @@ void Impact(int x, int y)
     DrawLevelField(x, y);
   }
 
-#if 1
   if (impact && CAN_EXPLODE_IMPACT(element))
-#else
-  if ((element == EL_BOMB ||
-       element == EL_SP_DISK_ORANGE ||
-       element == EL_DX_SUPABOMB) &&
-      (lastline || object_hit))                /* element is bomb */
-#endif
   {
     Bang(x, y);
     return;
@@ -2431,11 +2478,7 @@ void Impact(int x, int y)
     return;
   }
 
-#if 1
   if (object_hit)              /* check which object was hit */
-#else
-  if (!lastline && object_hit)         /* check which object was hit */
-#endif
   {
     if (CAN_PASS_MAGIC_WALL(element) && 
        (smashed == EL_MAGIC_WALL ||
@@ -2493,35 +2536,15 @@ void Impact(int x, int y)
       Bang(x, y + 1);
       return;
     }
-#if 1
     else if (CAN_SMASH_EVERYTHING(element))
-#else
-    else if (element == EL_ROCK ||
-            element == EL_SP_ZONK ||
-            element == EL_BD_ROCK)
-#endif
     {
       if (IS_CLASSIC_ENEMY(smashed) ||
-#if 1
          CAN_EXPLODE_SMASHED(smashed))
-#else
-         smashed == EL_BOMB ||
-         smashed == EL_SP_DISK_ORANGE ||
-         smashed == EL_DX_SUPABOMB ||
-         smashed == EL_SATELLITE ||
-         smashed == EL_PIG ||
-         smashed == EL_DRAGON ||
-         smashed == EL_MOLE)
-#endif
       {
        Bang(x, y + 1);
        return;
       }
-#if 1
       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
-#else
-      else if (!IS_MOVING(x, y + 1))
-#endif
       {
        if (smashed == EL_LAMP ||
            smashed == EL_LAMP_ACTIVE)
@@ -2544,11 +2567,7 @@ void Impact(int x, int y)
        }
        else if (smashed == EL_DIAMOND)
        {
-#if 1
          Feld[x][y + 1] = EL_DIAMOND_BREAKING;
-#else
-         Feld[x][y + 1] = EL_EMPTY;
-#endif
          PlaySoundLevel(x, y, SND_DIAMOND_BREAKING);
          return;
        }
@@ -2618,7 +2637,9 @@ void TurnRound(int x, int y)
     { MV_UP,   MV_DOWN,        MV_LEFT  },
     { 0,       0,              0        },
     { MV_LEFT, MV_RIGHT,       MV_DOWN  },
-    { 0,0,0 }, { 0,0,0 },      { 0,0,0  },
+    { 0,       0,              0        },
+    { 0,       0,              0        },
+    { 0,       0,              0        },
     { MV_RIGHT,        MV_LEFT,        MV_UP    }
   };
 
@@ -2637,15 +2658,15 @@ void TurnRound(int x, int y)
   int right_x = x + right_dx, right_y = y + right_dy;
   int move_x  = x + move_dx,  move_y  = y + move_dy;
 
+  int xx, yy;
+
   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
   {
     TestIfBadThingTouchesOtherBadThing(x, y);
 
-    if (IN_LEV_FIELD(right_x, right_y) &&
-       IS_FREE(right_x, right_y))
+    if (ENEMY_CAN_ENTER_FIELD(right_x, right_y))
       MovDir[x][y] = right_dir;
-    else if (!IN_LEV_FIELD(move_x, move_y) ||
-            !IS_FREE(move_x, move_y))
+    else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
       MovDir[x][y] = left_dir;
 
     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
@@ -2658,15 +2679,14 @@ void TurnRound(int x, int y)
   {
     TestIfBadThingTouchesOtherBadThing(x, y);
 
-    if (IN_LEV_FIELD(left_x, left_y) &&
-       IS_FREE(left_x, left_y))
+    if (ENEMY_CAN_ENTER_FIELD(left_x, left_y))
       MovDir[x][y] = left_dir;
-    else if (!IN_LEV_FIELD(move_x, move_y) ||
-            !IS_FREE(move_x, move_y))
+    else if (!ENEMY_CAN_ENTER_FIELD(move_x, move_y))
       MovDir[x][y] = right_dir;
 
     if ((element == EL_SPACESHIP ||
-        element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
+        element == EL_SP_SNIKSNAK ||
+        element == EL_SP_ELECTRON)
        && MovDir[x][y] != old_move_dir)
       MovDelay[x][y] = 9;
     else if (element == EL_BD_FIREFLY)     /* && MovDir[x][y] == right_dir) */
@@ -2674,16 +2694,8 @@ void TurnRound(int x, int y)
   }
   else if (element == EL_YAMYAM)
   {
-    boolean can_turn_left = FALSE, can_turn_right = FALSE;
-
-    if (IN_LEV_FIELD(left_x, left_y) &&
-       (IS_FREE_OR_PLAYER(left_x, left_y) ||
-        Feld[left_x][left_y] == EL_DIAMOND))
-      can_turn_left = TRUE;
-    if (IN_LEV_FIELD(right_x, right_y) &&
-       (IS_FREE_OR_PLAYER(right_x, right_y) ||
-        Feld[right_x][right_y] == EL_DIAMOND))
-      can_turn_right = TRUE;
+    boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
+    boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
 
     if (can_turn_left && can_turn_right)
       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
@@ -2694,20 +2706,12 @@ void TurnRound(int x, int y)
     else
       MovDir[x][y] = back_dir;
 
-    MovDelay[x][y] = 16+16*RND(3);
+    MovDelay[x][y] = 16 + 16 * RND(3);
   }
   else if (element == EL_DARK_YAMYAM)
   {
-    boolean can_turn_left = FALSE, can_turn_right = FALSE;
-
-    if (IN_LEV_FIELD(left_x, left_y) &&
-       (IS_FREE_OR_PLAYER(left_x, left_y) ||
-        IS_FOOD_DARK_YAMYAM(Feld[left_x][left_y])))
-      can_turn_left = TRUE;
-    if (IN_LEV_FIELD(right_x, right_y) &&
-       (IS_FREE_OR_PLAYER(right_x, right_y) ||
-        IS_FOOD_DARK_YAMYAM(Feld[right_x][right_y])))
-      can_turn_right = TRUE;
+    boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(left_x, left_y);
+    boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(right_x, right_y);
 
     if (can_turn_left && can_turn_right)
       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
@@ -2718,20 +2722,12 @@ void TurnRound(int x, int y)
     else
       MovDir[x][y] = back_dir;
 
-    MovDelay[x][y] = 16+16*RND(3);
+    MovDelay[x][y] = 16 + 16 * RND(3);
   }
   else if (element == EL_PACMAN)
   {
-    boolean can_turn_left = FALSE, can_turn_right = FALSE;
-
-    if (IN_LEV_FIELD(left_x, left_y) &&
-       (IS_FREE_OR_PLAYER(left_x, left_y) ||
-        IS_AMOEBOID(Feld[left_x][left_y])))
-      can_turn_left = TRUE;
-    if (IN_LEV_FIELD(right_x, right_y) &&
-       (IS_FREE_OR_PLAYER(right_x, right_y) ||
-        IS_AMOEBOID(Feld[right_x][right_y])))
-      can_turn_right = TRUE;
+    boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(left_x, left_y);
+    boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(right_x, right_y);
 
     if (can_turn_left && can_turn_right)
       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
@@ -2742,56 +2738,45 @@ void TurnRound(int x, int y)
     else
       MovDir[x][y] = back_dir;
 
-    MovDelay[x][y] = 6+RND(40);
+    MovDelay[x][y] = 6 + RND(40);
   }
   else if (element == EL_PIG)
   {
-    boolean can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE;
-    boolean should_turn_left = FALSE, should_turn_right = FALSE;
-    boolean should_move_on = FALSE;
+    boolean can_turn_left  = PIG_CAN_ENTER_FIELD(left_x, left_y);
+    boolean can_turn_right = PIG_CAN_ENTER_FIELD(right_x, right_y);
+    boolean can_move_on    = PIG_CAN_ENTER_FIELD(move_x, move_y);
+    boolean should_turn_left, should_turn_right, should_move_on;
     int rnd_value = 24;
     int rnd = RND(rnd_value);
 
-    if (IN_LEV_FIELD(left_x, left_y) &&
-       (IS_FREE(left_x, left_y) || IS_FOOD_PIG(Feld[left_x][left_y])))
-      can_turn_left = TRUE;
-    if (IN_LEV_FIELD(right_x, right_y) &&
-       (IS_FREE(right_x, right_y) || IS_FOOD_PIG(Feld[right_x][right_y])))
-      can_turn_right = TRUE;
-    if (IN_LEV_FIELD(move_x, move_y) &&
-       (IS_FREE(move_x, move_y) || IS_FOOD_PIG(Feld[move_x][move_y])))
-      can_move_on = TRUE;
-
-    if (can_turn_left &&
-       (!can_move_on ||
-        (IN_LEV_FIELD(x+back_dx+left_dx, y+back_dy+left_dy) &&
-         !IS_FREE(x+back_dx+left_dx, y+back_dy+left_dy))))
-      should_turn_left = TRUE;
-    if (can_turn_right &&
-       (!can_move_on ||
-        (IN_LEV_FIELD(x+back_dx+right_dx, y+back_dy+right_dy) &&
-         !IS_FREE(x+back_dx+right_dx, y+back_dy+right_dy))))
-      should_turn_right = TRUE;
-    if (can_move_on &&
-       (!can_turn_left || !can_turn_right ||
-        (IN_LEV_FIELD(x+move_dx+left_dx, y+move_dy+left_dy) &&
-         !IS_FREE(x+move_dx+left_dx, y+move_dy+left_dy)) ||
-        (IN_LEV_FIELD(x+move_dx+right_dx, y+move_dy+right_dy) &&
-         !IS_FREE(x+move_dx+right_dx, y+move_dy+right_dy))))
-      should_move_on = TRUE;
+    should_turn_left = (can_turn_left &&
+                       (!can_move_on ||
+                        IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
+                                                  y + back_dy + left_dy)));
+    should_turn_right = (can_turn_right &&
+                        (!can_move_on ||
+                         IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
+                                                   y + back_dy + right_dy)));
+    should_move_on = (can_move_on &&
+                     (!can_turn_left ||
+                      !can_turn_right ||
+                      IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
+                                                y + move_dy + left_dy) ||
+                      IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
+                                                y + move_dy + right_dy)));
 
     if (should_turn_left || should_turn_right || should_move_on)
     {
       if (should_turn_left && should_turn_right && should_move_on)
-       MovDir[x][y] = (rnd < rnd_value/3 ? left_dir :
-                       rnd < 2*rnd_value/3 ? right_dir :
+       MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
+                       rnd < 2 * rnd_value / 3 ? right_dir :
                        old_move_dir);
       else if (should_turn_left && should_turn_right)
-       MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : right_dir);
+       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
       else if (should_turn_left && should_move_on)
-       MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : old_move_dir);
+       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
       else if (should_turn_right && should_move_on)
-       MovDir[x][y] = (rnd < rnd_value/2 ? right_dir : old_move_dir);
+       MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
       else if (should_turn_left)
        MovDir[x][y] = left_dir;
       else if (should_turn_right)
@@ -2799,69 +2784,67 @@ void TurnRound(int x, int y)
       else if (should_move_on)
        MovDir[x][y] = old_move_dir;
     }
-    else if (can_move_on && rnd > rnd_value/8)
+    else if (can_move_on && rnd > rnd_value / 8)
       MovDir[x][y] = old_move_dir;
     else if (can_turn_left && can_turn_right)
-      MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : right_dir);
-    else if (can_turn_left && rnd > rnd_value/8)
+      MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
+    else if (can_turn_left && rnd > rnd_value / 8)
       MovDir[x][y] = left_dir;
     else if (can_turn_right && rnd > rnd_value/8)
       MovDir[x][y] = right_dir;
     else
       MovDir[x][y] = back_dir;
 
-    if (!IS_FREE(x+move_xy[MovDir[x][y]].x, y+move_xy[MovDir[x][y]].y) &&
-       !IS_FOOD_PIG(Feld[x+move_xy[MovDir[x][y]].x][y+move_xy[MovDir[x][y]].y]))
+    xx = x + move_xy[MovDir[x][y]].x;
+    yy = y + move_xy[MovDir[x][y]].y;
+
+    if (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy]))
       MovDir[x][y] = old_move_dir;
 
     MovDelay[x][y] = 0;
   }
   else if (element == EL_DRAGON)
   {
-    boolean can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE;
+    boolean can_turn_left  = IN_LEV_FIELD_AND_IS_FREE(left_x, left_y);
+    boolean can_turn_right = IN_LEV_FIELD_AND_IS_FREE(right_x, right_y);
+    boolean can_move_on    = IN_LEV_FIELD_AND_IS_FREE(move_x, move_y);
     int rnd_value = 24;
     int rnd = RND(rnd_value);
 
-    if (IN_LEV_FIELD(left_x, left_y) && IS_FREE(left_x, left_y))
-      can_turn_left = TRUE;
-    if (IN_LEV_FIELD(right_x, right_y) && IS_FREE(right_x, right_y))
-      can_turn_right = TRUE;
-    if (IN_LEV_FIELD(move_x, move_y) && IS_FREE(move_x, move_y))
-      can_move_on = TRUE;
-
-    if (can_move_on && rnd > rnd_value/8)
+    if (can_move_on && rnd > rnd_value / 8)
       MovDir[x][y] = old_move_dir;
     else if (can_turn_left && can_turn_right)
-      MovDir[x][y] = (rnd < rnd_value/2 ? left_dir : right_dir);
-    else if (can_turn_left && rnd > rnd_value/8)
+      MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
+    else if (can_turn_left && rnd > rnd_value / 8)
       MovDir[x][y] = left_dir;
-    else if (can_turn_right && rnd > rnd_value/8)
+    else if (can_turn_right && rnd > rnd_value / 8)
       MovDir[x][y] = right_dir;
     else
       MovDir[x][y] = back_dir;
 
-    if (!IS_FREE(x+move_xy[MovDir[x][y]].x, y+move_xy[MovDir[x][y]].y))
+    xx = x + move_xy[MovDir[x][y]].x;
+    yy = y + move_xy[MovDir[x][y]].y;
+
+    if (!IS_FREE(xx, yy))
       MovDir[x][y] = old_move_dir;
 
     MovDelay[x][y] = 0;
   }
   else if (element == EL_MOLE)
   {
-    boolean can_turn_left = FALSE, can_turn_right = FALSE, can_move_on = FALSE;
-
-    if (IN_LEV_FIELD(move_x, move_y) &&
-       (IS_FREE(move_x, move_y) || IS_AMOEBOID(Feld[move_x][move_y]) ||
-        Feld[move_x][move_y] == EL_AMOEBA_SHRINKING))
-      can_move_on = TRUE;
-
+    boolean can_move_on =
+      (MOLE_CAN_ENTER_FIELD(move_x, move_y,
+                           IS_AMOEBOID(Feld[move_x][move_y]) ||
+                           Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
     if (!can_move_on)
     {
-      if (IN_LEV_FIELD(left_x, left_y) &&
-         (IS_FREE(left_x, left_y) || IS_AMOEBOID(Feld[left_x][left_y])))
-       can_turn_left = TRUE;
-      if (IN_LEV_FIELD(right_x, right_y) &&
-         (IS_FREE(right_x, right_y) || IS_AMOEBOID(Feld[right_x][right_y])))
-       can_turn_right = TRUE;
+      boolean can_turn_left =
+       (MOLE_CAN_ENTER_FIELD(left_x, left_y,
+                             IS_AMOEBOID(Feld[left_x][left_y])));
+
+      boolean can_turn_right =
+       (MOLE_CAN_ENTER_FIELD(right_x, right_y,
+                             IS_AMOEBOID(Feld[right_x][right_y])));
 
       if (can_turn_left && can_turn_right)
        MovDir[x][y] = (RND(2) ? left_dir : right_dir);
@@ -2881,9 +2864,9 @@ void TurnRound(int x, int y)
   }
   else if (element == EL_SPRING)
   {
-    if ((MovDir[x][y] == MV_LEFT || MovDir[x][y] == MV_RIGHT) &&
-       (!IN_LEV_FIELD(move_x, move_y) || !IS_FREE(move_x, move_y) ||
-        (IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))
+    if (MovDir[x][y] & MV_HORIZONTAL &&
+       (!IN_LEV_FIELD_AND_IS_FREE(move_x, move_y) ||
+        IN_LEV_FIELD_AND_IS_FREE(x, y + 1)))
       MovDir[x][y] = MV_NO_MOVING;
 
     MovDelay[x][y] = 0;
@@ -2911,7 +2894,8 @@ void TurnRound(int x, int y)
        if (!player->active)
          continue;
 
-       if (attr_x == -1 || ABS(jx-x)+ABS(jy-y) < ABS(attr_x-x)+ABS(attr_y-y))
+       if (attr_x == -1 ||
+           ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
        {
          attr_x = jx;
          attr_y = jy;
@@ -2969,11 +2953,11 @@ void TurnRound(int x, int y)
       Moving2Blocked(x, y, &newx, &newy);
 
       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
-       MovDelay[x][y] = 8+8*!RND(3);
+       MovDelay[x][y] = 8 + 8 * !RND(3);
       else
        MovDelay[x][y] = 16;
     }
-    else
+    else if (element == EL_PENGUIN)
     {
       int newx, newy;
 
@@ -2988,24 +2972,43 @@ void TurnRound(int x, int y)
          new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
        Moving2Blocked(x, y, &newx, &newy);
 
-       if (IN_LEV_FIELD(newx, newy) &&
-           (IS_FREE(newx, newy) ||
-            Feld[newx][newy] == EL_ACID ||
-            (element == EL_PENGUIN &&
-             (Feld[newx][newy] == EL_EXIT_OPEN ||
-              IS_FOOD_PENGUIN(Feld[newx][newy])))))
+       if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
          return;
 
        MovDir[x][y] =
          new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
        Moving2Blocked(x, y, &newx, &newy);
 
-       if (IN_LEV_FIELD(newx, newy) &&
-           (IS_FREE(newx, newy) ||
-            Feld[newx][newy] == EL_ACID ||
-            (element == EL_PENGUIN &&
-             (Feld[newx][newy] == EL_EXIT_OPEN ||
-              IS_FOOD_PENGUIN(Feld[newx][newy])))))
+       if (PENGUIN_CAN_ENTER_FIELD(newx, newy))
+         return;
+
+       MovDir[x][y] = old_move_dir;
+       return;
+      }
+    }
+    else       /* (element == EL_SATELLITE) */
+    {
+      int newx, newy;
+
+      MovDelay[x][y] = 1;
+
+      if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
+      {
+       boolean first_horiz = RND(2);
+       int new_move_dir = MovDir[x][y];
+
+       MovDir[x][y] =
+         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
+       Moving2Blocked(x, y, &newx, &newy);
+
+       if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
+         return;
+
+       MovDir[x][y] =
+         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
+       Moving2Blocked(x, y, &newx, &newy);
+
+       if (ELEMENT_CAN_ENTER_FIELD_OR_ACID_2(newx, newy))
          return;
 
        MovDir[x][y] = old_move_dir;
@@ -3015,16 +3018,8 @@ void TurnRound(int x, int y)
   }
   else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS)
   {
-    boolean can_turn_left = FALSE, can_turn_right = FALSE;
-
-    if (IN_LEV_FIELD(left_x, left_y) &&
-       (IS_FREE(left_x, left_y) ||
-        (DONT_COLLIDE_WITH(element) && IS_FREE_OR_PLAYER(left_x, left_y))))
-      can_turn_left = TRUE;
-    if (IN_LEV_FIELD(right_x, right_y) &&
-       (IS_FREE(right_x, right_y) ||
-        (DONT_COLLIDE_WITH(element) && IS_FREE_OR_PLAYER(right_x, right_y))))
-      can_turn_right = TRUE;
+    boolean can_turn_left  = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
+    boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
 
     if (can_turn_left && can_turn_right)
       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
@@ -3056,13 +3051,9 @@ void TurnRound(int x, int y)
   }
   else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE)
   {
-    if (IN_LEV_FIELD(left_x, left_y) &&
-       (IS_FREE(left_x, left_y) ||
-        (DONT_COLLIDE_WITH(element) && IS_FREE_OR_PLAYER(left_x, left_y))))
+    if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
       MovDir[x][y] = left_dir;
-    else if (!IN_LEV_FIELD(move_x, move_y) ||
-            (!IS_FREE(move_x, move_y) &&
-             (!DONT_COLLIDE_WITH(element) || !IS_FREE_OR_PLAYER(move_x, move_y))))
+    else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
       MovDir[x][y] = right_dir;
 
     if (MovDir[x][y] != old_move_dir)
@@ -3070,13 +3061,9 @@ void TurnRound(int x, int y)
   }
   else if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
   {
-    if (IN_LEV_FIELD(right_x, right_y) &&
-       (IS_FREE(right_x, right_y) ||
-        (DONT_COLLIDE_WITH(element) && IS_FREE_OR_PLAYER(right_x, right_y))))
+    if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
       MovDir[x][y] = right_dir;
-    else if (!IN_LEV_FIELD(move_x, move_y) ||
-            (!IS_FREE(move_x, move_y) &&
-             (!DONT_COLLIDE_WITH(element) || !IS_FREE_OR_PLAYER(move_x, move_y))))
+    else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
       MovDir[x][y] = left_dir;
 
     if (MovDir[x][y] != old_move_dir)
@@ -3107,7 +3094,8 @@ void TurnRound(int x, int y)
        if (!player->active)
          continue;
 
-       if (attr_x == -1 || ABS(jx-x)+ABS(jy-y) < ABS(attr_x-x)+ABS(attr_y-y))
+       if (attr_x == -1 ||
+           ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
        {
          attr_x = jx;
          attr_y = jy;
@@ -3136,20 +3124,14 @@ void TurnRound(int x, int y)
        new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
       Moving2Blocked(x, y, &newx, &newy);
 
-      if (IN_LEV_FIELD(newx, newy) && (IS_FREE(newx, newy) ||
-                                      (DONT_COLLIDE_WITH(element) &&
-                                       IS_FREE_OR_PLAYER(newx, newy)) ||
-                                      Feld[newx][newy] == EL_ACID))
+      if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
        return;
 
       MovDir[x][y] =
        new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
       Moving2Blocked(x, y, &newx, &newy);
 
-      if (IN_LEV_FIELD(newx, newy) && (IS_FREE(newx, newy) ||
-                                      (DONT_COLLIDE_WITH(element) &&
-                                       IS_FREE_OR_PLAYER(newx, newy)) ||
-                                      Feld[newx][newy] == EL_ACID))
+      if (ELEMENT_CAN_ENTER_FIELD_OR_ACID(element, newx, newy))
        return;
 
       MovDir[x][y] = old_move_dir;
@@ -3332,11 +3314,17 @@ void StartMoving(int x, int y)
       GfxAction[x][y + 1] = ACTION_ACTIVE;
 #endif
     }
+#if 1
     else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
             JustStopped[x][y])
     {
+      /*
+      printf("::: %d\n", MovDir[x][y]);
+      */
+
       Impact(x, y);
     }
+#endif
     else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
     {
       if (MovDir[x][y] == MV_NO_MOVING)
@@ -3370,7 +3358,7 @@ void StartMoving(int x, int y)
             element != EL_DX_SUPABOMB)
 #endif
 #else
-    else if ((IS_SLIPPERY(Feld[x][y + 1]) ||
+    else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
              (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
             !IS_FALLING(x, y + 1) && !JustStopped[x][y + 1] &&
             element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
@@ -3390,6 +3378,11 @@ void StartMoving(int x, int y)
 
        InitMovingField(x, y, left ? MV_LEFT : MV_RIGHT);
        started_moving = TRUE;
+
+#if 0
+       if (element == EL_BOMB)
+         printf("::: SLIP DOWN [%d]\n", FrameCounter);
+#endif
       }
     }
     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
@@ -3797,6 +3790,17 @@ void ContinueMoving(int x, int y)
   int horiz_move = (dx != 0);
   int newx = x + dx, newy = y + dy;
   int step = (horiz_move ? dx : dy) * TILEX / MOVE_DELAY_NORMAL_SPEED;
+  struct PlayerInfo *player = (IS_PLAYER(x, y) ? PLAYERINFO(x, y) : NULL);
+#if 0
+  boolean pushing = (player != NULL && player->Pushing && player->MovPos != 0);
+#else
+  boolean pushing = (player != NULL && player->Pushing && player->is_moving);
+#endif
+
+#if 0
+  if (player && player->is_moving && player->MovPos == 0)
+    printf("::: !!!\n");
+#endif
 
   if (element == EL_AMOEBA_DROP || element == EL_AMOEBA_DROPPING)
     step /= 2;
@@ -3823,6 +3827,26 @@ void ContinueMoving(int x, int y)
 
   MovPos[x][y] += step;
 
+#if 1
+  if (pushing)         /* special case: moving object pushed by player */
+#if 1
+    MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
+#else
+    MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->GfxPos));
+#endif
+#endif
+
+#if 0
+  if (element == EL_SPRING)
+    printf("::: spring moves %d [%d: %d, %d, %d/%d]\n",
+          MovPos[x][y],
+          pushing,
+          (player?player->Pushing:-42),
+          (player?player->is_moving:-42),
+          (player?player->MovPos:-42),
+          (player?player->GfxPos:-42));
+#endif
+
   if (ABS(MovPos[x][y]) >= TILEX)      /* object reached its destination */
   {
     Feld[x][y] = EL_EMPTY;
@@ -3936,8 +3960,14 @@ void ContinueMoving(int x, int y)
     DrawLevelField(x, y);
     DrawLevelField(newx, newy);
 
-    Stop[newx][newy] = TRUE;
-    JustStopped[newx][newy] = 3;
+#if 0
+    if (game.engine_version >= RELEASE_IDENT(2,2,0,7) || !pushing)
+#endif
+      Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
+#if 1
+    if (!pushing)
+#endif
+      JustStopped[newx][newy] = 3;
 
     if (DONT_TOUCH(element))   /* object may be nasty to player or others */
     {
@@ -4848,10 +4878,12 @@ static void ChangeActiveTrap(int x, int y)
     DrawLevelFieldCrumbledSand(x, y);
 }
 
-static void ChangeElementDoIt(int x, int y, int element)
+static void ChangeElementDoIt(int x, int y, int element_new)
 {
+  CheckTriggeredElementChange(Feld[x][y], CE_OTHER_CHANGING);
+
   RemoveField(x, y);
-  Feld[x][y] = element;
+  Feld[x][y] = element_new;
 
   ResetGfxAnimation(x, y);
   ResetRandomAnimationValue(x, y);
@@ -4957,7 +4989,7 @@ static void CheckTriggeredElementChange(int trigger_element, int trigger_event)
 
   for (i=0; i<MAX_NUM_ELEMENTS; i++)
   {
-    if (!HAS_CHANGE_EVENT(i, trigger_event) ||
+    if (!CAN_CHANGE(i) || !HAS_CHANGE_EVENT(i, trigger_event) ||
        element_info[i].change.trigger != trigger_element)
       continue;
 
@@ -4972,6 +5004,20 @@ static void CheckTriggeredElementChange(int trigger_element, int trigger_event)
   }
 }
 
+static void CheckPlayerElementChange(int x, int y, int element,
+                                    int trigger_event)
+{
+  if (!CAN_CHANGE(element) || !HAS_CHANGE_EVENT(element, trigger_event))
+    return;
+
+#if 1
+  ChangeDelay[x][y] = 1;
+  ChangeElement(x, y);
+#else
+  ChangeElementDoIt(x, y, element_info[element].change.successor);
+#endif
+}
+
 static void PlayerActions(struct PlayerInfo *player, byte player_action)
 {
   static byte stored_player_action[MAX_PLAYERS];
@@ -5138,6 +5184,30 @@ void GameActions()
     stored_player[i].Frame++;
 #endif
 
+#if 1
+  if (game.engine_version < RELEASE_IDENT(2,2,0,7))
+  {
+    for (i=0; i<MAX_PLAYERS; i++)
+    {
+      struct PlayerInfo *player = &stored_player[i];
+      int x = player->jx;
+      int y = player->jy;
+
+      if (player->active && player->Pushing && player->is_moving &&
+         IS_MOVING(x, y))
+      {
+       ContinueMoving(x, y);
+
+       /* continue moving after pushing (this is actually a bug) */
+       if (!IS_MOVING(x, y))
+       {
+         Stop[x][y] = FALSE;
+       }
+      }
+    }
+  }
+#endif
+
   for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
   {
     Stop[x][y] = FALSE;
@@ -6301,6 +6371,7 @@ static boolean checkDiagonalPushing(struct PlayerInfo *player,
 int DigField(struct PlayerInfo *player,
             int x, int y, int real_dx, int real_dy, int mode)
 {
+  boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0));
   int jx = player->jx, jy = player->jy;
   int dx = x - jx, dy = y - jy;
   int move_direction = (dx == -1 ? MV_LEFT :
@@ -6327,7 +6398,14 @@ int DigField(struct PlayerInfo *player,
   }
 
   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
+  {
+#if 0
+    if (FrameCounter == 437)
+      printf("::: ---> IS_MOVING %d\n", MovDir[x][y]);
+#endif
+
     return MF_NO_ACTION;
+  }
 
 #if 0
   if (IS_TUBE(Feld[jx][jy]) || IS_TUBE(Back[jx][jy]))
@@ -6375,7 +6453,13 @@ int DigField(struct PlayerInfo *player,
 
   switch (element)
   {
+#if 0
     case EL_EMPTY:
+      PlaySoundLevelElementAction(x, y, player->element_nr, ACTION_MOVING);
+      break;
+#endif
+
+#if 0
     case EL_SAND:
     case EL_INVISIBLE_SAND:
     case EL_INVISIBLE_SAND_ACTIVE:
@@ -6384,15 +6468,16 @@ int DigField(struct PlayerInfo *player,
     case EL_SP_BUGGY_BASE:
     case EL_SP_BUGGY_BASE_ACTIVATING:
       RemoveField(x, y);
-#if 1
+
       if (mode != DF_SNAP && element != EL_EMPTY)
       {
        GfxElement[x][y] = (CAN_BE_CRUMBLED(element) ? EL_SAND : element);
        player->is_digging = TRUE;
       }
-#endif
+
       PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING);
       break;
+#endif
 
     case EL_EMERALD:
     case EL_BD_DIAMOND:
@@ -6404,13 +6489,13 @@ int DigField(struct PlayerInfo *player,
     case EL_PEARL:
     case EL_CRYSTAL:
       RemoveField(x, y);
-#if 1
+
       if (mode != DF_SNAP)
       {
        GfxElement[x][y] = element;
        player->is_collecting = TRUE;
       }
-#endif
+
       local_player->gems_still_needed -= (element == EL_DIAMOND ? 3 :
                                          element == EL_PEARL ? 5 :
                                          element == EL_CRYSTAL ? 8 : 1);
@@ -6434,6 +6519,7 @@ int DigField(struct PlayerInfo *player,
       CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
 
+#if 0
     case EL_ENVELOPE:
       Feld[x][y] = EL_EMPTY;
 #if 1
@@ -6443,6 +6529,7 @@ int DigField(struct PlayerInfo *player,
 #endif
       CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
+#endif
 
     case EL_EXTRA_TIME:
       RemoveField(x, y);
@@ -6698,6 +6785,10 @@ int DigField(struct PlayerInfo *player,
       if (dy)
        return MF_NO_ACTION;
 
+      if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
+         !(element == EL_SPRING && use_spring_bug))
+       return MF_NO_ACTION;
+
       player->Pushing = TRUE;
 
 #if 0
@@ -6712,8 +6803,14 @@ int DigField(struct PlayerInfo *player,
       if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
        return MF_NO_ACTION;
 
+
       if (player->push_delay == 0)
        player->push_delay = FrameCounter;
+
+#if 0
+      printf("want push... %d [%d]\n", FrameCounter, player->push_delay_value);
+#endif
+
 #if 0
       if (!FrameReached(&player->push_delay, player->push_delay_value) &&
          !tape.playing &&
@@ -6733,20 +6830,37 @@ int DigField(struct PlayerInfo *player,
       }
       else
       {
+#if 1
+       InitMovingField(x, y, (dx < 0 ? MV_LEFT :
+                              dx > 0 ? MV_RIGHT :
+                              dy < 0 ? MV_UP : MV_DOWN));
+       MovPos[x][y] = (dx != 0 ? dx : dy);
+#else
        RemoveField(x, y);
        Feld[x + dx][y + dy] = element;
+#endif
       }
 
+#if 0
+      printf("pushing %d/%d ... %d [%d]\n", dx, dy,
+            FrameCounter, player->push_delay_value);
+#endif
+
+#if 0
       if (element == EL_SPRING)
       {
        Feld[x + dx][y + dy] = EL_SPRING;
        MovDir[x + dx][y + dy] = move_direction;
       }
+#endif
 
       player->push_delay_value = (element == EL_SPRING ? 0 : 2 + RND(8));
 
       DrawLevelField(x + dx, y + dy);
       PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
+
+      CheckTriggeredElementChange(element, CE_OTHER_PUSHING);
+
       break;
 
     case EL_GATE_1:
@@ -6933,8 +7047,10 @@ int DigField(struct PlayerInfo *player,
       return MF_ACTION;
       break;
 
+#if 0
     case EL_SOKOBAN_FIELD_EMPTY:
       break;
+#endif
 
     case EL_SOKOBAN_OBJECT:
     case EL_SOKOBAN_FIELD_FULL:
@@ -7032,6 +7148,8 @@ int DigField(struct PlayerInfo *player,
        PlaySoundLevel(x, y, SND_GAME_SOKOBAN_SOLVING);
       }
 
+      CheckTriggeredElementChange(element, CE_OTHER_PUSHING);
+
       break;
 
     case EL_PENGUIN:
@@ -7043,19 +7161,20 @@ int DigField(struct PlayerInfo *player,
 
       if (IS_WALKABLE(element))
       {
+       PlaySoundLevelElementAction(x, y, player->element_nr, ACTION_MOVING);
        break;
       }
       else if (IS_DIGGABLE(element))
       {
        RemoveField(x, y);
-#if 1
+
        if (mode != DF_SNAP)
        {
          GfxElement[x][y] =
            (CAN_BE_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
          player->is_digging = TRUE;
        }
-#endif
+
        PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING);
 
        break;
@@ -7063,13 +7182,15 @@ int DigField(struct PlayerInfo *player,
       else if (IS_COLLECTIBLE(element))
       {
        RemoveField(x, y);
-#if 1
+
        if (mode != DF_SNAP)
        {
          GfxElement[x][y] = element;
          player->is_collecting = TRUE;
        }
-#endif
+
+       RaiseScoreElement(element);
+
        PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
 
        CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
@@ -7084,6 +7205,9 @@ int DigField(struct PlayerInfo *player,
        if (CAN_FALL(element) && dy)
          return MF_NO_ACTION;
 
+       if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))
+         return MF_NO_ACTION;
+
        if (!player->Pushing &&
            game.engine_version >= RELEASE_IDENT(2,2,0,7))
          player->push_delay_value = GET_NEW_PUSH_DELAY(element);
@@ -7103,8 +7227,15 @@ int DigField(struct PlayerInfo *player,
            !(tape.playing && tape.file_version < FILE_VERSION_2_0))
          return MF_NO_ACTION;
 
+#if 1
+       InitMovingField(x, y, (dx < 0 ? MV_LEFT :
+                              dx > 0 ? MV_RIGHT :
+                              dy < 0 ? MV_UP : MV_DOWN));
+       MovPos[x][y] = (dx != 0 ? dx : dy);
+#else
        RemoveField(x, y);
        Feld[x + dx][y + dy] = element;
+#endif
 
 #if 1
        if (game.engine_version < RELEASE_IDENT(2,2,0,7))
@@ -7116,8 +7247,19 @@ int DigField(struct PlayerInfo *player,
        DrawLevelField(x + dx, y + dy);
        PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
 
+       CheckTriggeredElementChange(element, CE_OTHER_PUSHING);
+#if 1
+       CheckPlayerElementChange(x, y, element, CE_PUSHED_BY_PLAYER);
+#else
+       CheckPlayerElementChange(x + dx, y + dy, element, CE_PUSHED_BY_PLAYER);
+#endif
+
        break;
       }
+      else
+      {
+       CheckPlayerElementChange(x, y, element, CE_PRESSED_BY_PLAYER);
+      }
 
       return MF_NO_ACTION;
   }
@@ -7425,6 +7567,7 @@ void RaiseScoreElement(int element)
       RaiseScore(level.score[SC_KEY]);
       break;
     default:
+      RaiseScore(element_info[element].score);
       break;
   }
 }