rnd-20051217-2-src
[rocksndiamonds.git] / src / game.c
index 1c606b528b6888d76720480a64b8cd0c9c8447cd..cc4dace14930446668c0b72ac43f2750b9f19be2 100644 (file)
 #define USE_NEW_AMOEBA_CODE    FALSE
 
 /* EXPERIMENTAL STUFF */
-#define USE_NEW_STUFF                  (                       * 1)
+#define USE_NEW_STUFF                  (                         1)
 
 #define USE_NEW_SP_SLIPPERY            (USE_NEW_STUFF          * 1)
-#define USE_NEW_COLLECT_COUNT          (USE_NEW_STUFF          * 1)
+#define USE_NEW_CUSTOM_VALUE           (USE_NEW_STUFF          * 1)
 #define USE_NEW_PLAYER_ANIM            (USE_NEW_STUFF          * 1)
 #define USE_NEW_ALL_SLIPPERY           (USE_NEW_STUFF          * 1)
 #define USE_NEW_PLAYER_SPEED           (USE_NEW_STUFF          * 1)
-
+#define USE_NEW_DELAYED_ACTION         (USE_NEW_STUFF          * 1)
+#define USE_NEW_SNAP_DELAY             (USE_NEW_STUFF          * 1)
 
 /* for DigField() */
 #define DF_NO_PUSH             0
                                 RND(element_info[e].move_delay_random))
 #define GET_MAX_MOVE_DELAY(e)  (   (element_info[e].move_delay_fixed) + \
                                    (element_info[e].move_delay_random))
+#define GET_NEW_CUSTOM_VALUE(e)        (   (element_info[e].ce_value_fixed_initial) +\
+                                RND(element_info[e].ce_value_random_initial))
 #define GET_CHANGE_DELAY(c)    (   ((c)->delay_fixed  * (c)->delay_frames) + \
                                 RND((c)->delay_random * (c)->delay_frames))
 
@@ -257,15 +260,15 @@ static void TestIfElementSmashesCustomElement(int, int, int);
 
 static void ChangeElement(int, int, int);
 
-static boolean CheckTriggeredElementChangeExt(int, int, int,int,int);
-#define CheckTriggeredElementChange(e, ev)                             \
-       CheckTriggeredElementChangeExt(e, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
-#define CheckTriggeredElementChangeByPlayer(e, ev, p, s)               \
-       CheckTriggeredElementChangeExt(e, ev, p, s, -1)
-#define CheckTriggeredElementChangeBySide(e, ev, s)                    \
-       CheckTriggeredElementChangeExt(e, ev, CH_PLAYER_ANY, s, -1)
-#define CheckTriggeredElementChangeByPage(e, ev, p)                    \
-       CheckTriggeredElementChangeExt(e, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
+static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
+#define CheckTriggeredElementChange(x, y, e, ev)                       \
+       CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
+#define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)         \
+       CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
+#define CheckTriggeredElementChangeBySide(x, y, e, ev, s)              \
+       CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
+#define CheckTriggeredElementChangeByPage(x, y, e, ev, p)              \
+       CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
 
 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
 #define CheckElementChange(x, y, e, te, ev)                            \
@@ -582,7 +585,7 @@ access_direction_list[] =
   { EL_SP_GRAVITY_OFF_PORT_UP,                                      MV_DOWN },
   { EL_SP_GRAVITY_OFF_PORT_DOWN,                            MV_UP           },
 
-  { EL_UNDEFINED,                      MV_NO_MOVING                         }
+  { EL_UNDEFINED,                      MV_NONE                              }
 };
 
 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
@@ -657,7 +660,7 @@ static int getBeltDirFromBeltSwitchElement(int element)
   static int belt_move_dir[3] =
   {
     MV_LEFT,
-    MV_NO_MOVING,
+    MV_NONE,
     MV_RIGHT
   };
 
@@ -890,8 +893,21 @@ static void InitField(int x, int y, boolean init_game)
       break;
 
     default:
+#if 1
+      if (IS_CUSTOM_ELEMENT(element))
+      {
+       if (CAN_MOVE(element))
+         InitMovDir(x, y);
+
+#if USE_NEW_CUSTOM_VALUE
+       if (!element_info[element].use_last_ce_value)
+         CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
+#endif
+      }
+#else
       if (IS_CUSTOM_ELEMENT(element) && CAN_MOVE(element))
        InitMovDir(x, y);
+#endif
       else if (IS_GROUP_ELEMENT(element))
       {
        struct ElementGroupInfo *group = element_info[element].group;
@@ -917,8 +933,18 @@ static void InitField(int x, int y, boolean init_game)
       break;
   }
 
-#if USE_NEW_COLLECT_COUNT
-  Count[x][y] = element_info[Feld[x][y]].collect_count_initial;
+#if 0
+
+#if USE_NEW_CUSTOM_VALUE
+
+#if 1
+  CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
+#else
+  CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
+#endif
+
+#endif
+
 #endif
 }
 
@@ -1263,6 +1289,9 @@ static void InitGameEngine()
     ei->change->change_function      = ch_delay->change_function;
     ei->change->post_change_function = ch_delay->post_change_function;
 
+    ei->change->can_change = TRUE;
+    ei->change->can_change_or_has_action = TRUE;
+
     ei->has_change_event[CE_DELAY] = TRUE;
 
     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
@@ -1317,6 +1346,8 @@ static void InitGameEngine()
     {
       ei->change_page[j].actual_trigger_element = EL_EMPTY;
       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
+      ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
+      ei->change_page[j].actual_trigger_ce_value = 0;
     }
   }
 
@@ -1495,10 +1526,10 @@ void InitGame()
     player->dynabombs_left = 0;
     player->dynabomb_xl = FALSE;
 
-    player->MovDir = MV_NO_MOVING;
+    player->MovDir = MV_NONE;
     player->MovPos = 0;
     player->GfxPos = 0;
-    player->GfxDir = MV_NO_MOVING;
+    player->GfxDir = MV_NONE;
     player->GfxAction = ACTION_DEFAULT;
     player->Frame = 0;
     player->StepFrame = 0;
@@ -1514,7 +1545,7 @@ void InitGame()
 
     player->step_counter = 0;
 
-    player->last_move_dir = MV_NO_MOVING;
+    player->last_move_dir = MV_NONE;
 
     player->is_waiting = FALSE;
     player->is_moving = FALSE;
@@ -1584,6 +1615,8 @@ void InitGame()
     player->move_delay       = game.initial_move_delay;
     player->move_delay_value = game.initial_move_delay_value;
 
+    player->move_delay_value_next = -1;
+
     player->move_delay_reset_counter = 0;
 
     player->push_delay       = -1;     /* initialized when pushing starts */
@@ -1612,7 +1645,7 @@ void InitGame()
 #if defined(NETWORK_AVALIABLE)
   /* initial null action */
   if (network_playing)
