rnd-20051223-1-src
[rocksndiamonds.git] / src / game.c
index 1d2230b38335817fb7e4dec6138c4a59cad7ffcb..346e1269ce6b77342083efe91436d2e4c4db659a 100644 (file)
 #define USE_NEW_AMOEBA_CODE    FALSE
 
 /* EXPERIMENTAL STUFF */
-#define USE_NEW_STUFF                  (TRUE                           * 1)
-
-#define USE_NEW_SP_SLIPPERY            (TRUE   * USE_NEW_STUFF         * 1)
-#define USE_NEW_COLLECT_COUNT          (TRUE   * USE_NEW_STUFF         * 1)
-#define USE_NEW_PLAYER_ANIM            (TRUE   * USE_NEW_STUFF         * 1)
-#define USE_NEW_ALL_SLIPPERY           (TRUE   * USE_NEW_STUFF         * 1)
-#define USE_NEW_PLAYER_SPEED           (TRUE   * USE_NEW_STUFF         * 1)
+#define USE_NEW_STUFF                  (                         1)
 
+#define USE_NEW_SP_SLIPPERY            (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))
 
        ((e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element :     \
         (e) == EL_TRIGGER_PLAYER  ? (ch)->actual_trigger_player : (e))
 
-#define GET_VALID_PLAYER_ELEMENT(e)                                    \
-       ((e) >= EL_PLAYER_1 && (e) <= EL_PLAYER_4 ? (e) : EL_PLAYER_1)
-
 #define CAN_GROW_INTO(e)                                               \
        ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
 
@@ -245,8 +245,8 @@ static void InitBeltMovement(void);
 static void CloseAllOpenTimegates(void);
 static void CheckGravityMovement(struct PlayerInfo *);
 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
-static void KillHeroUnlessEnemyProtected(int, int);
-static void KillHeroUnlessExplosionProtected(int, int);
+static void KillPlayerUnlessEnemyProtected(int, int);
+static void KillPlayerUnlessExplosionProtected(int, int);
 
 static void TestIfPlayerTouchesCustomElement(int, int);
 static void TestIfElementTouchesCustomElement(int, int);
@@ -257,15 +257,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 +582,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 +657,7 @@ static int getBeltDirFromBeltSwitchElement(int element)
   static int belt_move_dir[3] =
   {
     MV_LEFT,
-    MV_NO_MOVING,
+    MV_NONE,
     MV_RIGHT
   };
 
@@ -890,8 +890,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 +930,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 +1286,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 +1343,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 +1523,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 +1542,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;
@@ -1529,6 +1557,8 @@ void InitGame()
     player->is_bored = FALSE;
     player->is_sleeping = FALSE;
 
+    player->cannot_move = FALSE;
+
     player->frame_counter_bored = -1;
     player->frame_counter_sleeping = -1;
 
@@ -1584,6 +1614,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 +1644,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 +1656,7 @@ void InitGame()
   TimeLeft = level.time;
   TapeTime = 0;
 
-  ScreenMovDir = MV_NO_MOVING;
+  ScreenMovDir = MV_NONE;
   ScreenMovPos = 0;
   ScreenGfxPos = 0;
 
@@ -1638,7 +1670,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 +1678,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 +1693,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 +1718,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;
     }
   }
 
@@ -1725,8 +1757,7 @@ void InitGame()
        element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
 
       /* BD style elements prefer to slip down on the left side */
-      if (i == EL_BD_ROCK || i == EL_BD_DIAMOND ||
-         game.emulation == EMU_BOULDERDASH)
+      if (game.emulation == EMU_BOULDERDASH)
        element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
     }
   }
@@ -1762,7 +1793,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 */
@@ -1898,6 +1929,7 @@ void InitGame()
     int start_x = 0, start_y = 0;
     int found_rating = 0;
     int found_element = EL_UNDEFINED;
