rnd-20030629-2-src
[rocksndiamonds.git] / src / game.c
index f29054575911e49267634b141081c3caba569368..b51a18739ef7ed2f4dd3092b061f9b3c5c9e0d2b 100644 (file)
                                        (condition)))
 
 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                               \
-       ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 1)
+       ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0)
 
 #define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y)                       \
        ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID))
@@ -352,6 +352,44 @@ static struct ChangingElementInfo changing_element_list[] =
   }
 };
 
+struct
+{
+  int element;
+  int push_delay_fixed, push_delay_random;
+}
+push_delay_list[] =
+{
+  { EL_SPRING,                 0, 0 },
+  { EL_BALLOON,                        0, 0 },
+
+  { EL_SOKOBAN_OBJECT,         2, 0 },
+  { EL_SOKOBAN_FIELD_FULL,     2, 0 },
+  { EL_SATELLITE,              2, 0 },
+  { EL_SP_DISK_YELLOW,         2, 0 },
+
+  { EL_UNDEFINED,              0, 0 },
+};
+
+struct
+{
+  int element;
+  int gem_count;
+}
+collect_gem_count_list[] =
+{
+  { EL_EMERALD,                1 },
+  { EL_BD_DIAMOND,     1 },
+  { EL_EMERALD_YELLOW, 1 },
+  { EL_EMERALD_RED,    1 },
+  { EL_EMERALD_PURPLE, 1 },
+  { EL_DIAMOND,                3 },
+  { EL_SP_INFOTRON,    1 },
+  { EL_PEARL,          5 },
+  { EL_CRYSTAL,                8 },
+
+  { EL_UNDEFINED,      0 },
+};
+
 static struct ChangingElementInfo changing_element[MAX_NUM_ELEMENTS];
 static unsigned long trigger_events[MAX_NUM_ELEMENTS];
 
@@ -688,6 +726,8 @@ static void InitGameEngine()
     printf("       => game.engine_version == %06d\n", game.engine_version);
 #endif
 
+  /* ---------- initialize player's initial move delay --------------------- */
+
   /* dynamically adjust player properties according to game engine version */
   game.initial_move_delay =
     (game.engine_version <= VERSION_IDENT(2,0,1) ? INITIAL_MOVE_DELAY_ON :
@@ -697,6 +737,8 @@ static void InitGameEngine()
   game.initial_move_delay_value =
     (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
 
+  /* ---------- initialize changing elements ------------------------------- */
+
   /* initialize changing elements information */
   for (i=0; i<MAX_NUM_ELEMENTS; i++)
   {
@@ -709,8 +751,7 @@ static void InitGameEngine()
   }
 
   /* add changing elements from pre-defined list */
-  i = 0;
-  while (changing_element_list[i].base_element != EL_UNDEFINED)
+  for (i=0; changing_element_list[i].base_element != EL_UNDEFINED; i++)
   {
     struct ChangingElementInfo *ce = &changing_element_list[i];
     int element = ce->base_element;
@@ -721,8 +762,6 @@ static void InitGameEngine()
     changing_element[element].pre_change_function  = ce->pre_change_function;
     changing_element[element].change_function      = ce->change_function;
     changing_element[element].post_change_function = ce->post_change_function;
-
-    i++;
   }
 
   /* add changing elements from custom element configuration */
@@ -741,6 +780,8 @@ static void InitGameEngine()
                                              change->delay_frames);
   }
 
+  /* ---------- initialize trigger events ---------------------------------- */
+
   /* initialize trigger events information */
   for (i=0; i<MAX_NUM_ELEMENTS; i++)
     trigger_events[i] = EP_BITMASK_DEFAULT;
@@ -751,32 +792,38 @@ static void InitGameEngine()
       trigger_events[element_info[i].change.trigger] |=
        element_info[i].change.events;
 
-  /* set push delay value for all non-custom elements */
+  /* ---------- initialize push delay -------------------------------------- */
+
+  /* initialize push delay values to default */
   for (i=0; i<MAX_NUM_ELEMENTS; i++)
   {
     if (!IS_CUSTOM_ELEMENT(i))
     {
-      if (i == EL_SPRING ||
-         i == EL_BALLOON)
-      {
-       element_info[i].push_delay_fixed = 0;
-       element_info[i].push_delay_random = 0;
-      }
-      else if (i == EL_SOKOBAN_OBJECT ||
-              i == EL_SOKOBAN_FIELD_FULL ||
-              i == EL_SATELLITE ||
-              i == EL_SP_DISK_YELLOW)
-      {
-       element_info[i].push_delay_fixed = 2;
-       element_info[i].push_delay_random = 0;
-      }
-      else
-      {
-       element_info[i].push_delay_fixed = 2;
-       element_info[i].push_delay_random = 8;
-      }
+      element_info[i].push_delay_fixed = 2;
+      element_info[i].push_delay_random = 8;
     }
   }
