rnd-20030620-1-src
[rocksndiamonds.git] / src / game.c
index cd8e534197d5bb95bad9bce7a6663f8eb03c9bf7..88f2c53c32bbd122c54a991945d68e413a513040 100644 (file)
 
 #define        INIT_GFX_RANDOM()       (SimpleRND(1000000))
 
+#define GET_NEW_PUSH_DELAY(e)  (   (element_info[e].push_delay_fixed) + \
+                                RND(element_info[e].push_delay_random))
+#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
@@ -109,6 +163,10 @@ 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);
 static void PlaySoundLevelNearest(int, int, int);
 static void PlaySoundLevelAction(int, int, int);
@@ -295,8 +353,13 @@ static struct ChangingElementInfo changing_element_list[] =
 };
 
 static struct ChangingElementInfo changing_element[MAX_NUM_ELEMENTS];
+static unsigned long trigger_events[MAX_NUM_ELEMENTS];
 
 #define IS_AUTO_CHANGING(e)  (changing_element[e].base_element != EL_UNDEFINED)
+#define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
+#define IS_CHANGING(x, y)      (IS_AUTO_CHANGING(Feld[x][y]) || \
+                                IS_JUST_CHANGING(x, y))
+#define TRIGGERS_BY_COLLECTING(e) (trigger_events[e] & CE_OTHER_COLLECTING)
 
 
 void GetPlayerConfig()
@@ -372,7 +435,9 @@ static int getBeltDirFromBeltSwitchElement(int element)
 
 static void InitField(int x, int y, boolean init_game)
 {
-  switch (Feld[x][y])
+  int element = Feld[x][y];
+
+  switch (element)
   {
     case EL_SP_MURPHY:
       if (init_game)
@@ -569,6 +634,8 @@ static void InitField(int x, int y, boolean init_game)
       break;
 
     default:
+      if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
+       InitMovDir(x, y);
       break;
   }
 }
@@ -630,32 +697,6 @@ static void InitGameEngine()
   game.initial_move_delay_value =
     (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
 
-#if 0
-  /* dynamically adjust element properties according to game engine version */
-  {
-    static int ep_em_slippery_wall[] =
-    {
-      EL_STEELWALL,
-      EL_WALL,
-      EL_EXPANDABLE_WALL,
-      EL_EXPANDABLE_WALL_HORIZONTAL,
-      EL_EXPANDABLE_WALL_VERTICAL,
-      EL_EXPANDABLE_WALL_ANY
-    };
-    static int ep_em_slippery_wall_num = SIZEOF_ARRAY_INT(ep_em_slippery_wall);
-
-    /* special EM style gems behaviour */
-    for (i=0; i<ep_em_slippery_wall_num; i++)
-      SET_PROPERTY(ep_em_slippery_wall[i], EP_EM_SLIPPERY_WALL,
-                  level.em_slippery_gems);
-
-    /* "EL_EXPANDABLE_WALL_GROWING" wasn't slippery for EM gems in 2.0.1 */
-    SET_PROPERTY(EL_EXPANDABLE_WALL_GROWING, EP_EM_SLIPPERY_WALL,
-                (level.em_slippery_gems &&
-                 game.engine_version > VERSION_IDENT(2,0,1)));
-  }
-#endif
-
   /* initialize changing elements information */
   for (i=0; i<MAX_NUM_ELEMENTS; i++)
   {
@@ -687,26 +728,28 @@ static void InitGameEngine()
   /* add changing elements from custom element configuration */
   for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
   {
-    struct CustomElementChangeInfo *change = &level.custom_element[i].change;
     int element = EL_CUSTOM_START + i;
+    struct ElementChangeInfo *change = &element_info[element].change;
 
     /* only add custom elements that change after fixed/random frame delay */
-    if (!IS_CHANGEABLE(element) ||
-       (!HAS_CHANGE_EVENT(element, CE_DELAY_FIXED) &&
-        !HAS_CHANGE_EVENT(element, CE_DELAY_RANDOM)))
+    if (!CAN_CHANGE(element) || !HAS_CHANGE_EVENT(element, CE_DELAY))
       continue;
 
     changing_element[element].base_element = element;
     changing_element[element].next_element = change->successor;
-    changing_element[element].change_delay = 0;
+    changing_element[element].change_delay = (change->delay_fixed *
+                                             change->delay_frames);
+  }
 
-    if (HAS_CHANGE_EVENT(element, CE_DELAY_FIXED))
-      changing_element[element].change_delay +=
-       change->delay_fixed * change->delay_frames;
+  /* initialize trigger events information */
+  for (i=0; i<MAX_NUM_ELEMENTS; i++)
+    trigger_events[i] = EP_BITMASK_DEFAULT;
 
-    if (HAS_CHANGE_EVENT(element, CE_DELAY_RANDOM));
-    /* random frame delay added at runtime for each element individually */
-  }
+  /* add trigger events from element change event properties */
+  for (i=0; i<MAX_NUM_ELEMENTS; i++)
+    if (HAS_CHANGE_EVENT(i, CE_BY_OTHER))
+      trigger_events[element_info[i].change.trigger] |=
+       element_info[i].change.events;
 }
 
 
@@ -785,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;
@@ -836,7 +878,7 @@ void InitGame()
 
   AllPlayersGone = FALSE;
 
-  game.yam_content_nr = 0;
+  game.yamyam_content_nr = 0;
   game.magic_wall_active = FALSE;
   game.magic_wall_time_left = 0;
   game.light_time_left = 0;
@@ -860,6 +902,7 @@ void InitGame()
     {
       Feld[x][y] = Ur[x][y];
       MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
+      ChangeDelay[x][y] = 0;
       Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
       AmoebaNr[x][y] = 0;
       JustStopped[x][y] = 0;
@@ -1164,30 +1207,66 @@ void InitMovDir(int x, int y)
       break;
 
     default:
-      MovDir[x][y] = 1 << RND(4);
-      if (element != EL_BUG &&
-         element != EL_SPACESHIP &&
-         element != EL_BD_BUTTERFLY &&
-         element != EL_BD_FIREFLY)
-       break;
-
-      for (i=0; i<4; i++)
+      if (IS_CUSTOM_ELEMENT(element))
       {
-       int x1 = x + xy[i][0];
-       int y1 = y + xy[i][1];
-
-       if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
+       if (element_info[element].move_direction_initial != MV_NO_MOVING)
+         MovDir[x][y] = element_info[element].move_direction_initial;
+       else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS)
+         MovDir[x][y] = 1 << RND(4);
+       else if (element_info[element].move_pattern == MV_HORIZONTAL)
+         MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
+       else if (element_info[element].move_pattern == MV_VERTICAL)
+         MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
+       else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
+         MovDir[x][y] = element_info[element].move_pattern;
+       else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE ||
+                element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
        {
-         if (element == EL_BUG || element == EL_BD_BUTTERFLY)
+         for (i=0; i<4; i++)
          {
-           MovDir[x][y] = direction[0][i];
-           break;
+           int x1 = x + xy[i][0];
+           int y1 = y + xy[i][1];
+
+           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
+           {
+             if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
+               MovDir[x][y] = direction[0][i];
+             else
+               MovDir[x][y] = direction[1][i];
+
+             break;
+           }
          }
-         else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
-                  element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
+       }                
+      }
+      else
+      {
+       MovDir[x][y] = 1 << RND(4);
+
+       if (element != EL_BUG &&
+           element != EL_SPACESHIP &&
+           element != EL_BD_BUTTERFLY &&
+           element != EL_BD_FIREFLY)
+         break;
+
+       for (i=0; i<4; i++)
+       {
+         int x1 = x + xy[i][0];
+         int y1 = y + xy[i][1];
+
+         if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
          {
-           MovDir[x][y] = direction[1][i];
-           break;
+           if (element == EL_BUG || element == EL_BD_BUTTERFLY)
+           {
+             MovDir[x][y] = direction[0][i];
+             break;
+           }
+           else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
+                    element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
+           {
+             MovDir[x][y] = direction[1][i];
+             break;
+           }
          }
        }
       }