+    int player_nr = local_player->index_nr;
 
     for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
     {
@@ -1906,6 +1938,17 @@ void InitGame()
       int xx, yy;
       boolean is_player;
 
+      if (level.use_start_element[player_nr] &&
+         level.start_element[player_nr] == element &&
+         found_rating < 4)
+      {
+       start_x = x;
+       start_y = y;
+
+       found_rating = 4;
+       found_element = element;
+      }
+
       if (!IS_CUSTOM_ELEMENT(element))
        continue;
 
@@ -1913,6 +1956,7 @@ void InitGame()
       {
        for (i = 0; i < element_info[element].num_change_pages; i++)
        {
+         /* check for player created from custom element as single target */
          content = element_info[element].change_page[i].target_element;
          is_player = ELEM_IS_PLAYER(content);
 
@@ -1929,7 +1973,8 @@ void InitGame()
 
       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
       {
-       content = element_info[element].content[xx][yy];
+       /* check for player created from custom element as explosion content */
+       content = element_info[element].content.e[xx][yy];
        is_player = ELEM_IS_PLAYER(content);
 
        if (is_player && (found_rating < 2 || element < found_element))
@@ -1946,7 +1991,10 @@ void InitGame()
 
        for (i = 0; i < element_info[element].num_change_pages; i++)
        {
-         content= element_info[element].change_page[i].target_content[xx][yy];
+         /* check for player created from custom element as extended target */
+         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))
@@ -2141,7 +2189,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;
@@ -2332,7 +2380,7 @@ void GameWon()
     PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
   }
 
-  /* Hero disappears */
+  /* player disappears */
   if (ExitX >= 0 && ExitY >= 0)
     DrawLevelField(ExitX, ExitY);
 
@@ -2528,8 +2576,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];
@@ -2613,8 +2661,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;
@@ -2628,7 +2676,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)
@@ -2826,8 +2874,9 @@ void DrawRelocatePlayer(struct PlayerInfo *player)
 
 void RelocatePlayer(int jx, int jy, int el_player_raw)
 {
-  int el_player = GET_VALID_PLAYER_ELEMENT(el_player_raw);
-  struct PlayerInfo *player = &stored_player[el_player - EL_PLAYER_1];
+  int el_player = GET_PLAYER_ELEMENT(el_player_raw);
+  int player_nr = GET_PLAYER_NR(el_player);
+  struct PlayerInfo *player = &stored_player[player_nr];
   boolean ffwd_delay = (tape.playing && tape.fast_forward);
   boolean no_delay = (tape.warp_forward);
   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
@@ -2885,7 +2934,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;
@@ -2900,14 +2950,14 @@ void RelocatePlayer(int jx, int jy, int el_player_raw)
   if (player == local_player)  /* only visually relocate local player */
     DrawRelocatePlayer(player);
 
-  TestIfHeroTouchesBadThing(jx, jy);
+  TestIfPlayerTouchesBadThing(jx, jy);
   TestIfPlayerTouchesCustomElement(jx, jy);
 
   if (IS_CUSTOM_ELEMENT(element))
     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);
 }
 
@@ -3062,10 +3112,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)
@@ -3083,7 +3133,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;
 
@@ -3151,7 +3201,7 @@ void Explode(int ex, int ey, int phase, int mode)
     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
        !PLAYER_EXPLOSION_PROTECTED(x, y))
     {
-      KillHeroUnlessExplosionProtected(x, y);
+      KillPlayerUnlessExplosionProtected(x, y);
       border_explosion = TRUE;
     }
     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
@@ -3199,12 +3249,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);
@@ -3350,7 +3400,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)
@@ -3412,7 +3462,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;
@@ -3455,9 +3505,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];
@@ -3503,7 +3553,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);
 
@@ -3515,7 +3565,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);
 
@@ -3745,7 +3795,7 @@ void Impact(int x, int y)
   if (impact && element == EL_AMOEBA_DROP)
   {
     if (object_hit && IS_PLAYER(x, y + 1))
-      KillHeroUnlessEnemyProtected(x, y + 1);
+      KillPlayerUnlessEnemyProtected(x, y + 1);
     else if (object_hit && smashed == EL_PENGUIN)
       Bang(x, y + 1);
     else
@@ -3787,7 +3837,7 @@ void Impact(int x, int y)
     {
       if (CAN_SMASH_PLAYER(element))
       {
-       KillHeroUnlessEnemyProtected(x, y + 1);
+       KillPlayerUnlessEnemyProtected(x, y + 1);
        return;
       }
     }
@@ -3879,7 +3929,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);
        }
       }
@@ -4168,7 +4218,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)
@@ -4176,7 +4226,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;
   }
@@ -4245,7 +4295,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)
@@ -4382,6 +4432,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))
@@ -4435,7 +4490,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)
@@ -4481,7 +4536,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;
   }