+
+  /* set push delay value for certain elements from pre-defined list */
+  for (i=0; push_delay_list[i].element != EL_UNDEFINED; i++)
+  {
+    int e = push_delay_list[i].element;
+
+    element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
+    element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
+  }
+
+  /* ---------- initialize gem count --------------------------------------- */
+
+  /* initialize gem count values for each element */
+  for (i=0; i<MAX_NUM_ELEMENTS; i++)
+    if (!IS_CUSTOM_ELEMENT(i))
+      element_info[i].collect_gem_count = 0;
+
+  /* add gem count values for all elements from pre-defined list */
+  for (i=0; collect_gem_count_list[i].element != EL_UNDEFINED; i++)
+    element_info[collect_gem_count_list[i].element].collect_gem_count =
+      collect_gem_count_list[i].gem_count;
 }
 
 
@@ -1239,7 +1286,9 @@ void InitMovDir(int x, int y)
       {
        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)
+       else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
+                element_info[element].move_pattern == MV_TURNING_LEFT ||
+                element_info[element].move_pattern == MV_TURNING_RIGHT)
          MovDir[x][y] = 1 << RND(4);
        else if (element_info[element].move_pattern == MV_HORIZONTAL)
          MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
@@ -3045,12 +3094,18 @@ void TurnRound(int x, int y)
       }
     }
   }
-  else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS)
+  else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
+          element_info[element].move_pattern == MV_TURNING_LEFT ||
+          element_info[element].move_pattern == MV_TURNING_RIGHT)
   {
     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)
+    if (element_info[element].move_pattern == MV_TURNING_LEFT)
+      MovDir[x][y] = left_dir;
+    else if (element_info[element].move_pattern == MV_TURNING_RIGHT)
+      MovDir[x][y] = right_dir;
+    else 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);
@@ -3353,9 +3408,12 @@ void StartMoving(int x, int y)
             JustStopped[x][y])
 #endif
     {
-      /*
-      printf("::: %d\n", MovDir[x][y]);
-      */
+      /* calling "Impact()" here is not only completely unneccessary
+        (because it already gets called from "ContinueMoving()" in
+        all relevant situations), but also completely bullshit, because
+        "JustStopped" also indicates a finished *horizontal* movement;
+        we must keep this trash for backwards compatibility with older
+        tapes */
 
       Impact(x, y);
     }
@@ -3438,7 +3496,7 @@ void StartMoving(int x, int y)
     }
   }
 
-  /* not "else if" because of EL_SPRING */
+  /* not "else if" because of elements that can fall and move (EL_SPRING) */
   if (CAN_MOVE(element) && !started_moving)
   {
     int newx, newy;
@@ -3471,7 +3529,9 @@ void StartMoving(int x, int y)
       if (element != EL_YAMYAM &&
          element != EL_DARK_YAMYAM &&
          element != EL_PACMAN &&
-         !(element_info[element].move_pattern & MV_ANY_DIRECTION))
+         !(element_info[element].move_pattern & MV_ANY_DIRECTION) &&
+         element_info[element].move_pattern != MV_TURNING_LEFT &&
+         element_info[element].move_pattern != MV_TURNING_RIGHT)
       {
        TurnRound(x, y);
 
@@ -3825,12 +3885,16 @@ void ContinueMoving(int x, int y)
   int horiz_move = (dx != 0);
   int newx = x + dx, newy = y + dy;
   int step = (horiz_move ? dx : dy) * TILEX / MOVE_DELAY_NORMAL_SPEED;
+#if 1
+  boolean pushed = Pushed[x][y];
+#else
   struct PlayerInfo *player = (IS_PLAYER(x, y) ? PLAYERINFO(x, y) : NULL);
 #if 0
   boolean pushing = (player != NULL && player->Pushing && player->MovPos != 0);
 #else
   boolean pushing = (player != NULL && player->Pushing && player->is_moving);
 #endif
+#endif
 
 #if 0
   if (player && player->is_moving && player->MovPos == 0)
@@ -3864,7 +3928,7 @@ void ContinueMoving(int x, int y)
 
 #if 1
 #if 1
-  if (Pushed[x][y])    /* special case: moving object pushed by player */
+  if (pushed)          /* special case: moving object pushed by player */
 #else
   if (pushing)         /* special case: moving object pushed by player */
 #endif
@@ -3994,17 +4058,25 @@ void ContinueMoving(int x, int y)
 
 #if 1
 #if 0
+    /* 2.1.1 (does not work correctly for spring) */
     if (!CAN_MOVE(element))
       MovDir[newx][newy] = 0;
 #else
 
 #if 0
+    /* (does not work for falling objects that slide horizontally) */
     if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
       MovDir[newx][newy] = 0;
 #else
+    /*
     if (!CAN_MOVE(element) ||
        (element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
       MovDir[newx][newy] = 0;
+    */
+
+    if (!CAN_MOVE(element) ||
+       (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN))
+      MovDir[newx][newy] = 0;
 #endif
 
 #endif
@@ -4018,7 +4090,7 @@ void ContinueMoving(int x, int y)
 #endif
       Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
 #if 1
-    if (!pushing)
+    if (!pushed)       /* special case: moving object pushed by player */
 #endif
       JustStopped[newx][newy] = 3;
 
@@ -6389,10 +6461,12 @@ void RemoveHero(struct PlayerInfo *player)
 }
 
 /*
+  =============================================================================
   checkDiagonalPushing()
   -----------------------------------------------------------------------------
   check if diagonal input device direction results in pushing of object
   (by checking if the alternative direction is walkable, diggable, ...)
+  =============================================================================
 */
 
 static boolean checkDiagonalPushing(struct PlayerInfo *player,
@@ -6415,10 +6489,12 @@ static boolean checkDiagonalPushing(struct PlayerInfo *player,
 }
 
 /*
+  =============================================================================
   DigField()
   -----------------------------------------------------------------------------
   x, y:                        field next to player (non-diagonal) to try to dig to
   real_dx, real_dy:    direction as read from input device (can be diagonal)
+  =============================================================================
 */
 
 int DigField(struct PlayerInfo *player,
@@ -6427,6 +6503,7 @@ int DigField(struct PlayerInfo *player,
   boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0));
   int jx = player->jx, jy = player->jy;
   int dx = x - jx, dy = y - jy;
+  int nextx = x + dx, nexty = y + dy;
   int move_direction = (dx == -1 ? MV_LEFT :
                        dx == +1 ? MV_RIGHT :
                        dy == -1 ? MV_UP :
@@ -6451,14 +6528,7 @@ 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]))
@@ -6498,227 +6568,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)
   {
-#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:
-    case EL_TRAP:
-    case EL_SP_BASE:
-    case EL_SP_BUGGY_BASE:
-    case EL_SP_BUGGY_BASE_ACTIVATING:
-      RemoveField(x, y);
-
-      if (mode != DF_SNAP && element != EL_EMPTY)
-      {
-       GfxElement[x][y] = (CAN_BE_CRUMBLED(element) ? EL_SAND : element);
-       player->is_digging = TRUE;
-      }
-
-      PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING);
-      break;
-#endif
-
-    case EL_EMERALD:
-    case EL_BD_DIAMOND:
-    case EL_EMERALD_YELLOW:
-    case EL_EMERALD_RED:
-    case EL_EMERALD_PURPLE:
-    case EL_DIAMOND:
-    case EL_SP_INFOTRON:
-    case EL_PEARL:
-    case EL_CRYSTAL:
-      RemoveField(x, y);
-
-      if (mode != DF_SNAP)
-      {
-       GfxElement[x][y] = element;
-       player->is_collecting = TRUE;
-      }
-
-      local_player->gems_still_needed -= (element == EL_DIAMOND ? 3 :
-                                         element == EL_PEARL ? 5 :
-                                         element == EL_CRYSTAL ? 8 : 1);
-      if (local_player->gems_still_needed < 0)
-       local_player->gems_still_needed = 0;
-      RaiseScoreElement(element);
-      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);
-      if (level.time > 0)
-      {
-       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:
-    case EL_SP_DISK_RED:
-      RemoveField(x, y);
-      player->dynamite++;
-      player->use_disk_red_graphic = (element == EL_SP_DISK_RED);
-      RaiseScoreElement(EL_DYNAMITE);
-      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:
-      RemoveField(x, y);
-      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:
-    case EL_KEY_2:
-    case EL_KEY_3:
-    case EL_KEY_4:
-    {
-      int key_nr = element - EL_KEY_1;
-      int graphic = el2edimg(element);
-
-      RemoveField(x, y);
-      player->key[key_nr] = TRUE;
-      RaiseScoreElement(element);
-      DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
-                        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;
-    }
-
-    case EL_EM_KEY_1:
-    case EL_EM_KEY_2:
-    case EL_EM_KEY_3:
-    case EL_EM_KEY_4:
-    {
-      int key_nr = element - EL_EM_KEY_1;
-      int graphic = el2edimg(EL_KEY_1 + key_nr);
-
-      RemoveField(x, y);
-      player->key[key_nr] = TRUE;
-      RaiseScoreElement(element);
-      DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
-                        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;
-    }
-
     case EL_ROBOT_WHEEL:
       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
       ZX = x;
@@ -6819,171 +6674,6 @@ int DigField(struct PlayerInfo *player,
       return MF_ACTION;
       break;
 
-#if 0
-
-      /* the following elements cannot be pushed by "snapping" */
-    case EL_ROCK:
-    case EL_BOMB:
-    case EL_DX_SUPABOMB:
-    case EL_NUT:
-    case EL_TIME_ORB_EMPTY:
-    case EL_SP_ZONK:
-    case EL_SP_DISK_ORANGE:
-    case EL_SPRING:
-      if (mode == DF_SNAP)
-       return MF_NO_ACTION;
-
-      /* no "break" -- fall through to next case */
-
-      /* the following elements can be pushed by "snapping" */
-    case EL_BD_ROCK:
-      if (dy)
-       return MF_NO_ACTION;
-
-      if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
-         !(element == EL_SPRING && use_spring_bug))
-       return MF_NO_ACTION;
-
-      player->Pushing = TRUE;
-
-#if 0
-      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 (!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 &&
-         element != EL_SPRING)
-       return MF_NO_ACTION;
-#else
-      if (!FrameReached(&player->push_delay, player->push_delay_value) &&
-         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
-         element != EL_SPRING)
-       return MF_NO_ACTION;
-#endif
-
-      if (mode == DF_SNAP)
-      {
-       InitMovingField(x, y, move_direction);
-       ContinueMoving(x, y);
-      }
-      else
-      {
-#if 1
-       InitMovingField(x, y, (dx < 0 ? MV_LEFT :
-                              dx > 0 ? MV_RIGHT :
-                              dy < 0 ? MV_UP : MV_DOWN));
-       MovPos[x][y] = (dx != 0 ? dx : dy);
-#else
-       RemoveField(x, y);
-       Feld[x + dx][y + dy] = element;
-#endif
-      }
-
-#if 0
-      printf("pushing %d/%d ... %d [%d]\n", dx, dy,
-            FrameCounter, player->push_delay_value);
-#endif
-
-#if 0
-      if (element == EL_SPRING)
-      {
-       Feld[x + dx][y + dy] = EL_SPRING;
-       MovDir[x + dx][y + dy] = move_direction;
-      }
-#endif
-
-      player->push_delay_value = (element == EL_SPRING ? 0 : 2 + RND(8));
-
-      DrawLevelField(x + dx, y + dy);
-      PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
-
-      CheckTriggeredElementChange(element, CE_OTHER_PUSHING);
-
-      break;
-
-#endif
-
-    case EL_GATE_1:
-    case EL_GATE_2:
-    case EL_GATE_3:
-    case EL_GATE_4:
-      if (!player->key[element - EL_GATE_1])
-       return MF_NO_ACTION;
-      break;
-
-    case EL_GATE_1_GRAY:
-    case EL_GATE_2_GRAY:
-    case EL_GATE_3_GRAY:
-    case EL_GATE_4_GRAY:
-      if (!player->key[element - EL_GATE_1_GRAY])
-       return MF_NO_ACTION;
-      break;
-
-    case EL_EM_GATE_1:
-    case EL_EM_GATE_2:
-    case EL_EM_GATE_3:
-    case EL_EM_GATE_4:
-      if (!player->key[element - EL_EM_GATE_1])
-       return MF_NO_ACTION;
-      if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy))
-       return MF_NO_ACTION;
-
-      /* automatically move to the next field with double speed */
-      player->programmed_action = move_direction;
-      DOUBLE_PLAYER_SPEED(player);
-
-      PlaySoundLevel(x, y, SND_CLASS_GATE_PASSING);
-      break;
-
-    case EL_EM_GATE_1_GRAY:
-    case EL_EM_GATE_2_GRAY:
-    case EL_EM_GATE_3_GRAY:
-    case EL_EM_GATE_4_GRAY:
-      if (!player->key[element - EL_EM_GATE_1_GRAY])
-       return MF_NO_ACTION;
-      if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy))
-       return MF_NO_ACTION;
-
-      /* automatically move to the next field with double speed */
-      player->programmed_action = move_direction;
-      DOUBLE_PLAYER_SPEED(player);
-
-#if 1
-      PlaySoundLevelAction(x, y, ACTION_PASSING);
-#else
-      PlaySoundLevel(x, y, SND_GATE_PASSING);
-#endif
-      break;
-
-    case EL_SWITCHGATE_OPEN:
-    case EL_TIMEGATE_OPEN:
-      if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy))
-       return MF_NO_ACTION;
-
-      /* automatically move to the next field with double speed */
-      player->programmed_action = move_direction;
-      DOUBLE_PLAYER_SPEED(player);
-
-      PlaySoundLevelElementAction(x, y, element, ACTION_PASSING);
-      break;
-
     case EL_SP_PORT_LEFT:
     case EL_SP_PORT_RIGHT:
     case EL_SP_PORT_UP:
@@ -7015,8 +6705,8 @@ int DigField(struct PlayerInfo *player,
           element != EL_SP_GRAVITY_PORT_DOWN &&
           element != EL_SP_PORT_VERTICAL &&
           element != EL_SP_PORT_ANY) ||
-         !IN_LEV_FIELD(x + dx, y + dy) ||
-         !IS_FREE(x + dx, y + dy))
+         !IN_LEV_FIELD(nextx, nexty) ||
+         !IS_FREE(nextx, nexty))
        return MF_NO_ACTION;
 
       /* automatically move to the next field with double speed */
@@ -7065,28 +6755,10 @@ int DigField(struct PlayerInfo *player,
        if (!(tube_enter_directions[i][1] & move_direction))
          return MF_NO_ACTION;  /* tube has no opening in this direction */
 
-       PlaySoundLevel(x, y, SND_CLASS_TUBE_PASSING);
+       PlaySoundLevel(x, y, SND_CLASS_TUBE_WALKING);
       }
       break;
 
-    case EL_EXIT_CLOSED:
-    case EL_SP_EXIT_CLOSED:
-    case EL_EXIT_OPENING:
-      return MF_NO_ACTION;
-      break;
-
-    case EL_EXIT_OPEN:
-    case EL_SP_EXIT_OPEN:
-      if (mode == DF_SNAP)
-       return MF_NO_ACTION;
-
-      if (element == EL_EXIT_OPEN)
-       PlaySoundLevel(x, y, SND_CLASS_EXIT_PASSING);
-      else
-       PlaySoundLevel(x, y, SND_CLASS_SP_EXIT_PASSING);
-
-      break;
-
     case EL_LAMP:
       Feld[x][y] = EL_LAMP_ACTIVE;
       local_player->lights_still_needed--;
@@ -7104,171 +6776,61 @@ int DigField(struct PlayerInfo *player,
       return MF_ACTION;
       break;
 
-#if 0
-
-#if 0
-    case EL_SOKOBAN_FIELD_EMPTY:
-      break;
-#endif
-
-    case EL_SOKOBAN_OBJECT:
-    case EL_SOKOBAN_FIELD_FULL:
-    case EL_SATELLITE:
-    case EL_SP_DISK_YELLOW:
-    case EL_BALLOON:
-      if (mode == DF_SNAP)
-       return MF_NO_ACTION;
-
-      player->Pushing = TRUE;
-
-      if (!IN_LEV_FIELD(x+dx, y+dy)
-         || (!IS_FREE(x+dx, y+dy)
-             && (Feld[x+dx][y+dy] != EL_SOKOBAN_FIELD_EMPTY
-                 || !IS_SB_ELEMENT(element))))
-       return MF_NO_ACTION;
-
-      if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
-       return MF_NO_ACTION;
-
-      if (player->push_delay == 0)
-       player->push_delay = FrameCounter;
-#if 0
-      if (!FrameReached(&player->push_delay, player->push_delay_value) &&
-         !tape.playing && element != EL_BALLOON)
-       return MF_NO_ACTION;
-#else
-      if (!FrameReached(&player->push_delay, player->push_delay_value) &&
-         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
-         element != EL_BALLOON)
-       return MF_NO_ACTION;
-#endif
+    default:
 
-      if (IS_SB_ELEMENT(element))
+      if (IS_WALKABLE(element))
       {
-#if 1
-       if (element == EL_SOKOBAN_FIELD_FULL)
-       {
-         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
-         local_player->sokobanfields_still_needed++;
-       }
+       int sound_action = ACTION_WALKING;
 
-       if (Feld[x + dx][y + dy] == EL_SOKOBAN_FIELD_EMPTY)
+       if (element >= EL_GATE_1 && element <= EL_GATE_4)
        {
-         Back[x + dx][y + dy] = EL_SOKOBAN_FIELD_EMPTY;
-         local_player->sokobanfields_still_needed--;
+         if (!player->key[element - EL_GATE_1])
+           return MF_NO_ACTION;
        }
-
-       Feld[x][y] = EL_SOKOBAN_OBJECT;
-
-       if (Back[x][y] == Back[x + dx][y + dy])
-         PlaySoundLevelAction(x, y, ACTION_PUSHING);
-       else if (Back[x][y] != 0)
-         PlaySoundLevelElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
-                                     ACTION_EMPTYING);
-       else
-         PlaySoundLevelElementAction(x + dx, y + dy, EL_SOKOBAN_FIELD_EMPTY,
-                                     ACTION_FILLING);
-
-       InitMovingField(x, y, (dx < 0 ? MV_LEFT :
-                              dx > 0 ? MV_RIGHT :
-                              dy < 0 ? MV_UP : MV_DOWN));
-       MovPos[x][y] = (dx != 0 ? dx : dy);
-
-#if 0
-       printf("::: %s -> %s [%s -> %s]\n",
-              element_info[Feld[x][y]].token_name,
-              element_info[Feld[x + dx][y + dy]].token_name,
-              element_info[Back[x][y]].token_name,
-              element_info[Back[x + dx][y + dy]].token_name);
-#endif
-
-#else
-       if (element == EL_SOKOBAN_FIELD_FULL)
+       else if (element >= EL_GATE_1_GRAY && element <= EL_GATE_4_GRAY)
        {
-         Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
-         local_player->sokobanfields_still_needed++;
+         if (!player->key[element - EL_GATE_1_GRAY])
+           return MF_NO_ACTION;
        }
-       else
-         RemoveField(x, y);
-
-       if (Feld[x+dx][y+dy] == EL_SOKOBAN_FIELD_EMPTY)
+       else if (element == EL_EXIT_OPEN || element == EL_SP_EXIT_OPEN)
        {
-         Feld[x+dx][y+dy] = EL_SOKOBAN_FIELD_FULL;
-         local_player->sokobanfields_still_needed--;
-         if (element == EL_SOKOBAN_OBJECT)
-#if 1
-           PlaySoundLevelAction(x+dx, y+dy, ACTION_FILLING);
-#else
-           PlaySoundLevel(x, y, SND_CLASS_SOKOBAN_FIELD_FILLING);
-#endif
-         else
-#if 1
-           PlaySoundLevelAction(x+dx, y+dy, ACTION_PUSHING);
-#else
-           PlaySoundLevel(x, y, SND_SOKOBAN_OBJECT_PUSHING);
-#endif
+         sound_action = ACTION_PASSING;        /* player is passing exit */
        }
-       else
+       else if (element == EL_EMPTY)
        {
-         Feld[x+dx][y+dy] = EL_SOKOBAN_OBJECT;
-         if (element == EL_SOKOBAN_FIELD_FULL)
-#if 1
-           PlaySoundLevelAction(x+dx, y+dy, ACTION_EMPTYING);
-#else
-           PlaySoundLevel(x, y, SND_SOKOBAN_FIELD_EMPTYING);
-#endif
-         else
-#if 1
-           PlaySoundLevelAction(x+dx, y+dy, ACTION_PUSHING);
-#else
-           PlaySoundLevel(x, y, SND_SOKOBAN_OBJECT_PUSHING);
-#endif
+         sound_action = ACTION_MOVING;         /* nothing to walk on */
        }
-#endif
-      }
-      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
-       PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
-      }
-
-      player->push_delay_value = (element == EL_BALLOON ? 0 : 2);
 
-      DrawLevelField(x, y);
-      DrawLevelField(x + dx, y + dy);
+       /* play sound from background or player, whatever is available */
+       if (element_info[element].sound[sound_action] != SND_UNDEFINED)
+         PlaySoundLevelElementAction(x, y, element, sound_action);
+       else
+         PlaySoundLevelElementAction(x, y, player->element_nr, sound_action);
 
-      if (IS_SB_ELEMENT(element) &&
-         local_player->sokobanfields_still_needed == 0 &&
-         game.emulation == EMU_SOKOBAN)
-      {
-       player->LevelSolved = player->GameOver = TRUE;
-       PlaySoundLevel(x, y, SND_GAME_SOKOBAN_SOLVING);
+       break;
       }
+      else if (IS_PASSABLE(element))
+      {
+       if (!IN_LEV_FIELD(nextx, nexty) || !IS_FREE(nextx, nexty))
+         return MF_NO_ACTION;
 
-      CheckTriggeredElementChange(element, CE_OTHER_PUSHING);
-
-      break;
-
-#endif
+       if (element >= EL_EM_GATE_1 && element <= EL_EM_GATE_4)
+       {
+         if (!player->key[element - EL_EM_GATE_1])
+           return MF_NO_ACTION;
+       }
+       else if (element >= EL_EM_GATE_1_GRAY && element <= EL_EM_GATE_4_GRAY)
+       {
+         if (!player->key[element - EL_EM_GATE_1_GRAY])
+           return MF_NO_ACTION;
+       }
 
-    case EL_PENGUIN:
-    case EL_PIG:
-    case EL_DRAGON:
-      break;
+       /* automatically move to the next field with double speed */
+       player->programmed_action = move_direction;
+       DOUBLE_PLAYER_SPEED(player);
 
-    default:
+       PlaySoundLevelAction(x, y, ACTION_PASSING);
 
-      if (IS_WALKABLE(element))
-      {
-       PlaySoundLevelElementAction(x, y, player->element_nr, ACTION_MOVING);
        break;
       }
       else if (IS_DIGGABLE(element))
@@ -7296,8 +6858,64 @@ int DigField(struct PlayerInfo *player,
          player->is_collecting = TRUE;
        }
 
-       RaiseScoreElement(element);
+       if (element == EL_SPEED_PILL)
+         player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
+       else if (element == EL_EXTRA_TIME && level.time > 0)
+       {
+         TimeLeft += 10;
+         DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
+       }
+       else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
+       {
+         player->shield_normal_time_left += 10;
+         if (element == EL_SHIELD_DEADLY)
+           player->shield_deadly_time_left += 10;
+       }
+       else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
+       {
+         player->dynamite++;
+         player->use_disk_red_graphic = (element == EL_SP_DISK_RED);
 
+         DrawText(DX_DYNAMITE, DY_DYNAMITE,
+                  int2str(local_player->dynamite, 3), FONT_TEXT_2);
+       }
+       else if (element == EL_DYNABOMB_INCREASE_NUMBER)
+       {
+         player->dynabomb_count++;
+         player->dynabombs_left++;
+       }
+       else if (element == EL_DYNABOMB_INCREASE_SIZE)
+       {
+         player->dynabomb_size++;
+       }
+       else if (element == EL_DYNABOMB_INCREASE_POWER)
+       {
+         player->dynabomb_xl = TRUE;
+       }
+       else if ((element >= EL_KEY_1 && element <= EL_KEY_4) ||
+                (element >= EL_EM_KEY_1 && element <= EL_EM_KEY_4))
+       {
+         int key_nr = (element >= EL_KEY_1 && element <= EL_KEY_4 ?
+                       element - EL_KEY_1 : element - EL_EM_KEY_1);
+
+         player->key[key_nr] = TRUE;
+
+         DrawMiniGraphicExt(drawto, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
+                            el2edimg(EL_KEY_1 + key_nr));
+         redraw_mask |= REDRAW_DOOR_1;
+       }
+       else if (element_info[element].collect_gem_count > 0)
+       {
+         local_player->gems_still_needed -=
+           element_info[element].collect_gem_count;
+         if (local_player->gems_still_needed < 0)
+           local_player->gems_still_needed = 0;
+
+         DrawText(DX_EMERALDS, DY_EMERALDS,
+                  int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
+       }
+
+       RaiseScoreElement(element);
        PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
 
        CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
@@ -7325,9 +6943,9 @@ int DigField(struct PlayerInfo *player,
 
        player->Pushing = TRUE;
 
-       if (!(IN_LEV_FIELD(x + dx, y + dy) &&
-             (IS_FREE(x + dx, y + dy) ||
-              (Feld[x + dx][y + dy] == EL_SOKOBAN_FIELD_EMPTY &&
+       if (!(IN_LEV_FIELD(nextx, nexty) &&
+             (IS_FREE(nextx, nexty) ||
+              (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
                IS_SB_ELEMENT(element)))))
          return MF_NO_ACTION;
 
@@ -7350,21 +6968,21 @@ int DigField(struct PlayerInfo *player,
            local_player->sokobanfields_still_needed++;
          }
 
-         if (Feld[x + dx][y + dy] == EL_SOKOBAN_FIELD_EMPTY)
+         if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
          {
-           Back[x + dx][y + dy] = EL_SOKOBAN_FIELD_EMPTY;
+           Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
            local_player->sokobanfields_still_needed--;
          }
 
          Feld[x][y] = EL_SOKOBAN_OBJECT;
 
-         if (Back[x][y] == Back[x + dx][y + dy])
+         if (Back[x][y] == Back[nextx][nexty])
            PlaySoundLevelAction(x, y, ACTION_PUSHING);
          else if (Back[x][y] != 0)
            PlaySoundLevelElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
                                        ACTION_EMPTYING);
          else
-           PlaySoundLevelElementAction(x + dx, y + dy, EL_SOKOBAN_FIELD_EMPTY,
+           PlaySoundLevelElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
                                        ACTION_FILLING);
 
          if (local_player->sokobanfields_still_needed == 0 &&
@@ -7385,7 +7003,7 @@ int DigField(struct PlayerInfo *player,
          MovPos[x][y] = (dx != 0 ? dx : dy);
 
        Pushed[x][y] = TRUE;
-       Pushed[x + dx][y + dy] = TRUE;
+       Pushed[nextx][nexty] = TRUE;
 
        if (game.engine_version < RELEASE_IDENT(2,2,0,7))
          player->push_delay_value = GET_NEW_PUSH_DELAY(element);
@@ -7415,6 +7033,10 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
 {
   int jx = player->jx, jy = player->jy;
   int x = jx + dx, y = jy + dy;
+  int snap_direction = (dx == -1 ? MV_LEFT :
+                       dx == +1 ? MV_RIGHT :
+                       dy == -1 ? MV_UP :
+                       dy == +1 ? MV_DOWN : MV_NO_MOVING);
 
   if (player->MovPos && game.engine_version >= VERSION_IDENT(2,2,0))
     return FALSE;
@@ -7444,10 +7066,7 @@ boolean SnapField(struct PlayerInfo *player, int dx, int dy)
   if (player->snapped)
     return FALSE;
 
-  player->MovDir = (dx < 0 ? MV_LEFT :
-                   dx > 0 ? MV_RIGHT :
-                   dy < 0 ? MV_UP :
-                   dy > 0 ? MV_DOWN :  MV_NO_MOVING);
+  player->MovDir = snap_direction;
 
   if (DigField(player, x, y, 0, 0, DF_SNAP) == MF_NO_ACTION)
     return FALSE;
@@ -7687,6 +7306,7 @@ void RaiseScoreElement(int element)
       RaiseScore(level.score[SC_NUT]);
       break;
     case EL_DYNAMITE:
+    case EL_SP_DISK_RED:
     case EL_DYNABOMB_INCREASE_NUMBER:
     case EL_DYNABOMB_INCREASE_SIZE:
     case EL_DYNABOMB_INCREASE_POWER:
@@ -7706,7 +7326,7 @@ void RaiseScoreElement(int element)
       RaiseScore(level.score[SC_KEY]);
       break;
     default:
-      RaiseScore(element_info[element].score);
+      RaiseScore(element_info[element].collect_score);
       break;
   }
 }