rnd-20030610-1-src
[rocksndiamonds.git] / src / game.c
index 436751f65916f7005513f7bb2b13d248e7d4bc1b..c4764283bb635d6a25c086410d69e80695cd4c53 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))
+
 /* game button identifiers */
 #define GAME_CTRL_ID_STOP              0
 #define GAME_CTRL_ID_PAUSE             1
@@ -154,38 +159,144 @@ struct ChangingElementInfo
 
 static struct ChangingElementInfo changing_element_list[] =
 {
-  { EL_NUT_BREAKING,           EL_EMERALD,              6, NULL, NULL, NULL },
-  { EL_PEARL_BREAKING,         EL_EMPTY,                8, NULL, NULL, NULL },
-  { EL_EXIT_OPENING,           EL_EXIT_OPEN,           29, NULL, NULL, NULL },
-
-  { EL_SWITCHGATE_OPENING,     EL_SWITCHGATE_OPEN,     29, NULL, NULL, NULL },
-  { EL_SWITCHGATE_CLOSING,     EL_SWITCHGATE_CLOSED,   29, NULL, NULL, NULL },
-
-  { EL_TIMEGATE_OPENING,       EL_TIMEGATE_OPEN,       29, NULL, NULL, NULL },
-  { EL_TIMEGATE_CLOSING,       EL_TIMEGATE_CLOSED,     29, NULL, NULL, NULL },
-
-  { EL_ACID_SPLASH_LEFT,       EL_EMPTY,                8, NULL, NULL, NULL },
-  { EL_ACID_SPLASH_RIGHT,      EL_EMPTY,                8, NULL, NULL, NULL },
-
-  { EL_SP_BUGGY_BASE,          EL_SP_BUGGY_BASE_ACTIVATING, 0,
-    InitBuggyBase, NULL, NULL },
-  { EL_SP_BUGGY_BASE_ACTIVATING,EL_SP_BUGGY_BASE_ACTIVE, 0,
-    InitBuggyBase, NULL, NULL },
-  { EL_SP_BUGGY_BASE_ACTIVE,   EL_SP_BUGGY_BASE,        0,
-    InitBuggyBase, WarnBuggyBase, NULL },
-
-  { EL_TRAP,                   EL_TRAP_ACTIVE,          0,
-    InitTrap, NULL, ActivateTrap },
-  { EL_TRAP_ACTIVE,            EL_TRAP,                31,
-    NULL, ChangeActiveTrap, NULL },
-
-  { EL_ROBOT_WHEEL_ACTIVE,     EL_ROBOT_WHEEL,          0,
-    InitRobotWheel, RunRobotWheel, StopRobotWheel },
+  {
+    EL_NUT_BREAKING,
+    EL_EMERALD,
+    6,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_PEARL_BREAKING,
+    EL_EMPTY,
+    8,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_EXIT_OPENING,
+    EL_EXIT_OPEN,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_SWITCHGATE_OPENING,
+    EL_SWITCHGATE_OPEN,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_SWITCHGATE_CLOSING,
+    EL_SWITCHGATE_CLOSED,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_TIMEGATE_OPENING,
+    EL_TIMEGATE_OPEN,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_TIMEGATE_CLOSING,
+    EL_TIMEGATE_CLOSED,
+    29,
+    NULL,
+    NULL,
+    NULL
+  },
 
-  { EL_TIMEGATE_SWITCH_ACTIVE, EL_TIMEGATE_SWITCH,      0,
-    InitTimegateWheel, RunTimegateWheel, NULL },
+  {
+    EL_ACID_SPLASH_LEFT,
+    EL_EMPTY,
+    8,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_ACID_SPLASH_RIGHT,
+    EL_EMPTY,
+    8,
+    NULL,
+    NULL,
+    NULL
+  },
+  {
+    EL_SP_BUGGY_BASE,
+    EL_SP_BUGGY_BASE_ACTIVATING,
+    0,
+    InitBuggyBase,
+    NULL,
+    NULL
+  },
+  {
+    EL_SP_BUGGY_BASE_ACTIVATING,
+    EL_SP_BUGGY_BASE_ACTIVE,
+    0,
+    InitBuggyBase,
+    NULL,
+    NULL
+  },
+  {
+    EL_SP_BUGGY_BASE_ACTIVE,
+    EL_SP_BUGGY_BASE,
+    0,
+    InitBuggyBase,
+    WarnBuggyBase,
+    NULL
+  },
+  {
+    EL_TRAP,
+    EL_TRAP_ACTIVE,
+    0,
+    InitTrap,
+    NULL,
+    ActivateTrap
+  },
+  {
+    EL_TRAP_ACTIVE,
+    EL_TRAP,
+    31,
+    NULL,
+    ChangeActiveTrap,
+    NULL
+  },
+  {
+    EL_ROBOT_WHEEL_ACTIVE,
+    EL_ROBOT_WHEEL,
+    0,
+    InitRobotWheel,
+    RunRobotWheel,
+    StopRobotWheel
+  },
+  {
+    EL_TIMEGATE_SWITCH_ACTIVE,
+    EL_TIMEGATE_SWITCH,
+    0,
+    InitTimegateWheel,
+    RunTimegateWheel,
+    NULL
+  },
 
-  { EL_UNDEFINED,              EL_UNDEFINED,           -1, NULL        }
+  {
+    EL_UNDEFINED,
+    EL_UNDEFINED,
+    -1,
+    NULL,
+    NULL,
+    NULL
+  }
 };
 
 static struct ChangingElementInfo changing_element[MAX_NUM_ELEMENTS];