@@ -1425,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);
@@ -1519,6 +1600,7 @@ static void RemoveField(int x, int y)
   MovPos[x][y] = 0;
   MovDir[x][y] = 0;
   MovDelay[x][y] = 0;
+  ChangeDelay[x][y] = 0;
 }
 
 void RemoveMovingField(int x, int y)
@@ -1555,6 +1637,7 @@ void RemoveMovingField(int x, int y)
   Feld[newx][newy] = EL_EMPTY;
   MovPos[oldx][oldy] = MovDir[oldx][oldy] = MovDelay[oldx][oldy] = 0;
   MovPos[newx][newy] = MovDir[newx][newy] = MovDelay[newx][newy] = 0;
+  ChangeDelay[oldx][oldy] = ChangeDelay[newx][newy] = 0;
   GfxAction[oldx][oldy] = GfxAction[newx][newy] = ACTION_DEFAULT;
 
   DrawLevelField(oldx, oldy);
@@ -1654,7 +1737,7 @@ void Explode(int ex, int ey, int phase, int mode)
       Feld[ex][ey] = center_element;
     }
 
-    for (y=ey-1; y<=ey+1; y++) for(x=ex-1; x<=ex+1; x++)
+    for (y = ey - 1; y <= ey + 1; y++) for(x = ex - 1; x <= ex + 1; x++)
     {
       int element;
 
@@ -1734,10 +1817,14 @@ void Explode(int ex, int ey, int phase, int mode)
        Store[x][y] = EL_BD_DIAMOND;
       else if (center_element == EL_SP_ELECTRON)
        Store[x][y] = EL_SP_INFOTRON;
-      else if (center_element == EL_YAMYAM)
-       Store[x][y] = level.yam_content[game.yam_content_nr][x-ex+1][y-ey+1];
       else if (center_element == EL_AMOEBA_TO_DIAMOND)
        Store[x][y] = level.amoeba_content;
+      else if (center_element == EL_YAMYAM)
+       Store[x][y] =
+         level.yamyam_content[game.yamyam_content_nr][x - ex + 1][y - ey + 1];
+      else if (IS_CUSTOM_ELEMENT(center_element))
+       Store[x][y] =
+         element_info[center_element].content[x - ex + 1][y - ey + 1];
       else if (element == EL_WALL_EMERALD)
        Store[x][y] = EL_EMERALD;
       else if (element == EL_WALL_DIAMOND)
@@ -1771,7 +1858,11 @@ void Explode(int ex, int ey, int phase, int mode)
       }
 
       Feld[x][y] = EL_EXPLOSION;
+#if 1
+      GfxElement[x][y] = center_element;
+#else
       GfxElement[x][y] = EL_UNDEFINED;
+#endif
       MovDir[x][y] = MovPos[x][y] = 0;
       AmoebaNr[x][y] = 0;
       ExplodePhase[x][y] = 1;
@@ -1779,7 +1870,8 @@ void Explode(int ex, int ey, int phase, int mode)
     }
 
     if (center_element == EL_YAMYAM)
-      game.yam_content_nr = (game.yam_content_nr + 1) % level.num_yam_contents;
+      game.yamyam_content_nr =
+       (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
 
     return;
   }
@@ -1809,7 +1901,7 @@ void Explode(int ex, int ey, int phase, int mode)
 
     if (IS_PLAYER(x, y))
       KillHeroUnlessProtected(x, y);
-    else if (IS_CAN_EXPLODE(element))
+    else if (CAN_EXPLODE_BY_FIRE(element))
     {
       Feld[x][y] = Store2[x][y];
       Store2[x][y] = 0;
@@ -1825,12 +1917,13 @@ void Explode(int ex, int ey, int phase, int mode)
 
     element = Feld[x][y] = Store[x][y];
     Store[x][y] = Store2[x][y] = 0;
+    GfxElement[x][y] = EL_UNDEFINED;
 
     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
       element = Feld[x][y] = Back[x][y];
     Back[x][y] = 0;
 
-    MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
+    MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = ChangeDelay[x][y] = 0;
     InitField(x, y, FALSE);
     if (CAN_MOVE(element))
       InitMovDir(x, y);
@@ -1841,10 +1934,14 @@ void Explode(int ex, int ey, int phase, int mode)
   }
   else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
   {
+#if 1
+    int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
+#else
     int stored = Store[x][y];
     int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
                   stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
                   IMG_SP_EXPLOSION);
+#endif
     int frame = getGraphicAnimationFrame(graphic, phase - delay);
 
     if (phase == delay)
@@ -1921,10 +2018,22 @@ void Bang(int x, int y)
 {
   int element = Feld[x][y];
 
+  if (IS_PLAYER(x, y))
+  {
+    struct PlayerInfo *player = PLAYERINFO(x, y);
+
+    element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
+                           player->element_nr);
+  }
+
+#if 1
+  PlaySoundLevelAction(x, y, ACTION_EXPLODING);
+#else
   if (game.emulation == EMU_SUPAPLEX)
     PlaySoundLevel(x, y, SND_SP_ELEMENT_EXPLODING);
   else
     PlaySoundLevel(x, y, SND_ELEMENT_EXPLODING);
+#endif
 
 #if 0
   if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
@@ -1966,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)
@@ -2300,19 +2411,22 @@ void Impact(int x, int y)
 {
   boolean lastline = (y == lev_fieldy-1);
   boolean object_hit = FALSE;
+  boolean impact = (lastline || object_hit);
   int element = Feld[x][y];
-  int smashed = 0;
+  int smashed = EL_UNDEFINED;
 
   if (!lastline)       /* check if element below was hit */
   {
-    if (Feld[x][y+1] == EL_PLAYER_IS_LEAVING)
+    if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
       return;
 
-    object_hit = (!IS_FREE(x, y+1) && (!IS_MOVING(x, y+1) ||
-                                     MovDir[x][y+1] != MV_DOWN ||
-                                     MovPos[x][y+1] <= TILEY / 2));
+    object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
+                                        MovDir[x][y + 1] != MV_DOWN ||
+                                        MovPos[x][y + 1] <= TILEY / 2));
     if (object_hit)
-      smashed = MovingOrBlocked2Element(x, y+1);
+      smashed = MovingOrBlocked2Element(x, y + 1);
+
+    impact = (lastline || object_hit);
   }
 
   if (!lastline && smashed == EL_ACID) /* element falls into acid */
@@ -2321,33 +2435,39 @@ void Impact(int x, int y)
     return;
   }
 
-  if (lastline || object_hit)
+  if (impact)
   {
     ResetGfxAnimation(x, y);
     DrawLevelField(x, y);
   }
 
-  if ((element == EL_BOMB ||
-       element == EL_SP_DISK_ORANGE ||
-       element == EL_DX_SUPABOMB) &&
-      (lastline || object_hit))                /* element is bomb */
+  if (impact && CAN_EXPLODE_IMPACT(element))
   {
     Bang(x, y);
     return;
   }
-  else if (element == EL_PEARL)
+  else if (impact && element == EL_PEARL)
   {
     Feld[x][y] = EL_PEARL_BREAKING;
     PlaySoundLevel(x, y, SND_PEARL_BREAKING);
     return;
   }