@@ -4509,7 +4564,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;
 
@@ -4758,7 +4813,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;
@@ -4840,7 +4895,11 @@ void StartMoving(int x, int y)
 #if USE_NEW_ALL_SLIPPERY
       if (can_fall_both)
       {
-       can_fall_left = !(can_fall_right = RND(2));
+       if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
+         can_fall_right = FALSE;       /* slip down on left side */
+       else
+         can_fall_left = !(can_fall_right = RND(2));
+
        can_fall_both = FALSE;
       }
 #else
@@ -4896,7 +4955,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
@@ -4906,7 +4965,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);
@@ -5057,7 +5116,7 @@ void StartMoving(int x, int y)
        IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
        !PLAYER_ENEMY_PROTECTED(newx, newy))
     {
-      TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
+      TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
 
       return;
     }
@@ -5093,7 +5152,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))
       {
@@ -5358,7 +5417,7 @@ void StartMoving(int x, int y)
        DrawLevelElementAnimation(x, y, element);
 
       if (DONT_TOUCH(element))
-       TestIfBadThingTouchesHero(x, y);
+       TestIfBadThingTouchesPlayer(x, y);
 
       return;
     }
@@ -5437,7 +5496,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
   }
@@ -5455,7 +5514,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
   }
@@ -5490,8 +5549,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
   }
 
@@ -5500,8 +5559,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 */
@@ -5580,7 +5639,7 @@ void ContinueMoving(int x, int y)
 
   if (DONT_TOUCH(element))     /* object may be nasty to player or others */
   {
-    TestIfBadThingTouchesHero(newx, newy);
+    TestIfBadThingTouchesPlayer(newx, newy);
     TestIfBadThingTouchesFriend(newx, newy);
 
     if (!IS_CUSTOM_ELEMENT(element))
@@ -5603,7 +5662,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);
   }
 
@@ -6020,10 +6079,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))
@@ -6066,7 +6129,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])
@@ -6077,7 +6141,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);
@@ -6270,7 +6335,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);
     }
   }
@@ -6499,11 +6564,12 @@ static int getSpecialActionElement(int element, int number, int base_element)
 static int getModifiedActionNumber(int value_old, int operator, int operand,
                                   int value_min, int value_max)
 {
-  int value_new = (operator == CA_MODE_ADD      ? value_old + operand :
+  int value_new = (operator == CA_MODE_SET      ? operand :
+                  operator == CA_MODE_ADD      ? value_old + operand :
                   operator == CA_MODE_SUBTRACT ? value_old - operand :
                   operator == CA_MODE_MULTIPLY ? value_old * operand :
                   operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
-                  operator == CA_MODE_SET      ? operand :
+                  operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
                   value_old);
 
   return (value_new < value_min ? value_min :
@@ -6531,53 +6597,66 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
      EL_EMPTY);
 
+  int action_arg_direction =
+    (action_arg >= CA_ARG_DIRECTION_LEFT &&
+     action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
+     action_arg == CA_ARG_DIRECTION_TRIGGER ?
+     change->actual_trigger_side :
+     action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
+     MV_DIR_OPPOSITE(change->actual_trigger_side) :
+     MV_NONE);
+
   int action_arg_number_min =
     (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MIN :
      CA_ARG_MIN);
 
   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_NOT_MOVING &&
+     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 =
@@ -6585,25 +6664,18 @@ 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_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_element >= EL_PLAYER_1 &&
-     action_arg_element <= EL_PLAYER_4 ?
-     (1 << (action_arg_element - EL_PLAYER_1)) :
-     0);
-
-  /* (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 ?
      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
      PLAYER_BITS_ANY);
 
+  int action_arg_player_bits =
+    (action_arg >= CA_ARG_PLAYER_1 &&
+     action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
+     action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
+     PLAYER_BITS_ANY);
+
   /* ---------- execute action  ---------- */
 
   switch(action_type)
@@ -6626,7 +6698,17 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
     {
       for (i = 0; i < MAX_PLAYERS; i++)
        if (action_arg_player_bits & (1 << i))
-         KillHero(&stored_player[i]);
+         KillPlayer(&stored_player[i]);
+
+      break;
+    }
+
+    case CA_MOVE_PLAYER:
+    {
+      /* automatically move to the next field in specified direction */
+      for (i = 0; i < MAX_PLAYERS; i++)
+       if (trigger_player_bits & (1 << i))
+         stored_player[i].programmed_action = action_arg_direction;
 
       break;
     }
@@ -6649,8 +6731,9 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       break;
     }
 