@@ -266,7 +377,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)
@@ -463,6 +576,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;
   }
 }
@@ -524,32 +639,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++)
   {
@@ -561,6 +650,7 @@ static void InitGameEngine()
     changing_element[i].post_change_function = NULL;
   }
 
+  /* add changing elements from pre-defined list */
   i = 0;
   while (changing_element_list[i].base_element != EL_UNDEFINED)
   {
@@ -576,6 +666,22 @@ static void InitGameEngine()
 
     i++;
   }
+
+  /* add changing elements from custom element configuration */
+  for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+  {
+    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 (!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 = (change->delay_fixed *
+                                             change->delay_frames);
+  }
 }
 
 
@@ -705,7 +811,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;
@@ -947,8 +1053,7 @@ void InitGame()
   if (setup.sound_music)
     PlayMusic(level_nr);
 
-  if (!tape.playing)
-    KeyboardAutoRepeatOff();
+  KeyboardAutoRepeatOffUnlessAutoplay();
 
   if (options.debug)
   {
@@ -1034,30 +1139,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;
+           }
          }
        }
       }
@@ -1524,7 +1665,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;
 
@@ -1604,10 +1745,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)
@@ -1649,7 +1794,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;
   }
@@ -1679,7 +1825,7 @@ void Explode(int ex, int ey, int phase, int mode)
 
     if (IS_PLAYER(x, y))
       KillHeroUnlessProtected(x, y);