+  else if (impact && CAN_CHANGE(element) &&
+          HAS_CHANGE_EVENT(element, CE_IMPACT))
+  {
+    PlaySoundLevelElementAction(x, y, element, ACTION_IMPACT);
+
+    ChangeElementDoIt(x, y, element_info[element].change.successor);
+
+    return;
+  }
 
-  if (element == EL_AMOEBA_DROP && (lastline || object_hit))
+  if (impact && element == EL_AMOEBA_DROP)
   {
-    if (object_hit && IS_PLAYER(x, y+1))
-      KillHeroUnlessProtected(x, y+1);
+    if (object_hit && IS_PLAYER(x, y + 1))
+      KillHeroUnlessProtected(x, y + 1);
     else if (object_hit && smashed == EL_PENGUIN)
-      Bang(x, y+1);
+      Bang(x, y + 1);
     else
     {
       Feld[x][y] = EL_AMOEBA_GROWING;
@@ -2358,7 +2478,7 @@ void Impact(int x, int y)
     return;
   }
 
-  if (!lastline && object_hit)         /* check which object was hit */
+  if (object_hit)              /* check which object was hit */
   {
     if (CAN_PASS_MAGIC_WALL(element) && 
        (smashed == EL_MAGIC_WALL ||
@@ -2385,17 +2505,23 @@ void Impact(int x, int y)
 
     if (IS_PLAYER(x, y + 1))
     {
-      KillHeroUnlessProtected(x, y+1);
-      return;
+      if (CAN_SMASH_PLAYER(element))
+      {
+       KillHeroUnlessProtected(x, y + 1);
+       return;
+      }
     }
     else if (smashed == EL_PENGUIN)
     {
-      Bang(x, y + 1);
-      return;
+      if (CAN_SMASH_PLAYER(element))
+      {
+       Bang(x, y + 1);
+       return;
+      }
     }
     else if (element == EL_BD_DIAMOND)
     {
-      if (IS_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
+      if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
       {
        Bang(x, y + 1);
        return;
@@ -2410,23 +2536,15 @@ void Impact(int x, int y)
       Bang(x, y + 1);
       return;
     }
-    else if (element == EL_ROCK ||
-            element == EL_SP_ZONK ||
-            element == EL_BD_ROCK)
-    {
-      if (IS_ENEMY(smashed) ||
-         smashed == EL_BOMB ||
-         smashed == EL_SP_DISK_ORANGE ||
-         smashed == EL_DX_SUPABOMB ||
-         smashed == EL_SATELLITE ||
-         smashed == EL_PIG ||
-         smashed == EL_DRAGON ||
-         smashed == EL_MOLE)
+    else if (CAN_SMASH_EVERYTHING(element))
+    {
+      if (IS_CLASSIC_ENEMY(smashed) ||
+         CAN_EXPLODE_SMASHED(smashed))
       {
        Bang(x, y + 1);
        return;
       }
-      else if (!IS_MOVING(x, y + 1))
+      else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
       {
        if (smashed == EL_LAMP ||
            smashed == EL_LAMP_ACTIVE)
@@ -2436,36 +2554,41 @@ void Impact(int x, int y)
        }
        else if (smashed == EL_NUT)
        {
-         Feld[x][y+1] = EL_NUT_BREAKING;
+         Feld[x][y + 1] = EL_NUT_BREAKING;
          PlaySoundLevel(x, y, SND_NUT_BREAKING);
          RaiseScoreElement(EL_NUT);
          return;
        }
        else if (smashed == EL_PEARL)
        {
-         Feld[x][y+1] = EL_PEARL_BREAKING;
+         Feld[x][y + 1] = EL_PEARL_BREAKING;
          PlaySoundLevel(x, y, SND_PEARL_BREAKING);
          return;
        }
        else if (smashed == EL_DIAMOND)
        {
-         Feld[x][y+1] = EL_EMPTY;
+         Feld[x][y + 1] = EL_DIAMOND_BREAKING;
          PlaySoundLevel(x, y, SND_DIAMOND_BREAKING);
          return;
        }
        else if (IS_BELT_SWITCH(smashed))
        {
-         ToggleBeltSwitch(x, y+1);
+         ToggleBeltSwitch(x, y + 1);
        }
        else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
                 smashed == EL_SWITCHGATE_SWITCH_DOWN)
        {
-         ToggleSwitchgateSwitch(x, y+1);
+         ToggleSwitchgateSwitch(x, y + 1);
        }
        else if (smashed == EL_LIGHT_SWITCH ||
                 smashed == EL_LIGHT_SWITCH_ACTIVE)
        {
-         ToggleLightSwitch(x, y+1);
+         ToggleLightSwitch(x, y + 1);
+       }
+       else if (CAN_CHANGE(smashed) &&
+                HAS_CHANGE_EVENT(smashed, CE_SMASHED))
+       {
+         ChangeElementDoIt(x, y + 1, element_info[smashed].change.successor);
        }
       }
     }
@@ -2473,12 +2596,12 @@ void Impact(int x, int y)
 
   /* play sound of magic wall / mill */
   if (!lastline &&
-      (Feld[x][y+1] == EL_MAGIC_WALL_ACTIVE ||
-       Feld[x][y+1] == EL_BD_MAGIC_WALL_ACTIVE))
+      (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
+       Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
   {
-    if (Feld[x][y+1] == EL_MAGIC_WALL_ACTIVE)
+    if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
       PlaySoundLevel(x, y, SND_MAGIC_WALL_FILLING);
-    else if (Feld[x][y+1] == EL_BD_MAGIC_WALL_ACTIVE)
+    else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
       PlaySoundLevel(x, y, SND_BD_MAGIC_WALL_FILLING);
 
     return;
@@ -2496,26 +2619,28 @@ void TurnRound(int x, int y)
     int x, y;
   } move_xy[] =
   {
-    { 0, 0 },
-    {-1, 0 },
-    {+1, 0 },
-    { 0, 0 },
-    { 0, -1 },
-    { 0, 0 }, { 0, 0 }, { 0, 0 },
-    { 0, +1 }
+    {  0,  0 },
+    { -1,  0 },
+    { +1,  0 },
+    {  0,  0 },
+    {  0, -1 },
+    {  0,  0 }, { 0, 0 }, { 0, 0 },
+    {  0, +1 }
   };
   static struct
   {
     int left, right, back;
   } turn[] =
   {
-    { 0,       0,              0 },
+    { 0,       0,              0        },
     { MV_DOWN, MV_UP,          MV_RIGHT },
-    { MV_UP,   MV_DOWN,        MV_LEFT },
-    { 0,       0,              0 },
-    { MV_LEFT, MV_RIGHT,       MV_DOWN },
-    { 0,0,0 }, { 0,0,0 },      { 0,0,0 },
-    { MV_RIGHT,        MV_LEFT,        MV_UP }
+    { MV_UP,   MV_DOWN,        MV_LEFT  },
+    { 0,       0,              0        },
+    { MV_LEFT, MV_RIGHT,       MV_DOWN  },
+    { 0,       0,              0        },
+    { 0,       0,              0        },
+    { 0,       0,              0        },
+    { MV_RIGHT,        MV_LEFT,        MV_UP    }
   };
 
   int element = Feld[x][y];
@@ -2533,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)
@@ -2554,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) */
@@ -2570,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);
@@ -2590,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);
@@ -2614,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);
@@ -2638,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)
@@ -2695,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);
@@ -2777,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;
@@ -2807,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;
@@ -2834,8 +2922,8 @@ void TurnRound(int x, int y)
 
       for (i=0; i<4; i++)
       {
-       int ex = x + xy[i%4][0];
-       int ey = y + xy[i%4][1];
+       int ex = x + xy[i % 4][0];
+       int ey = y + xy[i % 4][1];
 
        if (IN_LEV_FIELD(ex, ey) && Feld[ex][ey] == EL_EXIT_OPEN)
        {
@@ -2847,67 +2935,207 @@ void TurnRound(int x, int y)
     }
 
     MovDir[x][y] = MV_NO_MOVING;
-    if (attr_x<x)
+    if (attr_x < x)
       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
-    else if (attr_x>x)
+    else if (attr_x > x)
       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
-    if (attr_y<y)
+    if (attr_y < y)
       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
-    else if (attr_y>y)
+    else if (attr_y > y)
       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
 
     if (element == EL_ROBOT)
     {
       int newx, newy;
 
-      if ((MovDir[x][y]&(MV_LEFT|MV_RIGHT)) && (MovDir[x][y]&(MV_UP|MV_DOWN)))
-       MovDir[x][y] &= (RND(2) ? (MV_LEFT|MV_RIGHT) : (MV_UP|MV_DOWN));
+      if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
+       MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
       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;
 
       MovDelay[x][y] = 1;
 
-      if ((MovDir[x][y]&(MV_LEFT|MV_RIGHT)) && (MovDir[x][y]&(MV_UP|MV_DOWN)))
+      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_LEFT|MV_RIGHT) : (MV_UP|MV_DOWN));
+         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_LEFT|MV_RIGHT) : (MV_UP|MV_DOWN));
+         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;
+       return;
+      }
+    }
+  }
+  else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS)
+  {
+    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);
+    else if (can_turn_left)
+      MovDir[x][y] = (RND(2) ? left_dir : back_dir);
+    else if (can_turn_right)
+      MovDir[x][y] = (RND(2) ? right_dir : back_dir);
+    else
+      MovDir[x][y] = back_dir;
+
+    MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
+  }
+  else if (element_info[element].move_pattern == MV_HORIZONTAL ||
+          element_info[element].move_pattern == MV_VERTICAL)
+  {
+    if (element_info[element].move_pattern & old_move_dir)
+      MovDir[x][y] = back_dir;
+    else if (element_info[element].move_pattern == MV_HORIZONTAL)
+      MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
+    else if (element_info[element].move_pattern == MV_VERTICAL)
+      MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
+
+    MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
+  }
+  else if (element_info[element].move_pattern & MV_ANY_DIRECTION)
+  {
+    MovDir[x][y] = element_info[element].move_pattern;
+    MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
+  }
+  else if (element_info[element].move_pattern == MV_ALONG_LEFT_SIDE)
+  {
+    if (ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
+      MovDir[x][y] = left_dir;
+    else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
+      MovDir[x][y] = right_dir;
+
+    if (MovDir[x][y] != old_move_dir)
+      MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
+  }
+  else if (element_info[element].move_pattern == MV_ALONG_RIGHT_SIDE)
+  {
+    if (ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
+      MovDir[x][y] = right_dir;
+    else if (!ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
+      MovDir[x][y] = left_dir;
+
+    if (MovDir[x][y] != old_move_dir)
+      MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
+  }
+  else if (element_info[element].move_pattern == MV_TOWARDS_PLAYER ||
+          element_info[element].move_pattern == MV_AWAY_FROM_PLAYER)
+  {
+    int attr_x = -1, attr_y = -1;
+    int newx, newy;
+    boolean move_away =
+      (element_info[element].move_pattern == MV_AWAY_FROM_PLAYER);
+
+    if (AllPlayersGone)
+    {
+      attr_x = ExitX;
+      attr_y = ExitY;
+    }
+    else
+    {
+      int i;
+
+      for (i=0; i<MAX_PLAYERS; i++)
+      {
+       struct PlayerInfo *player = &stored_player[i];
+       int jx = player->jx, jy = player->jy;
+
+       if (!player->active)
+         continue;
+
+       if (attr_x == -1 ||
+           ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
+       {
+         attr_x = jx;
+         attr_y = jy;
+       }
+      }
+    }
+
+    MovDir[x][y] = MV_NO_MOVING;
+    if (attr_x < x)
+      MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
+    else if (attr_x > x)
+      MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
+    if (attr_y < y)
+      MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
+    else if (attr_y > y)
+      MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
+
+    MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
+
+    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(element, 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(element, newx, newy))
+       return;
+
+      MovDir[x][y] = old_move_dir;
+    }
   }
 }
 
@@ -2951,7 +3179,7 @@ void StartMoving(int x, int y)
 
     if (element == EL_QUICKSAND_FULL)
     {
-      if (IS_FREE(x, y+1))
+      if (IS_FREE(x, y + 1))
       {
        InitMovingField(x, y, MV_DOWN);
        started_moving = TRUE;
@@ -2964,7 +3192,7 @@ void StartMoving(int x, int y)
        PlaySoundLevel(x, y, SND_QUICKSAND_EMPTYING);
 #endif
       }
-      else if (Feld[x][y+1] == EL_QUICKSAND_EMPTY)
+      else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
       {
        if (!MovDelay[x][y])
          MovDelay[x][y] = TILEY + 1;
@@ -2977,8 +3205,8 @@ void StartMoving(int x, int y)
        }
 
        Feld[x][y] = EL_QUICKSAND_EMPTY;
-       Feld[x][y+1] = EL_QUICKSAND_FULL;
-       Store[x][y+1] = Store[x][y];
+       Feld[x][y + 1] = EL_QUICKSAND_FULL;
+       Store[x][y + 1] = Store[x][y];
        Store[x][y] = 0;
 #if 1
        PlaySoundLevelAction(x, y, ACTION_FILLING);
@@ -2988,7 +3216,7 @@ void StartMoving(int x, int y)
       }
     }
     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
-            Feld[x][y+1] == EL_QUICKSAND_EMPTY)
+            Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
     {
       InitMovingField(x, y, MV_DOWN);
       started_moving = TRUE;
@@ -3003,7 +3231,7 @@ void StartMoving(int x, int y)
     }
     else if (element == EL_MAGIC_WALL_FULL)
     {
-      if (IS_FREE(x, y+1))
+      if (IS_FREE(x, y + 1))
       {
        InitMovingField(x, y, MV_DOWN);
        started_moving = TRUE;
@@ -3011,7 +3239,7 @@ void StartMoving(int x, int y)
        Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
        Store[x][y] = EL_CHANGED(Store[x][y]);
       }
-      else if (Feld[x][y+1] == EL_MAGIC_WALL_ACTIVE)
+      else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
       {
        if (!MovDelay[x][y])
          MovDelay[x][y] = TILEY/4 + 1;
@@ -3024,14 +3252,14 @@ void StartMoving(int x, int y)
        }
 
        Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
-       Feld[x][y+1] = EL_MAGIC_WALL_FULL;
-       Store[x][y+1] = EL_CHANGED(Store[x][y]);
+       Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
+       Store[x][y + 1] = EL_CHANGED(Store[x][y]);
        Store[x][y] = 0;
       }
     }
     else if (element == EL_BD_MAGIC_WALL_FULL)
     {
-      if (IS_FREE(x, y+1))
+      if (IS_FREE(x, y + 1))
       {
        InitMovingField(x, y, MV_DOWN);
        started_moving = TRUE;
@@ -3039,7 +3267,7 @@ void StartMoving(int x, int y)
        Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
        Store[x][y] = EL_CHANGED2(Store[x][y]);
       }
-      else if (Feld[x][y+1] == EL_BD_MAGIC_WALL_ACTIVE)
+      else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
       {
        if (!MovDelay[x][y])
          MovDelay[x][y] = TILEY/4 + 1;
@@ -3052,27 +3280,27 @@ void StartMoving(int x, int y)
        }
 
        Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
-       Feld[x][y+1] = EL_BD_MAGIC_WALL_FULL;
-       Store[x][y+1] = EL_CHANGED2(Store[x][y]);
+       Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
+       Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
        Store[x][y] = 0;
       }
     }
     else if (CAN_PASS_MAGIC_WALL(element) &&
-            (Feld[x][y+1] == EL_MAGIC_WALL_ACTIVE ||
-             Feld[x][y+1] == EL_BD_MAGIC_WALL_ACTIVE))
+            (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
+             Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
     {
       InitMovingField(x, y, MV_DOWN);
       started_moving = TRUE;
 
       Feld[x][y] =
-       (Feld[x][y+1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
+       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
         EL_BD_MAGIC_WALL_FILLING);
       Store[x][y] = element;
     }
 #if 0
-    else if (CAN_SMASH(element) && Feld[x][y+1] == EL_ACID)
+    else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
 #else
-    else if (CAN_FALL(element) && Feld[x][y+1] == EL_ACID)
+    else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
 #endif
     {
       SplashAcid(x, y);
@@ -3083,15 +3311,21 @@ void StartMoving(int x, int y)
       Store[x][y] = EL_ACID;
 #if 0
       /* !!! TEST !!! better use "_FALLING" etc. !!! */
-      GfxAction[x][y+1] = ACTION_ACTIVE;
+      GfxAction[x][y + 1] = ACTION_ACTIVE;
 #endif
     }
-    else if (CAN_SMASH(element) && Feld[x][y+1] == EL_BLOCKED &&
+#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);
     }
-    else if (IS_FREE(x, y+1) && element == EL_SPRING && use_spring_bug)
+#endif
+    else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
     {
       if (MovDir[x][y] == MV_NO_MOVING)
       {
@@ -3099,7 +3333,7 @@ void StartMoving(int x, int y)
        started_moving = TRUE;
       }
     }
-    else if (IS_FREE(x, y+1))
+    else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
     {
       if (JustStopped[x][y])   /* prevent animation from being restarted */
        MovDir[x][y] = MV_DOWN;
@@ -3112,28 +3346,28 @@ void StartMoving(int x, int y)
       Feld[x][y] = EL_AMOEBA_GROWING;
       Store[x][y] = EL_AMOEBA_WET;
     }
-    /* Store[x][y+1] must be zero, because:
-       (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y+1] == EL_QUICKSAND_EMPTY
+    /* Store[x][y + 1] must be zero, because:
+       (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
     */
 #if 0
 #if OLD_GAME_BEHAVIOUR
-    else if (IS_SLIPPERY(Feld[x][y+1]) && !Store[x][y+1])
+    else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
 #else
-    else if (IS_SLIPPERY(Feld[x][y+1]) && !Store[x][y+1] &&
-            !IS_FALLING(x, y+1) && !JustStopped[x][y+1] &&
+    else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
+            !IS_FALLING(x, y + 1) && !JustStopped[x][y + 1] &&
             element != EL_DX_SUPABOMB)
 #endif
 #else
-    else if ((IS_SLIPPERY(Feld[x][y+1]) ||
-             (IS_EM_SLIPPERY_WALL(Feld[x][y+1]) && IS_GEM(element))) &&
-            !IS_FALLING(x, y+1) && !JustStopped[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)
 #endif
     {
       boolean left  = (x>0 && IS_FREE(x-1, y) &&
-                      (IS_FREE(x-1, y+1) || Feld[x-1][y+1] == EL_ACID));
+                      (IS_FREE(x-1, y + 1) || Feld[x-1][y + 1] == EL_ACID));
       boolean right = (x<lev_fieldx-1 && IS_FREE(x+1, y) &&
-                      (IS_FREE(x+1, y+1) || Feld[x+1][y+1] == EL_ACID));
+                      (IS_FREE(x+1, y + 1) || Feld[x+1][y + 1] == EL_ACID));
 
       if (left || right)
       {
@@ -3144,13 +3378,18 @@ 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]))
+    else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
     {
       boolean left_is_free  = (x>0 && IS_FREE(x-1, y));
       boolean right_is_free = (x<lev_fieldx-1 && IS_FREE(x+1, y));
-      int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y+1]);
+      int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
       int belt_dir = game.belt_dir[belt_nr];
 
       if ((belt_dir == MV_LEFT  && left_is_free) ||
@@ -3196,7 +3435,8 @@ void StartMoving(int x, int y)
 
       if (element != EL_YAMYAM &&
          element != EL_DARK_YAMYAM &&
-         element != EL_PACMAN)
+         element != EL_PACMAN &&
+         !(element_info[element].move_pattern & MV_ANY_DIRECTION))
       {
        TurnRound(x, y);
 
@@ -3213,11 +3453,36 @@ void StartMoving(int x, int y)
     {
       MovDelay[x][y]--;
 
+#if 0
+      if (element == EL_YAMYAM)
+      {
+       printf("::: %d\n",
+              el_act_dir2img(EL_YAMYAM, ACTION_WAITING, MV_LEFT));
+       DrawLevelElementAnimation(x, y, element);
+      }
+#endif
+
+      if (MovDelay[x][y])      /* element still has to wait some time */
+      {
+#if 0
+       /* !!! PLACE THIS SOMEWHERE AFTER "TurnRound()" !!! */
+       ResetGfxAnimation(x, y);
+#endif
+       GfxAction[x][y] = ACTION_WAITING;
+      }
+
       if (element == EL_ROBOT ||
+#if 0
+         element == EL_PACMAN ||
+#endif
          element == EL_YAMYAM ||
          element == EL_DARK_YAMYAM)
       {
+#if 0
+       DrawLevelElementAnimation(x, y, element);
+#else
        DrawLevelElementAnimationIfNeeded(x, y, element);
+#endif
        PlaySoundLevelAction(x, y, ACTION_WAITING);
       }
       else if (element == EL_SP_ELECTRON)
@@ -3247,7 +3512,7 @@ void StartMoving(int x, int y)
          {
            int flamed = MovingOrBlocked2Element(xx, yy);
 
-           if (IS_ENEMY(flamed) || IS_CAN_EXPLODE(flamed))
+           if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
              Bang(xx, yy);
            else
              RemoveMovingField(xx, yy);
@@ -3271,29 +3536,33 @@ void StartMoving(int x, int y)
 
        return;
       }
+
+      GfxAction[x][y] = ACTION_MOVING;
     }
 
     /* now make next step */
 
     Moving2Blocked(x, y, &newx, &newy);        /* get next screen position */
 
-    if (IS_ENEMY(element) && IS_PLAYER(newx, newy) &&
+    if (DONT_COLLIDE_WITH(element) && IS_PLAYER(newx, newy) &&
        !PLAYER_PROTECTED(newx, newy))
     {
-
 #if 1
       TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
       return;
 #else
-      /* enemy got the player */
+      /* player killed by element which is deadly when colliding with */
       MovDir[x][y] = 0;
       KillHero(PLAYERINFO(newx, newy));
       return;
 #endif
 
     }
-    else if ((element == EL_PENGUIN || element == EL_ROBOT ||
-             element == EL_SATELLITE || element == EL_BALLOON) &&
+    else if ((element == EL_PENGUIN ||
+             element == EL_ROBOT ||
+             element == EL_SATELLITE ||
+             element == EL_BALLOON ||
+             IS_CUSTOM_ELEMENT(element)) &&
             IN_LEV_FIELD(newx, newy) &&
             MovDir[x][y] == MV_DOWN && Feld[newx][newy] == EL_ACID)
     {
@@ -3327,6 +3596,8 @@ void StartMoving(int x, int y)
       }
       else if (!IS_FREE(newx, newy))
       {
+       GfxAction[x][y] = ACTION_WAITING;
+
        if (IS_PLAYER(x, y))
          DrawPlayerField(x, y);
        else
@@ -3378,7 +3649,9 @@ void StartMoving(int x, int y)
        int element2 = (IN_LEV_FIELD(newx2, newy2) ?
                        MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
 
-       if ((wanna_flame || IS_ENEMY(element1) || IS_ENEMY(element2)) &&
+       if ((wanna_flame ||
+            IS_CLASSIC_ENEMY(element1) ||
+            IS_CLASSIC_ENEMY(element2)) &&
            element1 != EL_DRAGON && element2 != EL_DRAGON &&
            element1 != EL_FLAMES && element2 != EL_FLAMES)
        {
@@ -3471,23 +3744,30 @@ void StartMoving(int x, int y)
 
       TurnRound(x, y);
 
-      if (element == EL_BUG || element == EL_SPACESHIP ||
+#if 1
+      DrawLevelElementAnimation(x, y, element);
+#else
+      if (element == EL_BUG ||
+         element == EL_SPACESHIP ||
          element == EL_SP_SNIKSNAK)
        DrawLevelField(x, y);
-      else if (element == EL_BUG || element == EL_SPACESHIP ||
-              element == EL_SP_SNIKSNAK || element == EL_MOLE)
+      else if (element == EL_MOLE)
        DrawLevelField(x, y);
-      else if (element == EL_BD_BUTTERFLY || element == EL_BD_FIREFLY)
+      else if (element == EL_BD_BUTTERFLY ||
+              element == EL_BD_FIREFLY)
        DrawLevelElementAnimationIfNeeded(x, y, element);
       else if (element == EL_SATELLITE)
        DrawLevelElementAnimationIfNeeded(x, y, element);
       else if (element == EL_SP_ELECTRON)
        DrawLevelElementAnimationIfNeeded(x, y, element);
+#endif
 
       if (DONT_TOUCH(element))
        TestIfBadThingTouchesHero(x, y);
 
+#if 0
       PlaySoundLevelAction(x, y, ACTION_WAITING);
+#endif
 
       return;
     }
@@ -3509,7 +3789,9 @@ void ContinueMoving(int x, int y)
   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
   int horiz_move = (dx != 0);
   int newx = x + dx, newy = y + dy;
-  int step = (horiz_move ? dx : dy) * TILEX / 8;
+  int step = (horiz_move ? dx : dy) * TILEX / MOVE_DELAY_NORMAL_SPEED;
+  struct PlayerInfo *player = (IS_PLAYER(x, y) ? PLAYERINFO(x, y) : NULL);
+  boolean pushing = (player != NULL && player->Pushing && player->is_moving);
 
   if (element == EL_AMOEBA_DROP || element == EL_AMOEBA_DROPPING)
     step /= 2;
@@ -3526,6 +3808,8 @@ void ContinueMoving(int x, int y)
     step /= 2;
   else if (element == EL_SPRING && horiz_move)
     step *= 2;
+  else if (IS_CUSTOM_ELEMENT(element))
+    step = SIGN(step) * element_info[element].move_stepsize;
 
 #if OLD_GAME_BEHAVIOUR
   else if (CAN_FALL(element) && horiz_move && !IS_SP_ELEMENT(element))
@@ -3534,10 +3818,21 @@ void ContinueMoving(int x, int y)
 
   MovPos[x][y] += step;
 
+#if 1
+  if (pushing)         /* special case: moving object pushed by player */
+    MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->GfxPos));
+#endif
+
+#if 0
+  if (pushing)
+    printf("::: OOPS! pushing '%s'\n", element_info[element].token_name);
+#endif
+
   if (ABS(MovPos[x][y]) >= TILEX)      /* object reached its destination */
   {
     Feld[x][y] = EL_EMPTY;
     Feld[newx][newy] = element;
+    MovPos[x][y] = 0;  /* force "not moving" for "crumbled sand" */
 
     if (element == EL_MOLE)
     {
@@ -3617,6 +3912,9 @@ void ContinueMoving(int x, int y)
     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
     MovDelay[newx][newy] = 0;
 
+    /* copy element change control values to new field */
+    ChangeDelay[newx][newy] = ChangeDelay[x][y];
+
     /* copy animation control values to new field */
     GfxFrame[newx][newy]  = GfxFrame[x][y];
     GfxAction[newx][newy] = GfxAction[x][y];   /* keep action one frame */
@@ -3643,8 +3941,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 */
     {
@@ -3655,9 +3959,15 @@ void ContinueMoving(int x, int y)
     else if (element == EL_PENGUIN)
       TestIfFriendTouchesBadThing(newx, newy);
 
+#if 1
+    if (CAN_FALL(element) && direction == MV_DOWN &&
+       (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
+      Impact(x, newy);
+#else
     if (CAN_SMASH(element) && direction == MV_DOWN &&
-       (newy == lev_fieldy-1 || !IS_FREE(x, newy+1)))
+       (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
       Impact(x, newy);
+#endif
   }
   else                         /* still moving on */
   {
@@ -4155,7 +4465,7 @@ void Life(int ax, int ay)
 
 static void InitRobotWheel(int x, int y)
 {
-  MovDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
+  ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
 }
 
 static void RunRobotWheel(int x, int y)
@@ -4171,7 +4481,7 @@ static void StopRobotWheel(int x, int y)
 
 static void InitTimegateWheel(int x, int y)
 {
-  MovDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
+  ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
 }
 
 static void RunTimegateWheel(int x, int y)
@@ -4497,7 +4807,7 @@ static void InitBuggyBase(int x, int y)
   int element = Feld[x][y];
   int activating_delay = FRAMES_PER_SECOND / 4;
 
-  MovDelay[x][y] =
+  ChangeDelay[x][y] =
     (element == EL_SP_BUGGY_BASE ?
      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
      element == EL_SP_BUGGY_BASE_ACTIVATING ?
@@ -4532,7 +4842,7 @@ static void WarnBuggyBase(int x, int y)
 
 static void InitTrap(int x, int y)
 {
-  MovDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
+  ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
 }
 
 static void ActivateTrap(int x, int y)
@@ -4549,25 +4859,66 @@ static void ChangeActiveTrap(int x, int y)
     DrawLevelFieldCrumbledSand(x, y);
 }
 
+static void ChangeElementDoIt(int x, int y, int element_new)
+{
+  CheckTriggeredElementChange(Feld[x][y], CE_OTHER_CHANGING);
+
+  RemoveField(x, y);
+  Feld[x][y] = element_new;
+
+  ResetGfxAnimation(x, y);
+  ResetRandomAnimationValue(x, y);
+
+  InitField(x, y, FALSE);
+  if (CAN_MOVE(Feld[x][y]))
+    InitMovDir(x, y);
+
+  DrawLevelField(x, y);
+
+  if (CAN_BE_CRUMBLED(Feld[x][y]))
+  {
+    int sx = SCREENX(x), sy = SCREENY(y);
+    static int xy[4][2] =
+    {
+      { 0, -1 },
+      { -1, 0 },
+      { +1, 0 },
+      { 0, +1 }
+    };
+    int i;
+
+    for(i=0; i<4; i++)
+    {
+      int xx = x + xy[i][0];
+      int yy = y + xy[i][1];
+      int sxx = sx + xy[i][0];
+      int syy = sy + xy[i][1];
+
+      if (!IN_LEV_FIELD(xx, yy) ||
+         !IN_SCR_FIELD(sxx, syy) ||
+         !CAN_BE_CRUMBLED(Feld[xx][yy]) ||
+         IS_MOVING(xx, yy))
+       continue;
+
+      DrawLevelField(xx, yy);
+    }
+  }
+}
+
 static void ChangeElement(int x, int y)
 {
   int element = Feld[x][y];
 
-  if (IS_MOVING(x, y))                 /* never change a running system :-) */
-    return;
-
-  if (MovDelay[x][y] == 0)             /* initialize element change */
+  if (ChangeDelay[x][y] == 0)          /* initialize element change */
   {
-    MovDelay[x][y] = changing_element[element].change_delay + 1;
+    ChangeDelay[x][y] = changing_element[element].change_delay + 1;
 
-    if (IS_CUSTOM_ELEMENT(element) &&
-       HAS_CHANGE_EVENT(element, CE_DELAY_RANDOM))
+    if (IS_CUSTOM_ELEMENT(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
     {
-      int i = element - EL_CUSTOM_START;
-      int max_random_delay = level.custom_element[i].change.delay_random;
-      int delay_frames = level.custom_element[i].change.delay_frames;
+      int max_random_delay = element_info[element].change.delay_random;
+      int delay_frames = element_info[element].change.delay_frames;
 
-      MovDelay[x][y] += RND(max_random_delay * delay_frames);
+      ChangeDelay[x][y] += RND(max_random_delay * delay_frames);
     }
 
     ResetGfxAnimation(x, y);
@@ -4577,35 +4928,72 @@ static void ChangeElement(int x, int y)
       changing_element[element].pre_change_function(x, y);
   }
 
-  MovDelay[x][y]--;
+  ChangeDelay[x][y]--;
 
-  if (MovDelay[x][y] != 0)             /* continue element change */
+  if (ChangeDelay[x][y] != 0)          /* continue element change */
   {
-    if (IS_ANIMATED(el2img(element)))
-      DrawLevelElementAnimationIfNeeded(x, y, element);
+    int graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
+
+    if (IS_ANIMATED(graphic))
+      DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
 
     if (changing_element[element].change_function)
       changing_element[element].change_function(x, y);
   }
   else                                 /* finish element change */
   {
-    Feld[x][y] = changing_element[element].next_element;
+    int next_element = changing_element[element].next_element;
 
-    ResetGfxAnimation(x, y);
-    ResetRandomAnimationValue(x, y);
+    if (IS_MOVING(x, y))               /* never change a running system ;-) */
+    {
+      ChangeDelay[x][y] = 1;           /* try change after next move step */
 
-#if 1
-    InitField(x, y, FALSE);
-    if (CAN_MOVE(element))
-      InitMovDir(x, y);
-#endif
-    DrawLevelField(x, y);
+      return;
+    }
+
+    if (next_element != EL_UNDEFINED)
+      ChangeElementDoIt(x, y, next_element);
+    else
+      ChangeElementDoIt(x, y, element_info[element].change.successor);
 
     if (changing_element[element].post_change_function)
       changing_element[element].post_change_function(x, y);
   }
 }
 
+static void CheckTriggeredElementChange(int trigger_element, int trigger_event)
+{
+  int i, x, y;
+
+  if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
+    return;
+
+  for (i=0; i<MAX_NUM_ELEMENTS; i++)
+  {
+    if (!CAN_CHANGE(i) || !HAS_CHANGE_EVENT(i, trigger_event) ||
+       element_info[i].change.trigger != trigger_element)
+      continue;
+
+    for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
+    {
+      if (Feld[x][y] == i)
+      {
+       ChangeDelay[x][y] = 1;
+       ChangeElement(x, y);
+      }
+    }
+  }
+}
+
+static void CheckPlayerElementChange(int x, int y, int element,
+                                    int trigger_event)
+{
+  if (!CAN_CHANGE(element) || !HAS_CHANGE_EVENT(element, trigger_event))
+    return;
+
+  ChangeElementDoIt(x, y, element_info[element].change.successor);
+}
+
 static void PlayerActions(struct PlayerInfo *player, byte player_action)
 {
   static byte stored_player_action[MAX_PLAYERS];
@@ -4772,6 +5160,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;
@@ -4800,7 +5212,11 @@ void GameActions()
   for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
   {
     element = Feld[x][y];
+#if 1
+    graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
+#else
     graphic = el2img(element);
+#endif
 
 #if 0
     if (element == -1)
@@ -4832,15 +5248,46 @@ void GameActions()
       continue;
     }
 
+#if 1
+    /* this may take place after moving, so 'element' may have changed */
+    if (IS_CHANGING(x, y))
+    {
+      ChangeElement(x, y);
+      element = Feld[x][y];
+      graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
+    }
+#endif
+
     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
     {
       StartMoving(x, y);
 
+#if 1
+      graphic = el_act_dir2img(element, GfxAction[x][y], MovDir[x][y]);
+#if 0
+      if (element == EL_PACMAN)
+       printf("::: %d, %d, %d\n",
+              IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
+#endif
+#if 0
+      if (element == EL_YAMYAM)
+       printf("::: %d, %d, %d\n",
+              IS_ANIMATED(graphic), IS_MOVING(x, y), Stop[x][y]);
+#endif
+#endif
+
       if (IS_ANIMATED(graphic) &&
          !IS_MOVING(x, y) &&
          !Stop[x][y])
+      {
        DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
 
+#if 0
+       if (element == EL_YAMYAM)
+         printf("::: %d, %d\n", graphic, GfxFrame[x][y]);
+#endif
+      }
+
       if (IS_GEM(element) || element == EL_SP_INFOTRON)
        EdelsteinFunkeln(x, y);
     }
@@ -4893,11 +5340,11 @@ void GameActions()
 #endif
     else if (element == EL_EXPLOSION)
       ;        /* drawing of correct explosion animation is handled separately */
-    else if (IS_ANIMATED(graphic) && !IS_AUTO_CHANGING(element))
+    else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
 
-#if 1
-    /* this may take place after moving, therefore element may have changed */
+#if 0
+    /* this may take place after moving, so 'element' may have changed */
     if (IS_AUTO_CHANGING(Feld[x][y]))
       ChangeElement(x, y);
 #endif
@@ -5273,7 +5720,7 @@ boolean MoveFigureOneStep(struct PlayerInfo *player,
   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
 #endif
 
-  if (DONT_GO_TO(element))
+  if (DONT_RUN_INTO(element))
   {
     if (element == EL_ACID && dx == 0 && dy == 1)
     {
@@ -5611,11 +6058,11 @@ void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
 #endif
 
-    /* 1st case: good thing is moving towards DONT_GO_TO style bad thing;
+    /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
        2nd case: DONT_TOUCH style bad thing does not move away from good thing
     */
-    if ((DONT_GO_TO(test_element) && good_move_dir == test_dir[i]) ||
-       (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
+    if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
+       (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
     {
       kill_x = test_x;
       kill_y = test_y;
@@ -5675,11 +6122,11 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
 
     test_element = Feld[test_x][test_y];
 
-    /* 1st case: good thing is moving towards DONT_GO_TO style bad thing;
+    /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
        2nd case: DONT_TOUCH style bad thing does not move away from good thing
     */
-    if ((DONT_GO_TO(bad_element) &&  bad_move_dir == test_dir[i]) ||
-       (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
+    if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
+       (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
     {
       /* good thing is player or penguin that does not move away */
       if (IS_PLAYER(test_x, test_y))
@@ -5831,7 +6278,11 @@ void BuryHero(struct PlayerInfo *player)
   if (!player->active)
     return;
 
+#if 1
+  PlaySoundLevelElementAction(jx, jy, player->element_nr, ACTION_DYING);
+#else
   PlaySoundLevel(jx, jy, SND_CLASS_PLAYER_DYING);
+#endif
   PlaySoundLevel(jx, jy, SND_GAME_LOSING);
 
   player->GameOver = TRUE;
@@ -5910,10 +6361,10 @@ int DigField(struct PlayerInfo *player,
     player->is_collecting = FALSE;
   }
 
-  if (player->MovPos == 0)
+  if (player->MovPos == 0)     /* last pushing move finished */
     player->Pushing = FALSE;
 
-  if (mode == DF_NO_PUSH)
+  if (mode == DF_NO_PUSH)      /* player just stopped pushing */
   {
     player->Switching = FALSE;
     player->push_delay = 0;
@@ -5922,7 +6373,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]))
@@ -5970,7 +6428,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:
@@ -5979,15 +6443,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:
@@ -5999,13 +6464,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);
@@ -6015,18 +6480,31 @@ int DigField(struct PlayerInfo *player,
       DrawText(DX_EMERALDS, DY_EMERALDS,
               int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
       PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
 
     case EL_SPEED_PILL:
       RemoveField(x, y);
       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundLevel(x, y, SND_SPEED_PILL_COLLECTING);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
 
+#if 0
     case EL_ENVELOPE:
       Feld[x][y] = EL_EMPTY;
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundLevel(x, y, SND_ENVELOPE_COLLECTING);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
+#endif
 
     case EL_EXTRA_TIME:
       RemoveField(x, y);
@@ -6035,20 +6513,35 @@ int DigField(struct PlayerInfo *player,
        TimeLeft += 10;
        DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
       }
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundStereo(SND_EXTRA_TIME_COLLECTING, SOUND_MIDDLE);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
 
     case EL_SHIELD_NORMAL:
       RemoveField(x, y);
       player->shield_normal_time_left += 10;
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundLevel(x, y, SND_SHIELD_NORMAL_COLLECTING);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
 
     case EL_SHIELD_DEADLY:
       RemoveField(x, y);
       player->shield_normal_time_left += 10;
       player->shield_deadly_time_left += 10;
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundLevel(x, y, SND_SHIELD_DEADLY_COLLECTING);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
 
     case EL_DYNAMITE:
@@ -6060,6 +6553,7 @@ int DigField(struct PlayerInfo *player,
       DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(local_player->dynamite, 3),
               FONT_TEXT_2);
       PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
 
     case EL_DYNABOMB_INCREASE_NUMBER:
@@ -6067,21 +6561,36 @@ int DigField(struct PlayerInfo *player,
       player->dynabomb_count++;
       player->dynabombs_left++;
       RaiseScoreElement(EL_DYNAMITE);
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundLevel(x, y, SND_DYNABOMB_INCREASE_NUMBER_COLLECTING);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
 
     case EL_DYNABOMB_INCREASE_SIZE:
       RemoveField(x, y);
       player->dynabomb_size++;
       RaiseScoreElement(EL_DYNAMITE);
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundLevel(x, y, SND_DYNABOMB_INCREASE_SIZE_COLLECTING);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
 
     case EL_DYNABOMB_INCREASE_POWER:
       RemoveField(x, y);
       player->dynabomb_xl = TRUE;
       RaiseScoreElement(EL_DYNAMITE);
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundLevel(x, y, SND_DYNABOMB_INCREASE_POWER_COLLECTING);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
 
     case EL_KEY_1:
@@ -6099,7 +6608,12 @@ int DigField(struct PlayerInfo *player,
                         graphic);
       DrawMiniGraphicExt(window, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
                         graphic);
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundLevel(x, y, SND_CLASS_KEY_COLLECTING);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
     }
 
@@ -6118,7 +6632,12 @@ int DigField(struct PlayerInfo *player,
                         graphic);
       DrawMiniGraphicExt(window, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
                         graphic);
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundLevel(x, y, SND_CLASS_KEY_COLLECTING);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
     }
 
@@ -6255,8 +6774,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 &&
@@ -6276,10 +6801,22 @@ 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 (element == EL_SPRING)
       {
        Feld[x + dx][y + dy] = EL_SPRING;
@@ -6290,6 +6827,9 @@ int DigField(struct PlayerInfo *player,
 
       DrawLevelField(x + dx, y + dy);
       PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
+
+      CheckTriggeredElementChange(element, CE_OTHER_PUSHING);
+
       break;
 
     case EL_GATE_1:
@@ -6476,8 +7016,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:
@@ -6575,6 +7117,8 @@ int DigField(struct PlayerInfo *player,
        PlaySoundLevel(x, y, SND_GAME_SOKOBAN_SOLVING);
       }
 
+      CheckTriggeredElementChange(element, CE_OTHER_PUSHING);
+
       break;
 
     case EL_PENGUIN:
@@ -6586,19 +7130,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;
@@ -6606,15 +7151,19 @@ 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);
+
        break;
       }
       else if (IS_PUSHABLE(element))
@@ -6625,31 +7174,54 @@ int DigField(struct PlayerInfo *player,
        if (CAN_FALL(element) && dy)
          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);
+
        player->Pushing = TRUE;
 
-       if (!IN_LEV_FIELD(x+dx, y+dy) || !IS_FREE(x+dx, y+dy))
+       if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy))
          return MF_NO_ACTION;
 
        if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
          return MF_NO_ACTION;
 
-       if (player->push_delay == 0)
+       if (player->push_delay == 0)    /* new pushing; restart delay */
          player->push_delay = FrameCounter;
 
        if (!FrameReached(&player->push_delay, player->push_delay_value) &&
            !(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))
+         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
+#else
        player->push_delay_value = 2 + RND(8);
+#endif
 
        DrawLevelField(x + dx, y + dy);
        PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
 
+       CheckTriggeredElementChange(element, CE_OTHER_PUSHING);
+       CheckPlayerElementChange(x + dx, y + dy, element, CE_PUSHED_BY_PLAYER);
+
        break;
       }
+      else
+      {
+       CheckPlayerElementChange(x, y, element, CE_PRESSED_BY_PLAYER);
+      }
 
       return MF_NO_ACTION;
   }
@@ -6902,17 +7474,26 @@ void RaiseScoreElement(int element)
     case EL_EMERALD_YELLOW:
     case EL_EMERALD_RED:
     case EL_EMERALD_PURPLE:
+    case EL_SP_INFOTRON:
       RaiseScore(level.score[SC_EMERALD]);
       break;
     case EL_DIAMOND:
       RaiseScore(level.score[SC_DIAMOND]);
       break;
+    case EL_CRYSTAL:
+      RaiseScore(level.score[SC_CRYSTAL]);
+      break;
+    case EL_PEARL:
+      RaiseScore(level.score[SC_PEARL]);
+      break;
     case EL_BUG:
     case EL_BD_BUTTERFLY:
+    case EL_SP_ELECTRON:
       RaiseScore(level.score[SC_BUG]);
       break;
     case EL_SPACESHIP:
     case EL_BD_FIREFLY:
+    case EL_SP_SNIKSNAK:
       RaiseScore(level.score[SC_SPACESHIP]);
       break;
     case EL_YAMYAM:
@@ -6929,8 +7510,18 @@ void RaiseScoreElement(int element)
       RaiseScore(level.score[SC_NUT]);
       break;
     case EL_DYNAMITE:
+    case EL_DYNABOMB_INCREASE_NUMBER:
+    case EL_DYNABOMB_INCREASE_SIZE:
+    case EL_DYNABOMB_INCREASE_POWER:
       RaiseScore(level.score[SC_DYNAMITE]);
       break;
+    case EL_SHIELD_NORMAL:
+    case EL_SHIELD_DEADLY:
+      RaiseScore(level.score[SC_SHIELD]);
+      break;
+    case EL_EXTRA_TIME:
+      RaiseScore(level.score[SC_TIME_BONUS]);
+      break;
     case EL_KEY_1:
     case EL_KEY_2:
     case EL_KEY_3:
@@ -6938,6 +7529,7 @@ void RaiseScoreElement(int element)
       RaiseScore(level.score[SC_KEY]);
       break;
     default:
+      RaiseScore(element_info[element].score);
       break;
   }
 }