-    SendToServer_MovePlayer(MV_NO_MOVING);
+    SendToServer_MovePlayer(MV_NONE);
 #endif
 
   ZX = ZY = -1;
@@ -1624,7 +1657,7 @@ void InitGame()
   TimeLeft = level.time;
   TapeTime = 0;
 
-  ScreenMovDir = MV_NO_MOVING;
+  ScreenMovDir = MV_NONE;
   ScreenMovPos = 0;
   ScreenGfxPos = 0;
 
@@ -1638,7 +1671,7 @@ void InitGame()
   game.light_time_left = 0;
   game.timegate_time_left = 0;
   game.switchgate_pos = 0;
-  game.balloon_dir = MV_NO_MOVING;
+  game.wind_direction = level.wind_direction_initial;
   game.gravity = level.initial_gravity;
   game.explosions_delayed = TRUE;
 
@@ -1646,7 +1679,7 @@ void InitGame()
 
   for (i = 0; i < NUM_BELTS; i++)
   {
-    game.belt_dir[i] = MV_NO_MOVING;
+    game.belt_dir[i] = MV_NONE;
     game.belt_dir_nr[i] = 3;           /* not moving, next moving left */
   }
 
@@ -1661,8 +1694,8 @@ void InitGame()
       MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
       ChangeDelay[x][y] = 0;
       ChangePage[x][y] = -1;
-#if USE_NEW_COLLECT_COUNT
-      Count[x][y] = 0;         /* initialized in InitField() */
+#if USE_NEW_CUSTOM_VALUE
+      CustomValue[x][y] = 0;           /* initialized in InitField() */
 #endif
       Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
       AmoebaNr[x][y] = 0;
@@ -1686,7 +1719,7 @@ void InitGame()
       GfxRandom[x][y] = INIT_GFX_RANDOM();
       GfxElement[x][y] = EL_UNDEFINED;
       GfxAction[x][y] = ACTION_DEFAULT;
-      GfxDir[x][y] = MV_NO_MOVING;
+      GfxDir[x][y] = MV_NONE;
     }
   }
 
@@ -1761,7 +1794,7 @@ void InitGame()
 
   /* correct non-moving belts to start moving left */
   for (i = 0; i < NUM_BELTS; i++)
-    if (game.belt_dir[i] == MV_NO_MOVING)
+    if (game.belt_dir[i] == MV_NONE)
       game.belt_dir_nr[i] = 3;         /* not moving, next moving left */
 
   /* check if any connected player was not found in playfield */
@@ -1928,7 +1961,7 @@ void InitGame()
 
       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
       {
-       content = element_info[element].content[xx][yy];
+       content = element_info[element].content.e[xx][yy];
        is_player = ELEM_IS_PLAYER(content);
 
        if (is_player && (found_rating < 2 || element < found_element))
@@ -1945,7 +1978,9 @@ void InitGame()
 
        for (i = 0; i < element_info[element].num_change_pages; i++)
        {
-         content= element_info[element].change_page[i].target_content[xx][yy];
+         content =
+           element_info[element].change_page[i].target_content.e[xx][yy];
+
          is_player = ELEM_IS_PLAYER(content);
 
          if (is_player && (found_rating < 1 || element < found_element))
@@ -2140,7 +2175,7 @@ void InitMovDir(int x, int y)
 
        if (move_direction_initial == MV_START_PREVIOUS)
        {
-         if (MovDir[x][y] != MV_NO_MOVING)
+         if (MovDir[x][y] != MV_NONE)
            return;
 
          move_direction_initial = MV_START_AUTOMATIC;
@@ -2527,8 +2562,8 @@ void InitMovingField(int x, int y, int direction)
 
     MovDir[newx][newy] = MovDir[x][y];
 
-#if USE_NEW_COLLECT_COUNT
-    Count[newx][newy] = Count[x][y];
+#if USE_NEW_CUSTOM_VALUE
+    CustomValue[newx][newy] = CustomValue[x][y];
 #endif
 
     GfxFrame[newx][newy] = GfxFrame[x][y];
@@ -2612,8 +2647,8 @@ static void RemoveField(int x, int y)
   MovDir[x][y] = 0;
   MovDelay[x][y] = 0;
 
-#if USE_NEW_COLLECT_COUNT
-  Count[x][y] = 0;
+#if USE_NEW_CUSTOM_VALUE
+  CustomValue[x][y] = 0;
 #endif
 
   AmoebaNr[x][y] = 0;
@@ -2627,7 +2662,7 @@ static void RemoveField(int x, int y)
 
   GfxElement[x][y] = EL_UNDEFINED;
   GfxAction[x][y] = ACTION_DEFAULT;
-  GfxDir[x][y] = MV_NO_MOVING;
+  GfxDir[x][y] = MV_NONE;
 }
 
 void RemoveMovingField(int x, int y)
@@ -2884,7 +2919,8 @@ void RelocatePlayer(int jx, int jy, int el_player_raw)
                               CE_LEFT_BY_PLAYER,
                               player->index_bit, leave_side);
 
-  CheckTriggeredElementChangeByPlayer(old_element, CE_PLAYER_LEAVES_X,
+  CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
+                                     CE_PLAYER_LEAVES_X,
                                      player->index_bit, leave_side);
 
   Feld[jx][jy] = el_player;
@@ -2906,7 +2942,7 @@ void RelocatePlayer(int jx, int jy, int el_player_raw)
     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
                               player->index_bit, enter_side);
 
-  CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_ENTERS_X,
+  CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
                                      player->index_bit, enter_side);
 }
 
@@ -3061,10 +3097,10 @@ void Explode(int ex, int ey, int phase, int mode)
       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][xx][yy];
+       Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
       else if (IS_CUSTOM_ELEMENT(center_element) &&
-              element_info[center_element].content[xx][yy] != EL_EMPTY)
-       Store[x][y] = element_info[center_element].content[xx][yy];
+              element_info[center_element].content.e[xx][yy] != EL_EMPTY)
+       Store[x][y] = element_info[center_element].content.e[xx][yy];
       else if (element == EL_WALL_EMERALD)
        Store[x][y] = EL_EMERALD;
       else if (element == EL_WALL_DIAMOND)
@@ -3082,7 +3118,7 @@ void Explode(int ex, int ey, int phase, int mode)
       else if (element == EL_WALL_CRYSTAL)
        Store[x][y] = EL_CRYSTAL;
       else if (IS_CUSTOM_ELEMENT(element) && !CAN_EXPLODE(element))
-       Store[x][y] = element_info[element].content[1][1];
+       Store[x][y] = element_info[element].content.e[1][1];
       else
        Store[x][y] = EL_EMPTY;
 
@@ -3198,12 +3234,12 @@ void Explode(int ex, int ey, int phase, int mode)
     Back[x][y] = 0;
 
     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