-    else if (IS_EXPLOSIVE(element))
+    else if (CAN_EXPLODE_BY_FIRE(element))
     {
       Feld[x][y] = Store2[x][y];
       Store2[x][y] = 0;
@@ -1702,7 +1848,7 @@ void Explode(int ex, int ey, int phase, int mode)
 
     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
     InitField(x, y, FALSE);
-    if (CAN_MOVE(element) || COULD_MOVE(element))
+    if (CAN_MOVE(element))
       InitMovDir(x, y);
     DrawLevelField(x, y);
 
@@ -1720,7 +1866,7 @@ void Explode(int ex, int ey, int phase, int mode)
     if (phase == delay)
       DrawLevelFieldCrumbledSand(x, y);
 
-    if (IS_WALKABLE_OVER(Back[x][y]))
+    if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
     {
       DrawLevelElement(x, y, Back[x][y]);
       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
@@ -2170,8 +2316,9 @@ 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 */
   {
@@ -2191,16 +2338,20 @@ void Impact(int x, int y)
     return;
   }
 
-  if (lastline || object_hit)
+  if (impact)
   {
     ResetGfxAnimation(x, 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;
@@ -2212,12 +2363,12 @@ void Impact(int x, int y)
     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);
     else if (object_hit && smashed == EL_PENGUIN)
-      Bang(x, y+1);
+      Bang(x, y + 1);
     else
     {
       Feld[x][y] = EL_AMOEBA_GROWING;
@@ -2230,7 +2381,7 @@ void Impact(int x, int y)
 
   if (!lastline && object_hit)         /* check which object was hit */
   {
-    if (CAN_CHANGE(element) && 
+    if (CAN_PASS_MAGIC_WALL(element) && 
        (smashed == EL_MAGIC_WALL ||
         smashed == EL_BD_MAGIC_WALL))
     {
@@ -2265,7 +2416,7 @@ void Impact(int x, int y)
     }
     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;
@@ -2284,7 +2435,7 @@ void Impact(int x, int y)
             element == EL_SP_ZONK ||
             element == EL_BD_ROCK)
     {
-      if (IS_ENEMY(smashed) ||
+      if (IS_CLASSIC_ENEMY(smashed) ||
          smashed == EL_BOMB ||
          smashed == EL_SP_DISK_ORANGE ||
          smashed == EL_DX_SUPABOMB ||
@@ -2366,26 +2517,26 @@ 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];
@@ -2519,13 +2670,13 @@ void TurnRound(int x, int y)
     int rnd = RND(rnd_value);
 
     if (IN_LEV_FIELD(left_x, left_y) &&
-       (IS_FREE(left_x, left_y) || IS_GEM(Feld[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_GEM(Feld[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_GEM(Feld[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 &&
@@ -2577,7 +2728,7 @@ void TurnRound(int x, int y)
       MovDir[x][y] = back_dir;
 
     if (!IS_FREE(x+move_xy[MovDir[x][y]].x, y+move_xy[MovDir[x][y]].y) &&
-       !IS_GEM(Feld[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]))
       MovDir[x][y] = old_move_dir;
 
     MovDelay[x][y] = 0;
@@ -2704,8 +2855,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)
        {
@@ -2717,21 +2868,21 @@ 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))
@@ -2745,13 +2896,13 @@ void TurnRound(int x, int y)
 
       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) &&
@@ -2763,7 +2914,7 @@ void TurnRound(int x, int y)
          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) &&
@@ -2779,6 +2930,148 @@ 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;
+
+    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 (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))))
+      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))))
+      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 (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))))
+      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))))
+      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 (IN_LEV_FIELD(newx, newy) && (IS_FREE(newx, newy) ||
+                                      (DONT_COLLIDE_WITH(element) &&
+                                       IS_FREE_OR_PLAYER(newx, newy)) ||
+                                      Feld[newx][newy] == EL_ACID))
+       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))
+       return;
+
+      MovDir[x][y] = old_move_dir;
+    }
+  }
 }
 
 static boolean JustBeingPushed(int x, int y)
@@ -2804,7 +3097,7 @@ static boolean JustBeingPushed(int x, int y)
 
 void StartMoving(int x, int y)
 {
-  static boolean use_spring_bug = TRUE;
+  boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0));
   boolean started_moving = FALSE;      /* some elements can fall _and_ move */
   int element = Feld[x][y];
 
@@ -2927,7 +3220,7 @@ void StartMoving(int x, int y)
        Store[x][y] = 0;
       }
     }
-    else if (CAN_CHANGE(element) &&
+    else if (CAN_PASS_MAGIC_WALL(element) &&
             (Feld[x][y+1] == EL_MAGIC_WALL_ACTIVE ||
              Feld[x][y+1] == EL_BD_MAGIC_WALL_ACTIVE))
     {
@@ -3066,7 +3359,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);
 
@@ -3083,11 +3377,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)
@@ -3110,20 +3429,14 @@ void StartMoving(int x, int y)
          int sx = SCREENX(xx), sy = SCREENY(yy);
          int flame_graphic = graphic + (i - 1);
 
-#if 1
          if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
            break;
-#else
-         if (!IN_LEV_FIELD(xx, yy) ||
-             IS_HISTORIC_SOLID(Feld[xx][yy]) || Feld[xx][yy] == EL_EXPLOSION)
-           break;
-#endif
 
          if (MovDelay[x][y])
          {
            int flamed = MovingOrBlocked2Element(xx, yy);
 
-           if (IS_ENEMY(flamed) || IS_EXPLOSIVE(flamed))
+           if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_FIRE(flamed))
              Bang(xx, yy);
            else
              RemoveMovingField(xx, yy);