-    case CA_ADD_KEY:
+    case CA_SET_PLAYER_KEYS:
     {
+      int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
       int element = getSpecialActionElement(action_arg_element,
                                            action_arg_number, EL_KEY_1);
 
@@ -6660,30 +6743,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
        {
          if (trigger_player_bits & (1 << i))
          {
-           stored_player[i].key[KEY_NR(element)] = TRUE;
-
-           DrawGameValue_Keys(stored_player[i].key);
-
-           redraw_mask |= REDRAW_DOOR_1;
-         }
-       }
-      }
-
-      break;
-    }
-
-    case CA_DEL_KEY:
-    {
-      int element = getSpecialActionElement(action_arg_element,
-                                           action_arg_number, EL_KEY_1);
-
-      if (IS_KEY(element))
-      {
-       for (i = 0; i < MAX_PLAYERS; i++)
-       {
-         if (trigger_player_bits & (1 << i))
-         {
-           stored_player[i].key[KEY_NR(element)] = FALSE;
+           stored_player[i].key[KEY_NR(element)] = key_state;
 
            DrawGameValue_Keys(stored_player[i].key);
 
@@ -6703,12 +6763,12 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
        {
          int move_stepsize = TILEX / stored_player[i].move_delay_value;
 
-         if (action_mode == CA_MODE_ADD || action_mode == CA_MODE_SUBTRACT)
+         if (action_arg == CA_ARG_SPEED_SLOWER ||
+             action_arg == CA_ARG_SPEED_FASTER)
          {
-           /* translate "+" and "-" to "*" and "/" with powers of two */
-           action_arg_number = 1 << action_arg_number;
-           action_mode = (action_mode == CA_MODE_ADD ? CA_MODE_MULTIPLY :
-                          CA_MODE_DIVIDE);
+           action_arg_number = 2;
+           action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
+                          CA_MODE_MULTIPLY);
          }
 
          move_stepsize =
@@ -6721,19 +6781,70 @@ 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);
-#endif
+         stored_player[i].cannot_move =
+           (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
+       }
+      }
+
+      break;
+    }
+
+    case CA_SET_PLAYER_SHIELD:
+    {
+      for (i = 0; i < MAX_PLAYERS; i++)
+      {
+       if (trigger_player_bits & (1 << i))
+       {
+         if (action_arg == CA_ARG_SHIELD_OFF)
+         {
+           stored_player[i].shield_normal_time_left = 0;
+           stored_player[i].shield_deadly_time_left = 0;
+         }
+         else if (action_arg == CA_ARG_SHIELD_NORMAL)
+         {
+           stored_player[i].shield_normal_time_left = 999999;
+         }
+         else if (action_arg == CA_ARG_SHIELD_DEADLY)
+         {
+           stored_player[i].shield_normal_time_left = 999999;
+           stored_player[i].shield_deadly_time_left = 999999;
+         }
        }
       }
 
       break;
     }
 
-    case CA_SET_GEMS:
+    case CA_SET_PLAYER_ARTWORK:
+    {
+      for (i = 0; i < MAX_PLAYERS; i++)
+      {
+       int element = action_arg_element;
+      }
+
+      break;
+    }
+
+    case CA_SET_LEVEL_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_LEVEL_WIND:
+    {
+      game.wind_direction = action_arg_direction;
+
+      break;
+    }
+
+    case CA_SET_LEVEL_GEMS:
     {
       local_player->gems_still_needed = action_arg_number_new;
 
@@ -6742,7 +6853,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 */
       {
@@ -6752,13 +6863,13 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
 
        if (!TimeLeft && setup.time_limit)
          for (i = 0; i < MAX_PLAYERS; i++)
-           KillHero(&stored_player[i]);
+           KillPlayer(&stored_player[i]);
       }
 
       break;
     }
 
-    case CA_SET_SCORE:
+    case CA_SET_LEVEL_SCORE:
     {
       local_player->score = action_arg_number_new;
 
@@ -6774,73 +6885,31 @@ 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;
     }
 
-    case CA_SET_DYNABOMB_NUMBER:
-    {
-      printf("::: CA_SET_DYNABOMB_NUMBER -- not yet implemented\n");
-
-      break;
-    }
-
-    case CA_SET_DYNABOMB_SIZE:
-    {
-      printf("::: CA_SET_DYNABOMB_SIZE -- not yet implemented\n");
-
-      break;
-    }
-
-    case CA_SET_DYNABOMB_POWER:
-    {
-      printf("::: CA_SET_DYNABOMB_POWER -- not yet implemented\n");
-
-      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;
-    }
-
     default:
       break;
   }
@@ -6850,6 +6919,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]));
 
@@ -6877,6 +6949,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);
@@ -6895,7 +6972,7 @@ static void ChangeElementNowExt(struct ElementChangeInfo *change,
   Changed[x][y] |= ChangeEvent[x][y];  /* ignore same changes in this frame */
 #endif
 
-  TestIfBadThingTouchesHero(x, y);
+  TestIfBadThingTouchesPlayer(x, y);
   TestIfPlayerTouchesCustomElement(x, y);
   TestIfElementTouchesCustomElement(x, y);
 }
@@ -6915,6 +6992,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
@@ -6956,7 +7035,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;
@@ -7028,7 +7107,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);
@@ -7042,7 +7121,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
@@ -7052,14 +7134,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);
@@ -7137,7 +7314,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,
@@ -7173,6 +7353,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)
        {
@@ -7188,9 +7370,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
            }
          }
 
@@ -7248,6 +7440,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)
       {
@@ -7257,9 +7451,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
     }
   }
 
@@ -7699,6 +7903,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)
     {
@@ -7781,11 +7997,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];
@@ -7848,6 +8069,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);
 
@@ -8029,7 +8258,7 @@ void GameActions()
 
        if (!TimeLeft && setup.time_limit)
          for (i = 0; i < MAX_PLAYERS; i++)
-           KillHero(&stored_player[i]);
+           KillPlayer(&stored_player[i]);
       }
       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
        DrawGameValue_Time(TimePlayed);
@@ -8246,11 +8475,19 @@ 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;
 
+  if (player->cannot_move)
+  {
+    DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
+    SnapField(player, 0, 0);
+
+    return MF_NO_ACTION;
+  }
+
   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
     return MF_NO_ACTION;
 
@@ -8265,10 +8502,10 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
       InitMovingField(jx, jy, MV_DOWN);
       Store[jx][jy] = EL_ACID;
       ContinueMoving(jx, jy);
-      BuryHero(player);
+      BuryPlayer(player);
     }
     else
-      TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
+      TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
 
     return MF_MOVING;
   }
@@ -8288,6 +8525,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);
 
@@ -8329,7 +8572,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;
@@ -8483,12 +8726,12 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
 
   if (game.engine_version < VERSION_IDENT(3,0,7,0))
   {
-    TestIfHeroTouchesBadThing(jx, jy);
+    TestIfPlayerTouchesBadThing(jx, jy);
     TestIfPlayerTouchesCustomElement(jx, jy);
   }
 
   if (!player->active)
-    RemoveHero(player);
+    RemovePlayer(player);
 
   return moved;
 }