-    GfxDir[x][y] = MV_NO_MOVING;
+    GfxDir[x][y] = MV_NONE;
     ChangeDelay[x][y] = 0;
     ChangePage[x][y] = -1;
 
-#if USE_NEW_COLLECT_COUNT
-    Count[x][y] = 0;
+#if USE_NEW_CUSTOM_VALUE
+    CustomValue[x][y] = 0;
 #endif
 
     InitField_WithBug2(x, y, FALSE);
@@ -3349,7 +3385,7 @@ void Bang(int x, int y)
       break;
   }
 
-  CheckTriggeredElementChange(element, CE_EXPLOSION_OF_X);
+  CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
 }
 
 void SplashAcid(int x, int y)
@@ -3411,7 +3447,7 @@ static void InitBeltMovement()
 
       for (i = 0; i < NUM_BELTS; i++)
       {
-       if (IS_BELT(element) && game.belt_dir[i] != MV_NO_MOVING)
+       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
        {
          int e_belt_nr = getBeltNrFromBeltElement(element);
          int belt_nr = i;
@@ -3454,9 +3490,9 @@ static void ToggleBeltSwitch(int x, int y)
   static int belt_move_dir[4] =
   {
     MV_LEFT,
-    MV_NO_MOVING,
+    MV_NONE,
     MV_RIGHT,
-    MV_NO_MOVING,
+    MV_NONE,
   };
 
   int element = Feld[x][y];
@@ -3502,7 +3538,7 @@ static void ToggleBeltSwitch(int x, int y)
          DrawLevelField(xx, yy);
        }
       }
-      else if (IS_BELT(element) && belt_dir != MV_NO_MOVING)
+      else if (IS_BELT(element) && belt_dir != MV_NONE)
       {
        int e_belt_nr = getBeltNrFromBeltElement(element);
 
@@ -3514,7 +3550,7 @@ static void ToggleBeltSwitch(int x, int y)
          DrawLevelField(xx, yy);
        }
       }
-      else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NO_MOVING)
+      else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
       {
        int e_belt_nr = getBeltNrFromBeltActiveElement(element);
 
@@ -3878,7 +3914,7 @@ void Impact(int x, int y)
 
          CheckElementChangeBySide(x, y + 1, smashed, element,
                                   CE_SWITCHED, CH_SIDE_TOP);
-         CheckTriggeredElementChangeBySide(smashed, CE_SWITCH_OF_X,
+         CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
                                            CH_SIDE_TOP);
        }
       }
@@ -4167,7 +4203,7 @@ inline static void TurnRoundExt(int x, int y)
   }
   else if (element == EL_BALLOON)
   {
-    MovDir[x][y] = game.balloon_dir;
+    MovDir[x][y] = game.wind_direction;
     MovDelay[x][y] = 0;
   }
   else if (element == EL_SPRING)
@@ -4175,7 +4211,7 @@ inline static void TurnRoundExt(int x, int y)
     if (MovDir[x][y] & MV_HORIZONTAL &&
        (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
         SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
-      MovDir[x][y] = MV_NO_MOVING;
+      MovDir[x][y] = MV_NONE;
 
     MovDelay[x][y] = 0;
   }
@@ -4244,7 +4280,7 @@ inline static void TurnRoundExt(int x, int y)
       }
     }
 
-    MovDir[x][y] = MV_NO_MOVING;
+    MovDir[x][y] = MV_NONE;
     if (attr_x < x)
       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
     else if (attr_x > x)
@@ -4381,6 +4417,11 @@ inline static void TurnRoundExt(int x, int y)
     MovDir[x][y] = move_pattern;
     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
   }
+  else if (move_pattern & MV_WIND_DIRECTION)
+  {
+    MovDir[x][y] = game.wind_direction;
+    MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
+  }
   else if (move_pattern == MV_ALONG_LEFT_SIDE)
   {
     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
@@ -4434,7 +4475,7 @@ inline static void TurnRoundExt(int x, int y)
       }
     }
 
-    MovDir[x][y] = MV_NO_MOVING;
+    MovDir[x][y] = MV_NONE;
     if (attr_x < x)
       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
     else if (attr_x > x)
@@ -4480,7 +4521,7 @@ inline static void TurnRoundExt(int x, int y)
           move_pattern == MV_WHEN_DROPPED)
   {
     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
-      MovDir[x][y] = MV_NO_MOVING;
+      MovDir[x][y] = MV_NONE;
 
     MovDelay[x][y] = 0;
   }
@@ -4508,7 +4549,7 @@ inline static void TurnRoundExt(int x, int y)
     };
     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
     int move_preference = -1000000;    /* start with very low preference */
-    int new_move_dir = MV_NO_MOVING;
+    int new_move_dir = MV_NONE;
     int start_test = RND(4);
     int i;
 
@@ -4757,7 +4798,7 @@ void StartMoving(int x, int y)
     }
     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
     {
-      if (MovDir[x][y] == MV_NO_MOVING)
+      if (MovDir[x][y] == MV_NONE)
       {
        InitMovingField(x, y, MV_DOWN);
        started_moving = TRUE;
@@ -4899,7 +4940,7 @@ void StartMoving(int x, int y)
 
   /* not "else if" because of elements that can fall and move (EL_SPRING) */
 #if 0
-  if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NO_MOVING)
+  if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
 #else
   if (CAN_MOVE(element) && !started_moving)
 #endif
@@ -4909,7 +4950,7 @@ void StartMoving(int x, int y)
 
 #if 0
 #if DEBUG
-    if (MovDir[x][y] == MV_NO_MOVING)
+    if (MovDir[x][y] == MV_NONE)
     {
       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
             x, y, element, element_info[element].token_name);
@@ -5096,7 +5137,7 @@ void StartMoving(int x, int y)
        if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MF_MOVING)
          DrawLevelField(newx, newy);
        else
-         GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
+         GfxDir[x][y] = MovDir[x][y] = MV_NONE;
       }
       else if (!IS_FREE(newx, newy))
       {
@@ -5440,7 +5481,7 @@ void ContinueMoving(int x, int y)
       Feld[x][y] = EL_MAGIC_WALL_DEAD;
     element = Feld[newx][newy] = Store[x][y];
 
-#if USE_NEW_COLLECT_COUNT
+#if USE_NEW_CUSTOM_VALUE
     InitField(newx, newy, FALSE);
 #endif
   }
@@ -5458,7 +5499,7 @@ void ContinueMoving(int x, int y)
       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
     element = Feld[newx][newy] = Store[x][y];
 
-#if USE_NEW_COLLECT_COUNT
+#if USE_NEW_CUSTOM_VALUE
     InitField(newx, newy, FALSE);
 #endif
   }
@@ -5493,8 +5534,8 @@ void ContinueMoving(int x, int y)
     Changed[newx][newy]     = Changed[x][y];
     ChangeEvent[newx][newy] = ChangeEvent[x][y];
 
-#if USE_NEW_COLLECT_COUNT
-    Count[newx][newy] = Count[x][y];
+#if USE_NEW_CUSTOM_VALUE
+    CustomValue[newx][newy] = CustomValue[x][y];
 #endif
   }
 
@@ -5503,8 +5544,8 @@ void ContinueMoving(int x, int y)
   Changed[x][y] = FALSE;
   ChangeEvent[x][y] = -1;
 
-#if USE_NEW_COLLECT_COUNT
-  Count[x][y] = 0;
+#if USE_NEW_CUSTOM_VALUE
+  CustomValue[x][y] = 0;
 #endif
 
   /* copy animation control values to new field */
@@ -5606,7 +5647,7 @@ void ContinueMoving(int x, int y)
 
     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
                               player->index_bit, dig_side);
-    CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PUSHES_X,
+    CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
                                        player->index_bit, dig_side);
   }
 
@@ -6023,10 +6064,14 @@ void AmoebeAbleger(int ax, int ay)
 void Life(int ax, int ay)
 {
   int x1, y1, x2, y2;
+#if 0
   static int life[4] = { 2, 3, 3, 3 }; /* parameters for "game of life" */
+#endif
   int life_time = 40;
   int element = Feld[ax][ay];
   int graphic = el2img(element);
+  int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
+                        level.biomaze);
   boolean changed = FALSE;
 
   if (IS_ANIMATED(graphic))
@@ -6069,7 +6114,8 @@ void Life(int ax, int ay)
 
     if (xx == ax && yy == ay)          /* field in the middle */
     {
-      if (nachbarn < life[0] || nachbarn > life[1])
+      if (nachbarn < life_parameter[0] ||
+         nachbarn > life_parameter[1])
       {
        Feld[xx][yy] = EL_EMPTY;
        if (!Stop[xx][yy])
@@ -6080,7 +6126,8 @@ void Life(int ax, int ay)
     }
     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
     {                                  /* free border field */
-      if (nachbarn >= life[2] && nachbarn <= life[3])
+      if (nachbarn >= life_parameter[2] &&
+         nachbarn <= life_parameter[3])
       {
        Feld[xx][yy] = element;
        MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
@@ -6273,7 +6320,7 @@ void MauerWaechst(int x, int y)
 
       Feld[x][y] = Store[x][y];
       Store[x][y] = 0;
-      GfxDir[x][y] = MovDir[x][y] = MV_NO_MOVING;
+      GfxDir[x][y] = MovDir[x][y] = MV_NONE;
       DrawLevelField(x, y);
     }
   }
@@ -6541,47 +6588,51 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
 
   int action_arg_number_max =
     (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MAX :
-     action_type == CA_SET_GEMS ? 999 :
-     action_type == CA_SET_TIME ? 9999 :
-     action_type == CA_SET_SCORE ? 99999 :
+     action_type == CA_SET_LEVEL_GEMS ? 999 :
+     action_type == CA_SET_LEVEL_TIME ? 9999 :
+     action_type == CA_SET_LEVEL_SCORE ? 99999 :
      action_type == CA_SET_CE_SCORE ? 9999 :
-     action_type == CA_SET_CE_COUNT ? 9999 :
+     action_type == CA_SET_CE_VALUE ? 9999 :
      CA_ARG_MAX);
 
   int action_arg_number_reset =
     (action_type == CA_SET_PLAYER_SPEED ? TILEX/game.initial_move_delay_value :
-     action_type == CA_SET_GEMS ? level.gems_needed :
-     action_type == CA_SET_TIME ? level.time :
-     action_type == CA_SET_SCORE ? 0 :
+     action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
+     action_type == CA_SET_LEVEL_TIME ? level.time :
+     action_type == CA_SET_LEVEL_SCORE ? 0 :
      action_type == CA_SET_CE_SCORE ? 0 :
-     action_type == CA_SET_CE_COUNT ? ei->collect_count_initial :
+#if 1
+     action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) :
+#else
+     action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
+#endif
      0);
 
-  int action_arg_number_normal =
-    (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_NORMAL :
-     action_arg_number_reset);
-
   int action_arg_number =
     (action_arg <= CA_ARG_MAX ? action_arg :
+     action_arg >= CA_ARG_SPEED_VERY_SLOW &&
+     action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
+     action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
-     action_arg == CA_ARG_NUMBER_NORMAL ? action_arg_number_normal :
      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
-#if USE_NEW_COLLECT_COUNT
-     action_arg == CA_ARG_NUMBER_CE_COUNT ? Count[x][y] :
+#if USE_NEW_CUSTOM_VALUE
+     action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
 #else
-     action_arg == CA_ARG_NUMBER_CE_COUNT ? ei->collect_count_initial :
+     action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
 #endif
      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CHANGE_DELAY(change) :
+     action_arg == CA_ARG_ELEMENT_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
+     action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_ce_value :
      -1);
 
   int action_arg_number_old =
-    (action_type == CA_SET_GEMS ? local_player->gems_still_needed :
-     action_type == CA_SET_TIME ? TimeLeft :
-     action_type == CA_SET_SCORE ? local_player->score :
+    (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
+     action_type == CA_SET_LEVEL_TIME ? TimeLeft :
+     action_type == CA_SET_LEVEL_SCORE ? local_player->score :
      action_type == CA_SET_CE_SCORE ? ei->collect_score :
-     action_type == CA_SET_CE_COUNT ? Count[x][y] :
+     action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
      0);
 
   int action_arg_number_new =
@@ -6589,19 +6640,17 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
                            action_mode, action_arg_number,
                            action_arg_number_min, action_arg_number_max);
 
-  /* (for explicit player choice, set invalid value to "no player") */
   int action_arg_player_bits =
-    (action_arg == CA_ARG_PLAYER_ANY ? action_arg - CA_ARG_PLAYER :
+    (action_arg == CA_ARG_PLAYER_ANY ? PLAYER_BITS_ANY :
      action_arg >= CA_ARG_PLAYER_1 &&
      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
      action_arg >= CA_ARG_1 &&
-     action_arg <= CA_ARG_PLAYER_4 ? (1 << (action_arg - 1)) :
+     action_arg <= CA_ARG_PLAYER_4 ? (1 << (action_arg - CA_ARG_1)) :
      action_arg_element >= EL_PLAYER_1 &&
      action_arg_element <= EL_PLAYER_4 ?
      (1 << (action_arg_element - EL_PLAYER_1)) :
-     0);
+     PLAYER_BITS_ANY);
 
-  /* (for implicit player choice, set invalid value to "all players") */
   int trigger_player_bits =
     (change->actual_trigger_player >= EL_PLAYER_1 &&
      change->actual_trigger_player <= EL_PLAYER_4 ?
@@ -6676,7 +6725,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       break;
     }
 
-    case CA_DEL_KEY:
+    case CA_REMOVE_KEY:
     {
       int element = getSpecialActionElement(action_arg_element,
                                            action_arg_number, EL_KEY_1);
@@ -6699,6 +6748,46 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       break;
     }
 
+#if 1
+    case CA_SET_PLAYER_SPEED:
+    {
+      for (i = 0; i < MAX_PLAYERS; i++)
+      {
+       if (trigger_player_bits & (1 << i))
+       {
+         int move_stepsize = TILEX / stored_player[i].move_delay_value;
+
+         if (action_arg == CA_ARG_SPEED_SLOWER ||
+             action_arg == CA_ARG_SPEED_FASTER)
+         {
+           action_arg_number = 2;
+           action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
+                          CA_MODE_MULTIPLY);
+         }
+
+         move_stepsize =
+           getModifiedActionNumber(move_stepsize,
+                                   action_mode,
+                                   action_arg_number,
+                                   action_arg_number_min,
+                                   action_arg_number_max);
+
+         /* make sure that value is power of 2 */
+         move_stepsize = (1 << log_2(move_stepsize));
+
+         /* do no immediately change -- the player might just be moving */
+         stored_player[i].move_delay_value_next = TILEX / move_stepsize;
+
+#if 0
+         printf("::: move_delay_value == %d [%d]\n",
+                stored_player[i].move_delay_value_next, action_arg_number);
+#endif
+       }
+      }
+
+      break;
+    }
+#else
     case CA_SET_PLAYER_SPEED:
     {
       for (i = 0; i < MAX_PLAYERS; i++)
@@ -6725,19 +6814,42 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
          /* make sure that value is power of 2 */
          move_stepsize = (1 << log_2(move_stepsize));
 
-         stored_player[i].move_delay_value = TILEX / move_stepsize;
+         /* do no immediately change -- the player might just be moving */
+         stored_player[i].move_delay_value_next = TILEX / move_stepsize;
 
 #if 0
          printf("::: move_delay_value == %d [%d]\n",
-                stored_player[i].move_delay_value, action_arg_number);
+                stored_player[i].move_delay_value_next, action_arg_number);
 #endif
        }
       }
 
       break;
     }
+#endif
+
+    case CA_SET_PLAYER_GRAVITY:
+    {
+      game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
+                     action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
+                     action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
+                     game.gravity);
+      break;
+    }
+
+    case CA_SET_WIND_DIRECTION:
+    {
+      game.wind_direction = (action_arg >= CA_ARG_DIRECTION_NONE &&
+                            action_arg <= CA_ARG_DIRECTION_DOWN ?
+                            action_arg - CA_ARG_DIRECTION :
+                            action_arg == CA_ARG_DIRECTION_TRIGGER ?
+                            MV_DIR_OPPOSITE(change->actual_trigger_side) :
+                            game.wind_direction);
+
+      break;
+    }
 
-    case CA_SET_GEMS:
+    case CA_SET_LEVEL_GEMS:
     {
       local_player->gems_still_needed = action_arg_number_new;
 
@@ -6746,7 +6858,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       break;
     }
 
-    case CA_SET_TIME:
+    case CA_SET_LEVEL_TIME:
     {
       if (level.time > 0)      /* only modify limited time value */
       {
@@ -6762,7 +6874,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       break;
     }
 
-    case CA_SET_SCORE:
+    case CA_SET_LEVEL_SCORE:
     {
       local_player->score = action_arg_number_new;
 
@@ -6778,31 +6890,32 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       break;
     }
 
-    case CA_SET_CE_COUNT:
+    case CA_SET_CE_VALUE:
     {
-#if USE_NEW_COLLECT_COUNT
-      int count_last = Count[x][y];
+#if USE_NEW_CUSTOM_VALUE
+      int last_custom_value = CustomValue[x][y];
 
-      Count[x][y] = action_arg_number_new;
+      CustomValue[x][y] = action_arg_number_new;
 
 #if 0
-      printf("::: Count == %d\n", Count[x][y]);
+      printf("::: Count == %d\n", CustomValue[x][y]);
 #endif
 
-      if (Count[x][y] == 0 && count_last > 0)
+      if (CustomValue[x][y] == 0 && last_custom_value > 0)
       {
 #if 0
-       printf("::: CE_COUNT_AT_ZERO\n");
+       printf("::: CE_VALUE_GETS_ZERO\n");
 #endif
 
-       CheckElementChange(x, y, element, EL_UNDEFINED, CE_COUNT_AT_ZERO);
-       CheckTriggeredElementChange(element, CE_COUNT_AT_ZERO_OF_X);
+       CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
+       CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
       }
 #endif
 
       break;
     }
 
+#if 0
     case CA_SET_DYNABOMB_NUMBER:
     {
       printf("::: CA_SET_DYNABOMB_NUMBER -- not yet implemented\n");
@@ -6823,27 +6936,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
 
       break;
     }
-
-    case CA_TOGGLE_PLAYER_GRAVITY:
-    {
-      game.gravity = !game.gravity;
-
-      break;
-    }
-
-    case CA_ENABLE_PLAYER_GRAVITY:
-    {
-      game.gravity = TRUE;
-
-      break;
-    }
-
-    case CA_DISABLE_PLAYER_GRAVITY:
-    {
-      game.gravity = FALSE;
-
-      break;
-    }
+#endif
 
     default:
       break;
@@ -6854,6 +6947,9 @@ static void ChangeElementNowExt(struct ElementChangeInfo *change,
                                int x, int y, int target_element)
 {
   int previous_move_direction = MovDir[x][y];
+#if USE_NEW_CUSTOM_VALUE
+  int last_ce_value = CustomValue[x][y];
+#endif
   boolean add_player = (ELEM_IS_PLAYER(target_element) &&
                        IS_WALKABLE(Feld[x][y]));
 
@@ -6881,6 +6977,11 @@ static void ChangeElementNowExt(struct ElementChangeInfo *change,
     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
       MovDir[x][y] = previous_move_direction;
 
+#if USE_NEW_CUSTOM_VALUE
+    if (element_info[Feld[x][y]].use_last_ce_value)
+      CustomValue[x][y] = last_ce_value;
+#endif
+
     InitField_WithBug1(x, y, FALSE);
 
     DrawLevelField(x, y);
@@ -6919,6 +7020,8 @@ static boolean ChangeElementNow(int x, int y, int element, int page)
     /* reset actual trigger element, trigger player and action element */
     change->actual_trigger_element = EL_EMPTY;
     change->actual_trigger_player = EL_PLAYER_1;
+    change->actual_trigger_side = CH_SIDE_NONE;
+    change->actual_trigger_ce_value = 0;
   }
 
 #if 1
@@ -6960,7 +7063,7 @@ static boolean ChangeElementNow(int x, int y, int element, int page)
       boolean is_destructible;
       int ex = x + xx - 1;
       int ey = y + yy - 1;
-      int content_element = change->target_content[xx][yy];
+      int content_element = change->target_content.e[xx][yy];
       int e;
 
       can_replace[xx][yy] = TRUE;
@@ -7032,7 +7135,7 @@ static boolean ChangeElementNow(int x, int y, int element, int page)
 
          ChangeEvent[ex][ey] = ChangeEvent[x][y];
 
-         content_element = change->target_content[xx][yy];
+         content_element = change->target_content.e[xx][yy];
          target_element = GET_TARGET_ELEMENT(content_element, change);
 
          ChangeElementNowExt(change, ex, ey, target_element);
@@ -7046,7 +7149,10 @@ static boolean ChangeElementNow(int x, int y, int element, int page)
       }
 
       if (something_has_changed)
+      {
        PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
+       PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
+      }
     }
   }
   else
@@ -7056,14 +7162,109 @@ static boolean ChangeElementNow(int x, int y, int element, int page)
     ChangeElementNowExt(change, x, y, target_element);
 
     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
+    PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
   }
 
   /* this uses direct change before indirect change */
-  CheckTriggeredElementChangeByPage(old_element, CE_CHANGE_OF_X, page);
+  CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
 
   return TRUE;
 }
 
+#if USE_NEW_DELAYED_ACTION
+
+static void ChangeElement(int x, int y, int page)
+{
+  int element = MovingOrBlocked2Element(x, y);
+  struct ElementInfo *ei = &element_info[element];
+  struct ElementChangeInfo *change = &ei->change_page[page];
+
+#ifdef DEBUG
+  if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
+      !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
+  {
+    printf("\n\n");
+    printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
+          x, y, element, element_info[element].token_name);
+    printf("ChangeElement(): This should never happen!\n");
+    printf("\n\n");
+  }
+#endif
+
+  /* this can happen with classic bombs on walkable, changing elements */
+  if (!CAN_CHANGE_OR_HAS_ACTION(element))
+  {
+#if 0
+    if (!CAN_CHANGE(Back[x][y]))       /* prevent permanent repetition */
+      ChangeDelay[x][y] = 0;
+#endif
+
+    return;
+  }
+
+  if (ChangeDelay[x][y] == 0)          /* initialize element change */
+  {
+    ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
+
+    if (change->can_change)
+    {
+      ResetGfxAnimation(x, y);
+      ResetRandomAnimationValue(x, y);
+
+      if (change->pre_change_function)
+       change->pre_change_function(x, y);
+    }
+  }
+
+  ChangeDelay[x][y]--;
+
+  if (ChangeDelay[x][y] != 0)          /* continue element change */
+  {
+    if (change->can_change)
+    {
+      int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+
+      if (IS_ANIMATED(graphic))
+       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+
+      if (change->change_function)
+       change->change_function(x, y);
+    }
+  }
+  else                                 /* finish element change */
+  {
+    if (ChangePage[x][y] != -1)                /* remember page from delayed change */
+    {
+      page = ChangePage[x][y];
+      ChangePage[x][y] = -1;
+
+      change = &ei->change_page[page];
+    }
+
+    if (IS_MOVING(x, y))               /* never change a running system ;-) */
+    {
+      ChangeDelay[x][y] = 1;           /* try change after next move step */
+      ChangePage[x][y] = page;         /* remember page to use for change */
+
+      return;
+    }
+
+    if (change->can_change)
+    {
+      if (ChangeElementNow(x, y, element, page))
+      {
+       if (change->post_change_function)
+         change->post_change_function(x, y);
+      }
+    }
+
+    if (change->has_action)
+      ExecuteCustomElementAction(x, y, element, page);
+  }
+}
+
+#else
+
 static void ChangeElement(int x, int y, int page)
 {
   int element = MovingOrBlocked2Element(x, y);
@@ -7141,7 +7342,10 @@ static void ChangeElement(int x, int y, int page)
   }
 }
 
-static boolean CheckTriggeredElementChangeExt(int trigger_element,
+#endif
+
+static boolean CheckTriggeredElementChangeExt(int x, int y,
+                                             int trigger_element,
                                              int trigger_event,
                                              int trigger_player,
                                              int trigger_side,
@@ -7177,6 +7381,8 @@ static boolean CheckTriggeredElementChangeExt(int trigger_element,
       {
        change->actual_trigger_element = trigger_element;
        change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
+       change->actual_trigger_side = trigger_side;
+       change->actual_trigger_ce_value = CustomValue[x][y];
 
        if ((change->can_change && !change_done) || change->has_action)
        {
@@ -7192,9 +7398,19 @@ static boolean CheckTriggeredElementChangeExt(int trigger_element,
                ChangeEvent[x][y] = trigger_event;
                ChangeElement(x, y, p);
              }
-
+#if USE_NEW_DELAYED_ACTION
+             else if (change->has_action)
+             {
+               ExecuteCustomElementAction(x, y, element, p);
+               PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
+             }
+#else
              if (change->has_action)
+             {
                ExecuteCustomElementAction(x, y, element, p);
+               PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
+             }
+#endif
            }
          }
 
@@ -7252,6 +7468,8 @@ static boolean CheckElementChangeExt(int x, int y,
     {
       change->actual_trigger_element = trigger_element;
       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
+      change->actual_trigger_side = trigger_side;
+      change->actual_trigger_ce_value = CustomValue[x][y];
 
       if (change->can_change && !change_done)
       {
@@ -7261,9 +7479,19 @@ static boolean CheckElementChangeExt(int x, int y,
 
        change_done = TRUE;
       }
-
+#if USE_NEW_DELAYED_ACTION
+      else if (change->has_action)
+      {
+       ExecuteCustomElementAction(x, y, element, p);
+       PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
+      }
+#else
       if (change->has_action)
+      {
        ExecuteCustomElementAction(x, y, element, p);
+       PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
+      }
+#endif
     }
   }
 
@@ -7703,6 +7931,18 @@ void GameActions()
        RemoveField(x, y);
     }
 
+#if USE_NEW_SNAP_DELAY
+    if (Feld[x][y] == EL_ELEMENT_SNAPPING)
+    {
+      MovDelay[x][y]--;
+      if (MovDelay[x][y] <= 0)
+      {
+       RemoveField(x, y);
+       DrawLevelField(x, y);
+      }
+    }
+#endif
+
 #if DEBUG
     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
     {
@@ -7785,11 +8025,16 @@ void GameActions()
       printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
 #endif
 
+#if 1
+      ChangeElement(x, y, page);
+#else
       if (CAN_CHANGE(element))
        ChangeElement(x, y, page);
 
-      if (HAS_ACTION(element) && ChangeDelay[x][y] == 0)
+      if (HAS_ACTION(element))
        ExecuteCustomElementAction(x, y, element, page);
+#endif
+
 #endif
 
       element = Feld[x][y];
@@ -7852,6 +8097,14 @@ void GameActions()
       CheckForDragon(x, y);
     else if (element == EL_EXPLOSION)
       ;        /* drawing of correct explosion animation is handled separately */
+    else if (element == EL_ELEMENT_SNAPPING)
+    {
+#if 1
+      graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
+
+      DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+#endif
+    }
     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
 
@@ -8250,7 +8503,7 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
   player->MovDir = (dx < 0 ? MV_LEFT :
                    dx > 0 ? MV_RIGHT :
                    dy < 0 ? MV_UP :
-                   dy > 0 ? MV_DOWN :  MV_NO_MOVING);
+                   dy > 0 ? MV_DOWN :  MV_NONE);
 
   if (!IN_LEV_FIELD(new_jx, new_jy))
     return MF_NO_ACTION;
@@ -8292,6 +8545,12 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
   player->jy = new_jy;
   StorePlayer[new_jx][new_jy] = player->element_nr;
 
+  if (player->move_delay_value_next != -1)
+  {
+    player->move_delay_value = player->move_delay_value_next;
+    player->move_delay_value_next = -1;
+  }
+
   player->MovPos =
     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
 
@@ -8333,7 +8592,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
   player->move_delay = -1;             /* set to "uninitialized" value */
 
   /* store if player is automatically moved to next field */
-  player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
+  player->is_auto_moving = (player->programmed_action != MV_NONE);
 
   /* remove the last programmed player action */
   player->programmed_action = 0;
@@ -8553,21 +8812,21 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
     return;
 
 #if 0
-    printf("::: player->MovPos: %d -> %d\n",
-          player->MovPos,
-          player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
+  printf("::: player->MovPos: %d -> %d\n",
+        player->MovPos,
+        player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
 #endif
 
 #if USE_NEW_PLAYER_SPEED
-    if (player->MovPos != 0)
-    {
-      player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
-      player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
+  if (player->MovPos != 0)
+  {
+    player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
+    player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
 
-      /* before DrawPlayer() to draw correct player graphic for this case */
-      if (player->MovPos == 0)
-       CheckGravityMovement(player);
-    }
+    /* before DrawPlayer() to draw correct player graphic for this case */
+    if (player->MovPos == 0)
+      CheckGravityMovement(player);
+  }
 #else
   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
@@ -8627,14 +8886,16 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
                                   CE_LEFT_BY_PLAYER,
                                   player->index_bit, leave_side);
 
-      CheckTriggeredElementChangeByPlayer(old_element, CE_PLAYER_LEAVES_X,
+      CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
+                                         CE_PLAYER_LEAVES_X,
                                          player->index_bit, leave_side);
 
       if (IS_CUSTOM_ELEMENT(new_element))
        CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
                                   player->index_bit, enter_side);
 
-      CheckTriggeredElementChangeByPlayer(new_element, CE_PLAYER_ENTERS_X,
+      CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
+                                         CE_PLAYER_ENTERS_X,
                                          player->index_bit, enter_side);
     }
 
@@ -8706,7 +8967,7 @@ void ScrollScreen(struct PlayerInfo *player, int mode)
     redraw_mask |= REDRAW_FIELD;
   }
   else
-    ScreenMovDir = MV_NO_MOVING;
+    ScreenMovDir = MV_NONE;
 }
 
 void TestIfPlayerTouchesCustomElement(int x, int y)
@@ -8762,7 +9023,8 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
 
       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
                                 player->index_bit, border_side);
-      CheckTriggeredElementChangeByPlayer(border_element, CE_PLAYER_TOUCHES_X,
+      CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
+                                         CE_PLAYER_TOUCHES_X,
                                          player->index_bit, border_side);
     }
     else if (IS_PLAYER(xx, yy))
@@ -8777,7 +9039,8 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
 
       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
                                 player->index_bit, center_side);
-      CheckTriggeredElementChangeByPlayer(center_element, CE_PLAYER_TOUCHES_X,
+      CheckTriggeredElementChangeByPlayer(x, y, center_element,
+                                         CE_PLAYER_TOUCHES_X,
                                          player->index_bit, center_side);
       break;
     }
@@ -8980,7 +9243,7 @@ void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
       continue;
 
     test_move_dir =
-      (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
+      (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
 
     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
 
@@ -9054,7 +9317,7 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
       continue;
 
     test_move_dir =
-      (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
+      (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
 
     test_element = Feld[test_x][test_y];
 
@@ -9110,7 +9373,7 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
 
 void TestIfPlayerTouchesBadThing(int x, int y)
 {
-  TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
+  TestIfGoodThingHitsBadThing(x, y, MV_NONE);
 }
 
 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
@@ -9120,7 +9383,7 @@ void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
 
 void TestIfBadThingTouchesPlayer(int x, int y)
 {
-  TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
+  TestIfBadThingHitsGoodThing(x, y, MV_NONE);
 }
 
 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
@@ -9130,12 +9393,12 @@ void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
 
 void TestIfFriendTouchesBadThing(int x, int y)
 {
-  TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
+  TestIfGoodThingHitsBadThing(x, y, MV_NONE);
 }
 
 void TestIfBadThingTouchesFriend(int x, int y)
 {
-  TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
+  TestIfBadThingHitsGoodThing(x, y, MV_NONE);
 }
 
 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
@@ -9241,6 +9504,27 @@ void RemovePlayer(struct PlayerInfo *player)
   ExitY = ZY = jy;
 }
 
+#if USE_NEW_SNAP_DELAY
+static void setFieldForSnapping(int x, int y, int element, int direction)
+{
+  struct ElementInfo *ei = &element_info[element];
+  int direction_bit = MV_DIR_BIT(direction);
+  int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
+  int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
+               IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
+
+  Feld[x][y] = EL_ELEMENT_SNAPPING;
+  MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
+
+  ResetGfxAnimation(x, y);
+
+  GfxElement[x][y] = element;
+  GfxAction[x][y] = action;
+  GfxDir[x][y] = direction;
+  GfxFrame[x][y] = -1;
+}
+#endif
+
 /*
   =============================================================================
   checkDiagonalPushing()
@@ -9287,10 +9571,10 @@ int DigField(struct PlayerInfo *player,
   int jx = oldx, jy = oldy;
   int dx = x - jx, dy = y - jy;
   int nextx = x + dx, nexty = y + dy;
-  int move_direction = (dx == -1 ? MV_LEFT :
+  int move_direction = (dx == -1 ? MV_LEFT  :
                        dx == +1 ? MV_RIGHT :
-                       dy == -1 ? MV_UP :
-                       dy == +1 ? MV_DOWN : MV_NO_MOVING);
+                       dy == -1 ? MV_UP    :
+                       dy == +1 ? MV_DOWN  : MV_NONE);
   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
   int dig_side = MV_DIR_OPPOSITE(move_direction);
   int old_element = Feld[jx][jy];
@@ -9335,18 +9619,24 @@ int DigField(struct PlayerInfo *player,
     return MF_NO_ACTION;       /* field has no opening in this direction */
 
   element = Feld[x][y];
-#if USE_NEW_COLLECT_COUNT
-  collect_count = Count[x][y];
+#if USE_NEW_CUSTOM_VALUE
+
+#if 1
+  collect_count = element_info[element].collect_count_initial;
+#else
+  collect_count = CustomValue[x][y];
+#endif
+
 #else
   collect_count = element_info[element].collect_count_initial;
 #endif
 
 #if 0
   if (element != EL_BLOCKED &&
-      Count[x][y] != element_info[element].collect_count_initial)
+      CustomValue[x][y] != element_info[element].collect_count_initial)
     printf("::: %d: %d != %d\n",
           element,
-          Count[x][y],
+          CustomValue[x][y],
           element_info[element].collect_count_initial);
 #endif
 
@@ -9455,11 +9745,18 @@ int DigField(struct PlayerInfo *player,
 
     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
 
-    CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_DIGS_X,
+    CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
                                        player->index_bit, dig_side);
 
     if (mode == DF_SNAP)
+    {
       TestIfElementTouchesCustomElement(x, y); /* for empty space */
+
+#if USE_NEW_SNAP_DELAY
+      if (level.block_snap_field)
+       setFieldForSnapping(x, y, element, move_direction);
+#endif
+    }
   }
   else if (IS_COLLECTIBLE(element))
   {
@@ -9477,14 +9774,14 @@ int DigField(struct PlayerInfo *player,
     }
     else if (element == EL_EXTRA_TIME && level.time > 0)
     {
-      TimeLeft += 10;
+      TimeLeft += level.extra_time;
       DrawGameValue_Time(TimeLeft);
     }
     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
     {
-      player->shield_normal_time_left += 10;
+      player->shield_normal_time_left += level.shield_normal_time;
       if (element == EL_SHIELD_DEADLY)
-       player->shield_deadly_time_left += 10;
+       player->shield_deadly_time_left += level.shield_deadly_time;
     }
     else if (element == EL_DYNAMITE || element == EL_SP_DISK_RED)
     {
@@ -9545,11 +9842,18 @@ int DigField(struct PlayerInfo *player,
     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
 
     if (is_player)
-      CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_COLLECTS_X,
+      CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
                                          player->index_bit, dig_side);
 
     if (mode == DF_SNAP)
+    {
       TestIfElementTouchesCustomElement(x, y); /* for empty space */
+
+#if USE_NEW_SNAP_DELAY
+      if (level.block_snap_field)
+       setFieldForSnapping(x, y, element, move_direction);
+#endif
+    }
   }
   else if (IS_PUSHABLE(element))
   {
@@ -9678,7 +9982,7 @@ int DigField(struct PlayerInfo *player,
     {
       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
                                 player->index_bit, dig_side);
-      CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PUSHES_X,
+      CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
                                          player->index_bit, dig_side);
     }
   }
@@ -9686,7 +9990,7 @@ int DigField(struct PlayerInfo *player,
   {
     if (PLAYER_SWITCHING(player, x, y))
     {
-      CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PRESSES_X,
+      CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
                                          player->index_bit, dig_side);
 
       return MF_ACTION;
@@ -9736,20 +10040,19 @@ int DigField(struct PlayerInfo *player,
     {
       ActivateTimegateSwitch(x, y);
     }
-    else if (element == EL_BALLOON_SWITCH_LEFT ||
+    else if (element == EL_BALLOON_SWITCH_LEFT  ||
             element == EL_BALLOON_SWITCH_RIGHT ||
-            element == EL_BALLOON_SWITCH_UP ||
-            element == EL_BALLOON_SWITCH_DOWN ||
+            element == EL_BALLOON_SWITCH_UP    ||
+            element == EL_BALLOON_SWITCH_DOWN  ||
+            element == EL_BALLOON_SWITCH_NONE  ||
             element == EL_BALLOON_SWITCH_ANY)
     {
-      if (element == EL_BALLOON_SWITCH_ANY)
-       game.balloon_dir = move_direction;
-      else
-       game.balloon_dir = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT :
-                           element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
-                           element == EL_BALLOON_SWITCH_UP    ? MV_UP :
-                           element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN :
-                           MV_NO_MOVING);
+      game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
+                            element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
+                            element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
+                            element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
+                            element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
+                            move_direction);
     }
     else if (element == EL_LAMP)
     {
@@ -9762,17 +10065,21 @@ int DigField(struct PlayerInfo *player,
     else if (element == EL_TIME_ORB_FULL)
     {
       Feld[x][y] = EL_TIME_ORB_EMPTY;
-      TimeLeft += 10;
-      DrawGameValue_Time(TimeLeft);
+
+      if (level.time > 0 || level.use_time_orb_bug)
+      {
+       TimeLeft += level.time_orb_time;
+       DrawGameValue_Time(TimeLeft);
+      }
 
       ResetGfxAnimation(x, y);
       DrawLevelField(x, y);
     }
 
-    CheckTriggeredElementChangeByPlayer(element, CE_SWITCH_OF_X,
+    CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
                                        player->index_bit, dig_side);
 
-    CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PRESSES_X,
+    CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
                                        player->index_bit, dig_side);
 
     return MF_ACTION;
@@ -9787,13 +10094,13 @@ int DigField(struct PlayerInfo *player,
 
       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
                                 player->index_bit, dig_side);
-      CheckTriggeredElementChangeByPlayer(element, CE_SWITCH_OF_X,
+      CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
                                          player->index_bit, dig_side);
     }
 
     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
                               player->index_bit, dig_side);
-    CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PRESSES_X,
+    CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
                                        player->index_bit, dig_side);
 
     return MF_NO_ACTION;
@@ -9814,10 +10121,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 :
+  int snap_direction = (dx == -1 ? MV_LEFT  :
                        dx == +1 ? MV_RIGHT :
-                       dy == -1 ? MV_UP :
-                       dy == +1 ? MV_DOWN : MV_NO_MOVING);
+                       dy == -1 ? MV_UP    :
+                       dy == +1 ? MV_DOWN  : MV_NONE);
 
   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
     return FALSE;
@@ -9962,7 +10269,8 @@ boolean DropElement(struct PlayerInfo *player)
 
     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
                               player->index_bit, drop_side);
-    CheckTriggeredElementChangeByPlayer(new_element, CE_PLAYER_DROPS_X,
+    CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
+                                       CE_PLAYER_DROPS_X,
                                        player->index_bit, drop_side);
 
     TestIfElementTouchesCustomElement(dropx, dropy);