@@ -3147,29 +3460,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)
     {
@@ -3203,6 +3520,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
@@ -3212,7 +3531,7 @@ void StartMoving(int x, int y)
     }
     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
     {
-      if (IS_GEM(Feld[newx][newy]))
+      if (IS_FOOD_PIG(Feld[newx][newy]))
       {
        if (IS_MOVING(newx, newy))
          RemoveMovingField(newx, newy);
@@ -3254,7 +3573,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)
        {
@@ -3347,23 +3668,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;
     }
@@ -3385,7 +3713,7 @@ 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;
 
   if (element == EL_AMOEBA_DROP || element == EL_AMOEBA_DROPPING)
     step /= 2;
@@ -3402,6 +3730,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))
@@ -3532,7 +3862,7 @@ void ContinueMoving(int x, int y)
       TestIfFriendTouchesBadThing(newx, newy);
 
     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);
   }
   else                         /* still moving on */
@@ -4429,10 +4759,21 @@ 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 */
   {
     MovDelay[x][y] = changing_element[element].change_delay + 1;
 
+    if (IS_CUSTOM_ELEMENT(element) && HAS_CHANGE_EVENT(element, CE_DELAY))
+    {
+      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);
+    }
+
     ResetGfxAnimation(x, y);
     ResetRandomAnimationValue(x, y);
 
@@ -4457,6 +4798,11 @@ static void ChangeElement(int x, int y)
     ResetGfxAnimation(x, y);
     ResetRandomAnimationValue(x, y);
 
+#if 1
+    InitField(x, y, FALSE);
+    if (CAN_MOVE(element))
+      InitMovDir(x, y);
+#endif
     DrawLevelField(x, y);
 
     if (changing_element[element].post_change_function)
@@ -4658,7 +5004,20 @@ 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)
+    {
+      printf("::: %d,%d: %d [%d]\n", x, y, element, FrameCounter);
+
+      element = graphic = 0;
+    }
+#endif
 
     if (graphic_info[graphic].anim_global_sync)
       GfxFrame[x][y] = FrameCounter;
@@ -4685,11 +5044,32 @@ void GameActions()
     {
       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);
     }
@@ -4736,13 +5116,21 @@ void GameActions()
       MauerAbleger(x, y);
     else if (element == EL_FLAMES)
       CheckForDragon(x, y);
+#if 0
     else if (IS_AUTO_CHANGING(element))
       ChangeElement(x, y);
+#endif
     else if (element == EL_EXPLOSION)
       ;        /* drawing of correct explosion animation is handled separately */
-    else if (IS_ANIMATED(graphic))
+    else if (IS_ANIMATED(graphic) && !IS_AUTO_CHANGING(element))
       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
 
+#if 1
+    /* this may take place after moving, therefore element may have changed */
+    if (IS_AUTO_CHANGING(Feld[x][y]))
+      ChangeElement(x, y);
+#endif
+
     if (IS_BELT_ACTIVE(element))
       PlaySoundLevelAction(x, y, ACTION_ACTIVE);
 