@@ -8549,21 +8792,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);
@@ -8601,7 +8844,7 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
        Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
     {
       DrawPlayer(player);      /* needed here only to cleanup last field */
-      RemoveHero(player);
+      RemovePlayer(player);
 
       if (local_player->friends_still_needed == 0 ||
          IS_SP_ELEMENT(Feld[jx][jy]))
@@ -8623,20 +8866,22 @@ 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);
     }
 
     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
     {
-      TestIfHeroTouchesBadThing(jx, jy);
+      TestIfPlayerTouchesBadThing(jx, jy);
       TestIfPlayerTouchesCustomElement(jx, jy);
 
       /* needed because pushed element has not yet reached its destination,
@@ -8645,7 +8890,7 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
        TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
 
       if (!player->active)
-       RemoveHero(player);
+       RemovePlayer(player);
     }
 
     if (level.use_step_counter)
@@ -8665,7 +8910,7 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
 
        if (!TimeLeft && setup.time_limit)
          for (i = 0; i < MAX_PLAYERS; i++)
-           KillHero(&stored_player[i]);
+           KillPlayer(&stored_player[i]);
       }
       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
        DrawGameValue_Time(TimePlayed);
@@ -8702,7 +8947,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)
@@ -8758,7 +9003,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))
@@ -8773,7 +9019,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;
     }
@@ -8976,7 +9223,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);
 
@@ -9004,7 +9251,7 @@ void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
          !IS_INDESTRUCTIBLE(bad_element))
        Bang(kill_x, kill_y);
       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
-       KillHero(player);
+       KillPlayer(player);
     }
     else
       Bang(good_x, good_y);
@@ -9050,7 +9297,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];
 
@@ -9097,41 +9344,41 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
          !IS_INDESTRUCTIBLE(bad_element))
        Bang(bad_x, bad_y);
       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
-       KillHero(player);
+       KillPlayer(player);
     }
     else
       Bang(kill_x, kill_y);
   }
 }
 
-void TestIfHeroTouchesBadThing(int x, int y)
+void TestIfPlayerTouchesBadThing(int x, int y)
 {
-  TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
+  TestIfGoodThingHitsBadThing(x, y, MV_NONE);
 }
 
-void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
+void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
 {
   TestIfGoodThingHitsBadThing(x, y, move_dir);
 }
 
-void TestIfBadThingTouchesHero(int x, int y)
+void TestIfBadThingTouchesPlayer(int x, int y)
 {
-  TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
+  TestIfBadThingHitsGoodThing(x, y, MV_NONE);
 }
 
-void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
+void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
 {
   TestIfBadThingHitsGoodThing(x, y, 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)
@@ -9168,7 +9415,7 @@ void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
     Bang(bad_x, bad_y);
 }
 
-void KillHero(struct PlayerInfo *player)
+void KillPlayer(struct PlayerInfo *player)
 {
   int jx = player->jx, jy = player->jy;
 
@@ -9183,22 +9430,22 @@ void KillHero(struct PlayerInfo *player)
   player->shield_deadly_time_left = 0;
 
   Bang(jx, jy);
-  BuryHero(player);
+  BuryPlayer(player);
 }
 
-static void KillHeroUnlessEnemyProtected(int x, int y)
+static void KillPlayerUnlessEnemyProtected(int x, int y)
 {
   if (!PLAYER_ENEMY_PROTECTED(x, y))
-    KillHero(PLAYERINFO(x, y));
+    KillPlayer(PLAYERINFO(x, y));
 }
 
-static void KillHeroUnlessExplosionProtected(int x, int y)
+static void KillPlayerUnlessExplosionProtected(int x, int y)
 {
   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
-    KillHero(PLAYERINFO(x, y));
+    KillPlayer(PLAYERINFO(x, y));
 }
 
-void BuryHero(struct PlayerInfo *player)
+void BuryPlayer(struct PlayerInfo *player)
 {
   int jx = player->jx, jy = player->jy;
 
@@ -9209,10 +9456,10 @@ void BuryHero(struct PlayerInfo *player)
   PlayLevelSound(jx, jy, SND_GAME_LOSING);
 
   player->GameOver = TRUE;
-  RemoveHero(player);
+  RemovePlayer(player);
 }
 
-void RemoveHero(struct PlayerInfo *player)
+void RemovePlayer(struct PlayerInfo *player)
 {
   int jx = player->jx, jy = player->jy;
   int i, found = FALSE;
@@ -9223,6 +9470,9 @@ void RemoveHero(struct PlayerInfo *player)
   if (!ExplodeField[jx][jy])
     StorePlayer[jx][jy] = 0;
 
+  if (player->is_moving)
+    DrawLevelField(player->last_jx, player->last_jy);
+
   for (i = 0; i < MAX_PLAYERS; i++)
     if (stored_player[i].active)
       found = TRUE;
@@ -9234,6 +9484,27 @@ void RemoveHero(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()
@@ -9280,10 +9551,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];
@@ -9328,18 +9599,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
 
@@ -9448,11 +9725,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))
   {
@@ -9470,14 +9754,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)
     {
@@ -9538,11 +9822,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))
   {
@@ -9671,7 +9962,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);
     }
   }
@@ -9679,7 +9970,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;
@@ -9729,20 +10020,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)
     {
@@ -9755,17 +10045,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;
@@ -9780,13 +10074,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;
@@ -9807,10 +10101,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;
@@ -9955,7 +10249,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);