@@ -5114,7 +5502,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)
     {
@@ -5452,11 +5840,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;
@@ -5516,11 +5904,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))
@@ -5711,7 +6099,6 @@ void RemoveHero(struct PlayerInfo *player)
 static boolean checkDiagonalPushing(struct PlayerInfo *player,
                                    int x, int y, int real_dx, int real_dy)
 {
-#if 1
   int jx, jy, dx, dy, xx, yy;
 
   if (real_dx == 0 || real_dy == 0)    /* no diagonal direction => push */
@@ -5725,32 +6112,7 @@ static boolean checkDiagonalPushing(struct PlayerInfo *player,
   xx = jx + (dx == 0 ? real_dx : 0);
   yy = jy + (dy == 0 ? real_dy : 0);
 
-  return (!IN_LEV_FIELD(xx, yy) || IS_SOLID(Feld[xx][yy]));
-#else
-
-  if (real_dx && real_dy)      /* diagonal direction input => do check */
-  {
-    /* diagonal direction: check alternative direction */
-    int jx = player->jx, jy = player->jy;
-    int dx = x - jx, dy = y - jy;
-    int xx = jx + (dx == 0 ? real_dx : 0);
-    int yy = jy + (dy == 0 ? real_dy : 0);
-
-    if (IN_LEV_FIELD(xx, yy))
-    {
-      int element = Feld[xx][yy];
-
-      if (game.engine_version < VERSION_IDENT(2,2,0))
-       return IS_HISTORIC_SOLID(element);
-      else
-       return !(IS_WALKABLE(element) ||
-                IS_DIGGABLE(element) ||
-                IS_COLLECTIBLE(element));
-    }
-  }
-
-  return TRUE;         /* no diagonal direction input => push object */
-#endif
+  return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
 }
 
 /*
@@ -5777,13 +6139,14 @@ 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;
+
     return MF_NO_ACTION;
   }
 
@@ -5828,6 +6191,12 @@ int DigField(struct PlayerInfo *player,
 
   element = Feld[x][y];
 
+#if 1
+  if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
+      game.engine_version >= VERSION_IDENT(2,2,0))
+    return MF_NO_ACTION;
+#endif
+
   switch (element)
   {
     case EL_EMPTY:
@@ -6103,20 +6472,17 @@ int DigField(struct PlayerInfo *player,
 
       player->Pushing = TRUE;
 
+#if 0
+      if (element == EL_ROCK)
+       printf("::: wanna push [%d] [%d]\n",
+              FrameCounter, player->push_delay_value);
+#endif
+
       if (!IN_LEV_FIELD(x+dx, y+dy) || !IS_FREE(x+dx, y+dy))
        return MF_NO_ACTION;
 
-#if 1
       if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
        return MF_NO_ACTION;
-#else
-      if (real_dy)
-      {
-       if (IN_LEV_FIELD(jx, jy+real_dy) &&
-           !IS_HISTORIC_SOLID(Feld[jx][jy+real_dy]))
-         return MF_NO_ACTION;
-      }
-#endif
 
       if (player->push_delay == 0)
        player->push_delay = FrameCounter;
@@ -6358,23 +6724,8 @@ int DigField(struct PlayerInfo *player,
                  || !IS_SB_ELEMENT(element))))
        return MF_NO_ACTION;
 
-#if 1
       if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
        return MF_NO_ACTION;
-#else
-      if (dx && real_dy)
-      {
-       if (IN_LEV_FIELD(jx, jy+real_dy) &&
-           !IS_HISTORIC_SOLID(Feld[jx][jy+real_dy]))
-         return MF_NO_ACTION;
-      }
-      else if (dy && real_dx)
-      {
-       if (IN_LEV_FIELD(jx+real_dx, jy) &&
-           !IS_HISTORIC_SOLID(Feld[jx+real_dx][jy]))
-         return MF_NO_ACTION;
-      }
-#endif
 
       if (player->push_delay == 0)
        player->push_delay = FrameCounter;
@@ -6461,7 +6812,41 @@ int DigField(struct PlayerInfo *player,
       break;
 
     default:
-      if (IS_PUSHABLE(element))
+
+      if (IS_WALKABLE(element))
+      {
+       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;
+      }
+      else if (IS_COLLECTIBLE(element))
+      {
+       RemoveField(x, y);
+#if 1
+       if (mode != DF_SNAP)
+       {
+         GfxElement[x][y] = element;
+         player->is_collecting = TRUE;
+       }
+#endif
+       PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+
+       break;
+      }
+      else if (IS_PUSHABLE(element))
       {
        if (mode == DF_SNAP)
          return MF_NO_ACTION;
@@ -6469,30 +6854,19 @@ 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 1
        if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
          return MF_NO_ACTION;
-#else
-       if (dx && real_dy)
-       {
-         if (IN_LEV_FIELD(jx, jy+real_dy) &&
-             !IS_HISTORIC_SOLID(Feld[jx][jy+real_dy]))
-           return MF_NO_ACTION;
-       }
-       else if (dy && real_dx)
-       {
-         if (IN_LEV_FIELD(jx+real_dx, jy) &&
-             !IS_HISTORIC_SOLID(Feld[jx+real_dx][jy]))
-           return MF_NO_ACTION;
-       }
-#endif
 
-       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) &&
@@ -6502,7 +6876,12 @@ int DigField(struct PlayerInfo *player,
        RemoveField(x, y);
        Feld[x + dx][y + dy] = element;
 
+#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);
@@ -6526,6 +6905,9 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
   int jx = player->jx, jy = player->jy;
   int x = jx + dx, y = jy + dy;
 
+  if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0))
+    return FALSE;
+
   if (!player->active || !IN_LEV_FIELD(x, y))
     return FALSE;
 
@@ -6556,7 +6938,7 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
                    dy < 0 ? MV_UP :
                    dy > 0 ? MV_DOWN :  MV_NO_MOVING);
 
-  if (!DigField(player, x, y, 0, 0, DF_SNAP))
+  if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
     return FALSE;
 
   player->snapped = TRUE;
@@ -6758,17 +7140,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:
@@ -6785,8 +7176,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: