rnd-20051228-2-src
[rocksndiamonds.git] / src / game.c
index 7949da60c770145cb4d447fd1329c6b36e6a98b9..4a2f56697a9bed798aabe349f5153c0dea16a033 100644 (file)
 #define USE_NEW_AMOEBA_CODE    FALSE
 
 /* EXPERIMENTAL STUFF */
-#define USE_NEW_STUFF                  (TRUE                           * 1)
-
-#define USE_NEW_MOVE_STYLE             (TRUE   * USE_NEW_STUFF         * 1)
-#define USE_NEW_MOVE_DELAY             (TRUE   * USE_NEW_STUFF         * 1)
-#define USE_NEW_PUSH_DELAY             (TRUE   * USE_NEW_STUFF         * 1)
-#define USE_NEW_BLOCK_STYLE            (TRUE   * USE_NEW_STUFF         * 1)
-#define USE_NEW_SP_SLIPPERY            (TRUE   * USE_NEW_STUFF         * 1)
-#define USE_NEW_RANDOMIZE              (TRUE   * USE_NEW_STUFF         * 1)
-
-#define USE_CAN_MOVE_NOT_MOVING                (TRUE   * USE_NEW_STUFF         * 1)
-#define USE_PREVIOUS_MOVE_DIR          (TRUE   * USE_NEW_STUFF         * 1)
-
-#define USE_PUSH_BUGFIX                        (TRUE   * USE_NEW_STUFF         * 1)
-#define USE_GRAVITY_BUGFIX_NEW         (TRUE   * USE_NEW_STUFF         * 1)
-#define USE_GRAVITY_BUGFIX_OLD         (TRUE   * USE_NEW_STUFF         * 0)
-
-#define USE_PENGUIN_COLLECT_BUGFIX     (TRUE   * USE_NEW_STUFF         * 1)
-
-#define USE_IMPACT_BUGFIX              (TRUE   * USE_NEW_STUFF         * 1)
-
-#define USE_HITTING_SOMETHING_BUGFIX   (TRUE   * USE_NEW_STUFF         * 1)
-#define USE_HIT_BY_SOMETHING_BUGFIX    (TRUE   * USE_NEW_STUFF         * 1)
-
-#define USE_DROP_BUGFIX                        (TRUE   * USE_NEW_STUFF         * 1)
-
-#define USE_CHANGE_TO_TRIGGERED                (TRUE   * USE_NEW_STUFF         * 1)
-
-#define USE_BACK_WALKABLE_BUGFIX       (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
 #define INITIAL_MOVE_DELAY_ON  0
 
 /* values for player movement speed (which is in fact a delay value) */
+#define MOVE_DELAY_MIN_SPEED   32
 #define MOVE_DELAY_NORMAL_SPEED        8
 #define MOVE_DELAY_HIGH_SPEED  4
+#define MOVE_DELAY_MAX_SPEED   1
 
+#if 0
 #define DOUBLE_MOVE_DELAY(x)   (x = (x <= MOVE_DELAY_HIGH_SPEED ? x * 2 : x))
 #define HALVE_MOVE_DELAY(x)    (x = (x >= MOVE_DELAY_HIGH_SPEED ? x / 2 : x))
+#else
+#define DOUBLE_MOVE_DELAY(x)   (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
+#define HALVE_MOVE_DELAY(x)    (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
+#endif
 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY((p)->move_delay_value))
 #define HALVE_PLAYER_SPEED(p)  (DOUBLE_MOVE_DELAY((p)->move_delay_value))
 
 /* values for other actions */
 #define MOVE_STEPSIZE_NORMAL   (TILEX / MOVE_DELAY_NORMAL_SPEED)
+#define MOVE_STEPSIZE_MIN      (1)
+#define MOVE_STEPSIZE_MAX      (TILEX)
 
 #define GET_DX_FROM_DIR(d)     ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
 #define GET_DY_FROM_DIR(d)     ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 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))
 
 
 /* forward declaration for internal use */
 
+static void SetPlayerWaiting(struct PlayerInfo *, boolean);
 static void AdvanceFrameAndPlayerCounters(int);
 
 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
@@ -257,8 +246,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);
@@ -271,25 +260,21 @@ static void ChangeElement(int, int, int);
 
 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)
+       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)
+       CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
 
-static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
+static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
 #define CheckElementChange(x, y, e, te, ev)                            \
-       CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
+       CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                  \
-       CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s, -1)
+       CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
 #define CheckElementChangeBySide(x, y, e, te, ev, s)                   \
-       CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s, -1)
-#define CheckElementChangeByPage(x, y, e, te, ev, p)                   \
-       CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
+       CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
 
 static void PlayLevelSound(int, int, int);
 static void PlayLevelSoundNearest(int, int, int);
@@ -598,7 +583,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];
@@ -673,7 +658,7 @@ static int getBeltDirFromBeltSwitchElement(int element)
   static int belt_move_dir[3] =
   {
     MV_LEFT,
-    MV_NO_MOVING,
+    MV_NONE,
     MV_RIGHT
   };
 
@@ -696,7 +681,7 @@ static void InitPlayerField(int x, int y, int element, boolean init_game)
       }
       else
       {
-       stored_player[0].use_murphy_graphic = TRUE;
+       stored_player[0].use_murphy = TRUE;
       }
 
       Feld[x][y] = EL_PLAYER_1;
@@ -714,7 +699,6 @@ static void InitPlayerField(int x, int y, int element, boolean init_game)
                                level.sp_block_last_field :
                                level.block_last_field);
 
-#if USE_NEW_BLOCK_STYLE
     /* ---------- initialize player's last field block delay --------------- */
 
     /* always start with reliable default value (no adjustment needed) */
@@ -727,7 +711,6 @@ static void InitPlayerField(int x, int y, int element, boolean init_game)
     /* special case 2: in game engines before 3.1.1, blocking was different */
     if (game.use_block_last_field_bug)
       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
-#endif
 
     if (!options.network || player->connected)
     {
@@ -908,8 +891,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;
@@ -934,6 +930,20 @@ static void InitField(int x, int y, boolean init_game)
       }
       break;
   }
+
+#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
 }
 
 static inline void InitField_WithBug1(int x, int y, boolean init_game)
@@ -1223,7 +1233,6 @@ static void InitGameEngine()
 
   /* ---------- initialize player's initial move delay --------------------- */
 
-#if USE_NEW_MOVE_DELAY
   /* dynamically adjust player properties according to level information */
   game.initial_move_delay_value =
     (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
@@ -1231,16 +1240,6 @@ static void InitGameEngine()
   /* dynamically adjust player properties according to game engine version */
   game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
                             game.initial_move_delay_value : 0);
-#else
-  /* dynamically adjust player properties according to game engine version */
-  game.initial_move_delay =
-    (game.engine_version <= VERSION_IDENT(2,0,1,0) ? INITIAL_MOVE_DELAY_ON :
-     INITIAL_MOVE_DELAY_OFF);
-
-  /* dynamically adjust player properties according to level information */
-  game.initial_move_delay_value =
-    (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
-#endif
 
   /* ---------- initialize player's initial push delay --------------------- */
 
@@ -1288,12 +1287,29 @@ 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);
     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
   }
 
+  /* ---------- initialize internal run-time variables ------------- */
+
+  for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+  {
+    struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
+
+    for (j = 0; j < ei->num_change_pages; j++)
+    {
+      ei->change_page[j].can_change_or_has_action =
+       (ei->change_page[j].can_change |
+        ei->change_page[j].has_action);
+    }
+  }
+
   /* add change events from custom element configuration */
   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
   {
@@ -1301,7 +1317,7 @@ static void InitGameEngine()
 
     for (j = 0; j < ei->num_change_pages; j++)
     {
-      if (!ei->change_page[j].can_change)
+      if (!ei->change_page[j].can_change_or_has_action)
        continue;
 
       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
@@ -1318,20 +1334,6 @@ static void InitGameEngine()
     }
   }
 
-  /* ---------- initialize internal run-time variables ------------- */
-
-  for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
-  {
-    struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
-
-    for (j = 0; j < ei->num_change_pages; j++)
-    {
-      ei->change_page[j].can_change_or_has_action =
-       (ei->change_page[j].can_change |
-        ei->change_page[j].has_action);
-    }
-  }
-
   /* ---------- initialize run-time trigger player and element ------------- */
 
   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
@@ -1342,6 +1344,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;
     }
   }
 
@@ -1359,7 +1363,7 @@ static void InitGameEngine()
 
     for (j = 0; j < ei->num_change_pages; j++)
     {
-      if (!ei->change_page[j].can_change)
+      if (!ei->change_page[j].can_change_or_has_action)
        continue;
 
       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
@@ -1414,16 +1418,11 @@ static void InitGameEngine()
     {
       if (IS_SP_ELEMENT(i))
       {
-#if USE_NEW_MOVE_STYLE
        /* set SP push delay to just enough to push under a falling zonk */
        int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
 
        element_info[i].push_delay_fixed  = delay;
        element_info[i].push_delay_random = 0;
-#else
-       element_info[i].push_delay_fixed  = 6;  /* just enough to escape ... */
-       element_info[i].push_delay_random = 0;  /* ... from falling zonk     */
-#endif
       }
     }
   }
@@ -1443,16 +1442,23 @@ static void InitGameEngine()
     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
   }
 
-  /* ---------- initialize gem count --------------------------------------- */
+  /* ---------- initialize collect score ----------------------------------- */
+
+  /* initialize collect score values for custom elements from initial value */
+  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+    if (IS_CUSTOM_ELEMENT(i))
+      element_info[i].collect_score = element_info[i].collect_score_initial;
+
+  /* ---------- initialize collect count ----------------------------------- */
 
-  /* initialize gem count values for each element */
+  /* initialize collect count values for non-custom elements */
   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
     if (!IS_CUSTOM_ELEMENT(i))
-      element_info[i].collect_count = 0;
+      element_info[i].collect_count_initial = 0;
 
-  /* add gem count values for all elements from pre-defined list */
+  /* add collect count values for all elements from pre-defined list */
   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
-    element_info[collect_count_list[i].element].collect_count =
+    element_info[collect_count_list[i].element].collect_count_initial =
       collect_count_list[i].count;
 
   /* ---------- initialize access direction -------------------------------- */
@@ -1468,6 +1474,28 @@ static void InitGameEngine()
       access_direction_list[i].direction;
 }
 
+int get_num_special_action(int element, int action_first, int action_last)
+{
+  int num_special_action = 0;
+  int i, j;
+
+  for (i = action_first; i <= action_last; i++)
+  {
+    boolean found = FALSE;
+
+    for (j = 0; j < NUM_DIRECTIONS; j++)
+      if (el_act_dir2img(element, i, j) !=
+         el_act_dir2img(element, ACTION_DEFAULT, j))
+       found = TRUE;
+
+    if (found)
+      num_special_action++;
+    else
+      break;
+  }
+
+  return num_special_action;
+}
 
 /*
   =============================================================================
@@ -1482,7 +1510,7 @@ void InitGame()
   boolean emulate_bd = TRUE;   /* unless non-BOULDERDASH elements found */
   boolean emulate_sb = TRUE;   /* unless non-SOKOBAN     elements found */
   boolean emulate_sp = TRUE;   /* unless non-SUPAPLEX    elements found */
-  int i, j, k, x, y;
+  int i, j, x, y;
 
   InitGameEngine();
 
@@ -1518,15 +1546,16 @@ 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;
 
-    player->use_murphy_graphic = FALSE;
+    player->use_murphy = FALSE;
+    player->artwork_element = player->element_nr;
 
     player->block_last_field = FALSE;  /* initialized in InitPlayerField() */
     player->block_delay_adjustment = 0;        /* initialized in InitPlayerField() */
@@ -1537,7 +1566,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;
@@ -1552,6 +1581,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;
 
@@ -1563,61 +1594,31 @@ void InitGame()
     player->special_action_bored = ACTION_DEFAULT;
     player->special_action_sleeping = ACTION_DEFAULT;
 
-    player->num_special_action_bored = 0;
-    player->num_special_action_sleeping = 0;
-
-    /* determine number of special actions for bored and sleeping animation */
-    for (j = ACTION_BORING_1; j <= ACTION_BORING_LAST; j++)
-    {
-      boolean found = FALSE;
-
-      for (k = 0; k < NUM_DIRECTIONS; k++)
-       if (el_act_dir2img(player->element_nr, j, k) !=
-           el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
-         found = TRUE;
-
-      if (found)
-       player->num_special_action_bored++;
-      else
-       break;
-    }
-    for (j = ACTION_SLEEPING_1; j <= ACTION_SLEEPING_LAST; j++)
-    {
-      boolean found = FALSE;
-
-      for (k = 0; k < NUM_DIRECTIONS; k++)
-       if (el_act_dir2img(player->element_nr, j, k) !=
-           el_act_dir2img(player->element_nr, ACTION_DEFAULT, k))
-         found = TRUE;
-
-      if (found)
-       player->num_special_action_sleeping++;
-      else
-       break;
-    }
+    /* set number of special actions for bored and sleeping animation */
+    player->num_special_action_bored =
+      get_num_special_action(player->artwork_element,
+                            ACTION_BORING_1, ACTION_BORING_LAST);
+    player->num_special_action_sleeping =
+      get_num_special_action(player->artwork_element,
+                            ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
 
     player->switch_x = -1;
     player->switch_y = -1;
 
-#if USE_DROP_BUGFIX
     player->drop_x = -1;
     player->drop_y = -1;
-#endif
 
     player->show_envelope = 0;
 
     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;
 
-#if USE_NEW_PUSH_DELAY
     player->push_delay       = -1;     /* initialized when pushing starts */
     player->push_delay_value = game.initial_push_delay_value;
-#else
-    player->push_delay       = 0;
-    player->push_delay_value = game.initial_push_delay_value;
-#endif
 
     player->drop_delay = 0;
 
@@ -1642,7 +1643,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;
@@ -1654,7 +1655,7 @@ void InitGame()
   TimeLeft = level.time;
   TapeTime = 0;
 
-  ScreenMovDir = MV_NO_MOVING;
+  ScreenMovDir = MV_NONE;
   ScreenMovPos = 0;
   ScreenGfxPos = 0;
 
@@ -1668,15 +1669,18 @@ 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;
 
+  game.lenses_time_left = 0;
+  game.magnify_time_left = 0;
+
   game.envelope_active = FALSE;
 
   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 */
   }
 
@@ -1691,6 +1695,9 @@ void InitGame()
       MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
       ChangeDelay[x][y] = 0;
       ChangePage[x][y] = -1;
+#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;
       WasJustMoving[x][y] = 0;
@@ -1713,7 +1720,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;
     }
   }
 
@@ -1738,6 +1745,26 @@ void InitGame()
                    emulate_sb ? EMU_SOKOBAN :
                    emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
 
+#if USE_NEW_ALL_SLIPPERY
+  /* initialize type of slippery elements */
+  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+  {
+    if (!IS_CUSTOM_ELEMENT(i))
+    {
+      /* default: elements slip down either to the left or right randomly */
+      element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
+
+      /* SP style elements prefer to slip down on the left side */
+      if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
+       element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
+
+      /* BD style elements prefer to slip down on the left side */
+      if (game.emulation == EMU_BOULDERDASH)
+       element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
+    }
+  }
+#endif
+
   /* initialize explosion and ignition delay */
   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
   {
@@ -1768,7 +1795,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 */
@@ -1796,10 +1823,10 @@ void InitGame()
          player->element_nr = some_player->element_nr;
 #endif
 
-#if USE_NEW_BLOCK_STYLE
+         player->artwork_element = some_player->artwork_element;
+
          player->block_last_field       = some_player->block_last_field;
          player->block_delay_adjustment = some_player->block_delay_adjustment;
-#endif
 
          StorePlayer[jx][jy] = player->element_nr;
          player->jx = player->last_jx = jx;
@@ -1906,6 +1933,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++)
     {
@@ -1914,6 +1942,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;
 
@@ -1921,6 +1960,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);
 
@@ -1937,7 +1977,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))
@@ -1954,7 +1995,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))
@@ -2149,7 +2193,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;
@@ -2280,12 +2324,17 @@ void GameWon()
     {
       if (!tape.playing && !setup.sound_loops)
        PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
-      if (TimeLeft > 0 && !(TimeLeft % 10))
-       RaiseScore(level.score[SC_TIME_BONUS]);
-      if (TimeLeft > 100 && !(TimeLeft % 10))
+
+      if (TimeLeft > 100 && TimeLeft % 10 == 0)
+      {
        TimeLeft -= 10;
+       RaiseScore(level.score[SC_TIME_BONUS] * 10);
+      }
       else
+      {
        TimeLeft--;
+       RaiseScore(level.score[SC_TIME_BONUS]);
+      }
 
       DrawGameValue_Time(TimeLeft);
 
@@ -2308,12 +2357,17 @@ void GameWon()
     {
       if (!tape.playing && !setup.sound_loops)
        PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
-      if (TimePlayed < 999 && !(TimePlayed % 10))
-       RaiseScore(level.score[SC_TIME_BONUS]);
-      if (TimePlayed < 900 && !(TimePlayed % 10))
+
+      if (TimePlayed < 900 && TimePlayed % 10 == 0)
+      {
        TimePlayed += 10;
+       RaiseScore(level.score[SC_TIME_BONUS] * 10);
+      }
       else
+      {
        TimePlayed++;
+       RaiseScore(level.score[SC_TIME_BONUS]);
+      }
 
       DrawGameValue_Time(TimePlayed);
 
@@ -2340,7 +2394,7 @@ void GameWon()
     PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
   }
 
-  /* Hero disappears */
+  /* player disappears */
   if (ExitX >= 0 && ExitY >= 0)
     DrawLevelField(ExitX, ExitY);
 
@@ -2522,43 +2576,29 @@ void InitMovingField(int x, int y, int direction)
   if (!WasJustMoving[x][y] || direction != MovDir[x][y])
     ResetGfxAnimation(x, y);
 
-#if USE_CAN_MOVE_NOT_MOVING
-
   MovDir[x][y] = direction;
   GfxDir[x][y] = direction;
   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
                     ACTION_FALLING : ACTION_MOVING);
 
+  /* this is needed for CEs with property "can move" / "not moving" */
+
   if (getElementMoveStepsize(x, y) != 0)       /* moving or being moved */
   {
     if (Feld[newx][newy] == EL_EMPTY)
       Feld[newx][newy] = EL_BLOCKED;
 
     MovDir[newx][newy] = MovDir[x][y];
+
+#if USE_NEW_CUSTOM_VALUE
+    CustomValue[newx][newy] = CustomValue[x][y];
+#endif
+
     GfxFrame[newx][newy] = GfxFrame[x][y];
     GfxRandom[newx][newy] = GfxRandom[x][y];
     GfxAction[newx][newy] = GfxAction[x][y];
     GfxDir[newx][newy] = GfxDir[x][y];
   }
-
-#else
-
-  MovDir[newx][newy] = MovDir[x][y] = direction;
-  GfxDir[x][y] = direction;
-
-  if (Feld[newx][newy] == EL_EMPTY)
-    Feld[newx][newy] = EL_BLOCKED;
-
-  if (direction == MV_DOWN && CAN_FALL(element))
-    GfxAction[x][y] = ACTION_FALLING;
-  else
-    GfxAction[x][y] = ACTION_MOVING;
-
-  GfxFrame[newx][newy] = GfxFrame[x][y];
-  GfxRandom[newx][newy] = GfxRandom[x][y];
-  GfxAction[newx][newy] = GfxAction[x][y];
-  GfxDir[newx][newy] = GfxDir[x][y];
-#endif
 }
 
 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
@@ -2635,6 +2675,10 @@ static void RemoveField(int x, int y)
   MovDir[x][y] = 0;
   MovDelay[x][y] = 0;
 
+#if USE_NEW_CUSTOM_VALUE
+  CustomValue[x][y] = 0;
+#endif
+
   AmoebaNr[x][y] = 0;
   ChangeDelay[x][y] = 0;
   ChangePage[x][y] = -1;
@@ -2646,7 +2690,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)
@@ -2844,8 +2888,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);
@@ -2884,11 +2929,7 @@ void RelocatePlayer(int jx, int jy, int el_player_raw)
       ScrollPlayer(player, SCROLL_GO_ON);
       ScrollScreen(NULL, SCROLL_GO_ON);
 
-#if USE_NEW_MOVE_DELAY
       AdvanceFrameAndPlayerCounters(player->index_nr);
-#else
-      FrameCounter++;
-#endif
 
       DrawPlayer(player);
 
@@ -2923,15 +2964,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(jx, jy, element,
-                                     CE_PLAYER_ENTERS_X,
+  CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
                                      player->index_bit, enter_side);
 }
 
@@ -3070,7 +3110,7 @@ void Explode(int ex, int ey, int phase, int mode)
            break;
        }
 
-       if (PLAYERINFO(ex, ey)->use_murphy_graphic)
+       if (PLAYERINFO(ex, ey)->use_murphy)
          Store[x][y] = EL_EMPTY;
       }
       else if (center_element == EL_MOLE)
@@ -3086,10 +3126,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)
@@ -3107,7 +3147,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;
 
@@ -3175,7 +3215,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))
@@ -3223,10 +3263,14 @@ 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_CUSTOM_VALUE
+    CustomValue[x][y] = 0;
+#endif
+
     InitField_WithBug2(x, y, FALSE);
 
     DrawLevelField(x, y);
@@ -3324,7 +3368,7 @@ void Bang(int x, int y)
   {
     struct PlayerInfo *player = PLAYERINFO(x, y);
 
-    element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
+    element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
                            player->element_nr);
   }
 
@@ -3432,7 +3476,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;
@@ -3475,9 +3519,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];
@@ -3523,7 +3567,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);
 
@@ -3535,7 +3579,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);
 
@@ -3607,50 +3651,155 @@ static void RedrawAllLightSwitchesAndInvisibleElements()
 {
   int x, y;
 
-  for (y = 0; y < lev_fieldy; y++)
+  for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
   {
-    for (x = 0; x < lev_fieldx; x++)
+    int element = Feld[x][y];
+
+    if (element == EL_LIGHT_SWITCH &&
+       game.light_time_left > 0)
     {
-      int element = Feld[x][y];
+      Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
+      DrawLevelField(x, y);
+    }
+    else if (element == EL_LIGHT_SWITCH_ACTIVE &&
+            game.light_time_left == 0)
+    {
+      Feld[x][y] = EL_LIGHT_SWITCH;
+      DrawLevelField(x, y);
+    }
+    else if (element == EL_EMC_DRIPPER &&
+            game.light_time_left > 0)
+    {
+      Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
+      DrawLevelField(x, y);
+    }
+    else if (element == EL_EMC_DRIPPER_ACTIVE &&
+            game.light_time_left == 0)
+    {
+      Feld[x][y] = EL_EMC_DRIPPER;
+      DrawLevelField(x, y);
+    }
+    else if (element == EL_INVISIBLE_STEELWALL ||
+            element == EL_INVISIBLE_WALL ||
+            element == EL_INVISIBLE_SAND)
+    {
+      if (game.light_time_left > 0)
+       Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
 
-      if (element == EL_LIGHT_SWITCH &&
-         game.light_time_left > 0)
-      {
-       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
-       DrawLevelField(x, y);
-      }
-      else if (element == EL_LIGHT_SWITCH_ACTIVE &&
-              game.light_time_left == 0)
-      {
-       Feld[x][y] = EL_LIGHT_SWITCH;
-       DrawLevelField(x, y);
-      }
-      else if (element == EL_INVISIBLE_STEELWALL ||
-              element == EL_INVISIBLE_WALL ||
-              element == EL_INVISIBLE_SAND)
-      {
-       if (game.light_time_left > 0)
-         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
+      DrawLevelField(x, y);
 
-       DrawLevelField(x, y);
+      /* uncrumble neighbour fields, if needed */
+      if (element == EL_INVISIBLE_SAND)
+       DrawLevelFieldCrumbledSandNeighbours(x, y);
+    }
+    else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
+            element == EL_INVISIBLE_WALL_ACTIVE ||
+            element == EL_INVISIBLE_SAND_ACTIVE)
+    {
+      if (game.light_time_left == 0)
+       Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
 
-       /* uncrumble neighbour fields, if needed */
-       if (element == EL_INVISIBLE_SAND)
-         DrawLevelFieldCrumbledSandNeighbours(x, y);
-      }
-      else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
-              element == EL_INVISIBLE_WALL_ACTIVE ||
-              element == EL_INVISIBLE_SAND_ACTIVE)
-      {
-       if (game.light_time_left == 0)
-         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
+      DrawLevelField(x, y);
 
-       DrawLevelField(x, y);
+      /* re-crumble neighbour fields, if needed */
+      if (element == EL_INVISIBLE_SAND)
+       DrawLevelFieldCrumbledSandNeighbours(x, y);
+    }
+  }
+}
 
-       /* re-crumble neighbour fields, if needed */
-       if (element == EL_INVISIBLE_SAND)
-         DrawLevelFieldCrumbledSandNeighbours(x, y);
-      }
+static void RedrawAllInvisibleElementsForLenses()
+{
+  int x, y;
+
+  for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
+  {
+    int element = Feld[x][y];
+
+    if (element == EL_EMC_DRIPPER &&
+       game.lenses_time_left > 0)
+    {
+      Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
+      DrawLevelField(x, y);
+    }
+    else if (element == EL_EMC_DRIPPER_ACTIVE &&
+            game.lenses_time_left == 0)
+    {
+      Feld[x][y] = EL_EMC_DRIPPER;
+      DrawLevelField(x, y);
+    }
+    else if (element == EL_INVISIBLE_STEELWALL ||
+            element == EL_INVISIBLE_WALL ||
+            element == EL_INVISIBLE_SAND)
+    {
+      if (game.lenses_time_left > 0)
+       Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
+
+      DrawLevelField(x, y);
+
+      /* uncrumble neighbour fields, if needed */
+      if (element == EL_INVISIBLE_SAND)
+       DrawLevelFieldCrumbledSandNeighbours(x, y);
+    }
+    else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
+            element == EL_INVISIBLE_WALL_ACTIVE ||
+            element == EL_INVISIBLE_SAND_ACTIVE)
+    {
+      if (game.lenses_time_left == 0)
+       Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
+
+      DrawLevelField(x, y);
+
+      /* re-crumble neighbour fields, if needed */
+      if (element == EL_INVISIBLE_SAND)
+       DrawLevelFieldCrumbledSandNeighbours(x, y);
+    }
+  }
+}
+
+static void RedrawAllInvisibleElementsForMagnifier()
+{
+  int x, y;
+
+  for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
+  {
+    int element = Feld[x][y];
+
+    if (element == EL_EMC_FAKE_GRASS &&
+       game.magnify_time_left > 0)
+    {
+      Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
+      DrawLevelField(x, y);
+    }
+    else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
+            game.magnify_time_left == 0)
+    {
+      Feld[x][y] = EL_EMC_FAKE_GRASS;
+      DrawLevelField(x, y);
+    }
+    else if (IS_GATE_GRAY(element) &&
+            game.magnify_time_left > 0)
+    {
+      Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
+                   element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
+                   IS_EM_GATE_GRAY(element) ?
+                   element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
+                   IS_EMC_GATE_GRAY(element) ?
+                   element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
+                   element);
+      DrawLevelField(x, y);
+    }
+    else if (IS_GATE_GRAY_ACTIVE(element) &&
+            game.magnify_time_left == 0)
+    {
+      Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
+                   element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
+                   IS_EM_GATE_GRAY_ACTIVE(element) ?
+                   element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
+                   IS_EMC_GATE_GRAY_ACTIVE(element) ?
+                   element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
+                   element);
+      DrawLevelField(x, y);
     }
   }
 }
@@ -3765,7 +3914,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
@@ -3807,7 +3956,7 @@ void Impact(int x, int y)
     {
       if (CAN_SMASH_PLAYER(element))
       {
-       KillHeroUnlessEnemyProtected(x, y + 1);
+       KillPlayerUnlessEnemyProtected(x, y + 1);
        return;
       }
     }
@@ -3899,8 +4048,8 @@ void Impact(int x, int y)
 
          CheckElementChangeBySide(x, y + 1, smashed, element,
                                   CE_SWITCHED, CH_SIDE_TOP);
-         CheckTriggeredElementChangeBySide(x, y + 1, smashed,
-                                           CE_SWITCH_OF_X, CH_SIDE_TOP);
+         CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
+                                           CH_SIDE_TOP);
        }
       }
       else
@@ -4188,7 +4337,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)
@@ -4196,7 +4345,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;
   }
@@ -4265,7 +4414,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)
@@ -4359,10 +4508,8 @@ inline static void TurnRoundExt(int x, int y)
     boolean can_turn_right =
       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
 
-#if USE_CAN_MOVE_NOT_MOVING
-    if (element_info[element].move_stepsize == 0)      /* not moving */
+    if (element_info[element].move_stepsize == 0)      /* "not moving" */
       return;
-#endif
 
     if (move_pattern == MV_TURNING_LEFT)
       MovDir[x][y] = left_dir;
@@ -4404,6 +4551,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))
@@ -4457,7 +4609,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)
@@ -4474,15 +4626,13 @@ inline static void TurnRoundExt(int x, int y)
       boolean first_horiz = RND(2);
       int new_move_dir = MovDir[x][y];
 
-#if USE_CAN_MOVE_NOT_MOVING
-      if (element_info[element].move_stepsize == 0)    /* not moving */
+      if (element_info[element].move_stepsize == 0)    /* "not moving" */
       {
        first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
        MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
 
        return;
       }
-#endif
 
       MovDir[x][y] =
        new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
@@ -4505,7 +4655,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;
   }
@@ -4533,7 +4683,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 +4908,6 @@ void StartMoving(int x, int y)
     else if ((game.engine_version >= VERSION_IDENT(3,1,0,0) &&
              CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
 
-#if USE_IMPACT_BUGFIX
             (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
              CAN_FALL(element) && WasJustFalling[x][y] &&
              (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
@@ -4766,15 +4915,6 @@ void StartMoving(int x, int y)
             (game.engine_version < VERSION_IDENT(2,2,0,7) &&
              CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
              (Feld[x][y + 1] == EL_BLOCKED)))
-#else
-            (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
-             CAN_SMASH(element) && WasJustFalling[x][y] &&
-             (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
-
-            (game.engine_version < VERSION_IDENT(2,2,0,7) &&
-             CAN_SMASH(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
-             (Feld[x][y + 1] == EL_BLOCKED)))
-#endif
     {
       /* this is needed for a special case not covered by calling "Impact()"
         from "ContinueMoving()": if an element moves to a tile directly below
@@ -4792,7 +4932,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;
@@ -4824,14 +4964,29 @@ void StartMoving(int x, int y)
                                 Feld[x + 1][y + 1] == EL_ACID));
       boolean can_fall_any  = (can_fall_left || can_fall_right);
       boolean can_fall_both = (can_fall_left && can_fall_right);
+      int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
 
-      if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
+#if USE_NEW_ALL_SLIPPERY
+      if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
       {
-       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
-
-       if (slippery_type == SLIPPERY_ONLY_LEFT)
+       if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
          can_fall_right = FALSE;
-       else if (slippery_type == SLIPPERY_ONLY_RIGHT)
+       else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
+         can_fall_left = FALSE;
+       else if (slippery_type == SLIPPERY_ONLY_LEFT)
+         can_fall_right = FALSE;
+       else if (slippery_type == SLIPPERY_ONLY_RIGHT)
+         can_fall_left = FALSE;
+
+       can_fall_any  = (can_fall_left || can_fall_right);
+       can_fall_both = FALSE;
+      }
+#else
+      if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
+      {
+       if (slippery_type == SLIPPERY_ONLY_LEFT)
+         can_fall_right = FALSE;
+       else if (slippery_type == SLIPPERY_ONLY_RIGHT)
          can_fall_left = FALSE;
        else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
          can_fall_right = FALSE;
@@ -4841,7 +4996,10 @@ void StartMoving(int x, int y)
        can_fall_any  = (can_fall_left || can_fall_right);
        can_fall_both = (can_fall_left && can_fall_right);
       }
+#endif
 
+#if USE_NEW_ALL_SLIPPERY
+#else
 #if USE_NEW_SP_SLIPPERY
       /* !!! better use the same properties as for custom elements here !!! */
       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
@@ -4851,7 +5009,19 @@ void StartMoving(int x, int y)
        can_fall_both = FALSE;
       }
 #endif
+#endif
+
+#if USE_NEW_ALL_SLIPPERY
+      if (can_fall_both)
+      {
+       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
       if (can_fall_both)
       {
        if (game.emulation == EMU_BOULDERDASH ||
@@ -4862,6 +5032,7 @@ void StartMoving(int x, int y)
 
        can_fall_both = FALSE;
       }
+#endif
 
       if (can_fall_any)
       {
@@ -4903,7 +5074,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
@@ -4913,7 +5084,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);
@@ -5064,7 +5235,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;
     }
@@ -5100,7 +5271,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))
       {
@@ -5181,14 +5352,11 @@ void StartMoving(int x, int y)
       Store[newx][newy] = EL_EMPTY;
       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
       {
-#if USE_CHANGE_TO_TRIGGERED
        int move_leave_element = element_info[element].move_leave_element;
 
+       /* this makes it possible to leave the removed element again */
        Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
                             new_element : move_leave_element);
-#else
-       Store[newx][newy] = element_info[element].move_leave_element;
-#endif
       }
 
       if (move_pattern & MV_MAZE_RUNNER_STYLE)
@@ -5368,7 +5536,7 @@ void StartMoving(int x, int y)
        DrawLevelElementAnimation(x, y, element);
 
       if (DONT_TOUCH(element))
-       TestIfBadThingTouchesHero(x, y);
+       TestIfBadThingTouchesPlayer(x, y);
 
       return;
     }
@@ -5382,11 +5550,6 @@ void StartMoving(int x, int y)
     ContinueMoving(x, y);
 }
 
-/* (emacs is confused here for some reason; this makes it happy again ;-) ) */
-void dummy()
-{
-}
-
 void ContinueMoving(int x, int y)
 {
   int element = Feld[x][y];
@@ -5451,6 +5614,10 @@ void ContinueMoving(int x, int y)
     if (!game.magic_wall_active)
       Feld[x][y] = EL_MAGIC_WALL_DEAD;
     element = Feld[newx][newy] = Store[x][y];
+
+#if USE_NEW_CUSTOM_VALUE
+    InitField(newx, newy, FALSE);
+#endif
   }
   else if (element == EL_BD_MAGIC_WALL_FILLING)
   {
@@ -5465,6 +5632,10 @@ void ContinueMoving(int x, int y)
     if (!game.magic_wall_active)
       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
     element = Feld[newx][newy] = Store[x][y];
+
+#if USE_NEW_CUSTOM_VALUE
+    InitField(newx, newy, FALSE);
+#endif
   }
   else if (element == EL_AMOEBA_DROPPING)
   {
@@ -5486,6 +5657,7 @@ void ContinueMoving(int x, int y)
   MovPos[x][y] = 0;
   MovDir[x][y] = 0;
   MovDelay[x][y] = 0;
+
   MovDelay[newx][newy] = 0;
 
   if (CAN_CHANGE(element))
@@ -5495,6 +5667,10 @@ void ContinueMoving(int x, int y)
     ChangePage[newx][newy]  = ChangePage[x][y];
     Changed[newx][newy]     = Changed[x][y];
     ChangeEvent[newx][newy] = ChangeEvent[x][y];
+
+#if USE_NEW_CUSTOM_VALUE
+    CustomValue[newx][newy] = CustomValue[x][y];
+#endif
   }
 
   ChangeDelay[x][y] = 0;
@@ -5502,6 +5678,10 @@ void ContinueMoving(int x, int y)
   Changed[x][y] = FALSE;
   ChangeEvent[x][y] = -1;
 
+#if USE_NEW_CUSTOM_VALUE
+  CustomValue[x][y] = 0;
+#endif
+
   /* copy animation control values to new field */
   GfxFrame[newx][newy]  = GfxFrame[x][y];
   GfxRandom[newx][newy] = GfxRandom[x][y];     /* keep same random value */
@@ -5517,18 +5697,15 @@ void ContinueMoving(int x, int y)
   {
     int move_leave_element = ei->move_leave_element;
 
-#if USE_CHANGE_TO_TRIGGERED
+    /* this makes it possible to leave the removed element again */
     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
         ei->move_leave_element == EL_TRIGGER_ELEMENT)
       move_leave_element = stored;
-#endif
 
     Feld[x][y] = move_leave_element;
 
-#if USE_PREVIOUS_MOVE_DIR
     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
       MovDir[x][y] = direction;
-#endif
 
     InitField(x, y, FALSE);
 
@@ -5581,7 +5758,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))
@@ -5590,35 +5767,26 @@ void ContinueMoving(int x, int y)
   else if (element == EL_PENGUIN)
     TestIfFriendTouchesBadThing(newx, newy);
 
-#if USE_NEW_MOVE_STYLE
   /* give the player one last chance (one more frame) to move away */
   if (CAN_FALL(element) && direction == MV_DOWN &&
       (last_line || (!IS_FREE(x, newy + 1) &&
                     (!IS_PLAYER(x, newy + 1) ||
                      game.engine_version < VERSION_IDENT(3,1,1,0)))))
     Impact(x, newy);
-#else
-  if (CAN_FALL(element) && direction == MV_DOWN &&
-      (last_line || !IS_FREE(x, newy + 1)))
-    Impact(x, newy);
-#endif
 
-#if USE_PUSH_BUGFIX
   if (pushed_by_player && !game.use_change_when_pushing_bug)
-#else
-  if (pushed_by_player)
-#endif
-
   {
-    int dig_side = MV_DIR_OPPOSITE(direction);
+    int push_side = MV_DIR_OPPOSITE(direction);
     struct PlayerInfo *player = PLAYERINFO(x, y);
 
     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
-                              player->index_bit, dig_side);
+                              player->index_bit, push_side);
     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
-                                       player->index_bit, dig_side);
+                                       player->index_bit, push_side);
   }
 
+  CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
+
   TestIfElementTouchesCustomElement(x, y);     /* empty or new element */
 
   TestIfElementHitsCustomElement(newx, newy, direction);
@@ -6032,10 +6200,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))
@@ -6078,7 +6250,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])
@@ -6089,7 +6262,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);
@@ -6282,7 +6456,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);
     }
   }
@@ -6508,14 +6682,15 @@ static int getSpecialActionElement(int element, int number, int base_element)
          EL_EMPTY);
 }
 
-static int getModifiedActionNumber(int value_old, int value_min, int value_max,
-                                  int operator, int operand)
+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 :
@@ -6523,7 +6698,7 @@ static int getModifiedActionNumber(int value_old, int value_min, int value_max,
          value_new);
 }
 
-static void ExecuteCustomElementAction(int element, int page)
+static void ExecuteCustomElementAction(int x, int y, int element, int page)
 {
   struct ElementInfo *ei = &element_info[element];
   struct ElementChangeInfo *change = &ei->change_page[page];
@@ -6535,7 +6710,11 @@ static void ExecuteCustomElementAction(int element, int page)
   if (!change->has_action)
     return;
 
-  /* ---------- determine action paramater values ---------- */
+  /* ---------- determine action paramater values -------------------------- */
+
+  int level_time_value =
+    (level.time > 0 ? TimeLeft :
+     TimePlayed);
 
   int action_arg_element =
     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
@@ -6543,35 +6722,89 @@ static void ExecuteCustomElementAction(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_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_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_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 :
+#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 =
     (action_arg <= CA_ARG_MAX ? action_arg :
-     action_arg == CA_ARG_NUMBER_MIN ? CA_ARG_MIN :
-     action_arg == CA_ARG_NUMBER_MAX ? CA_ARG_MAX :
+     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_CE_SCORE ? ei->collect_score :
-     action_arg == CA_ARG_NUMBER_CE_COUNT ? ei->collect_count :
+#if USE_NEW_CUSTOM_VALUE
+     action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
+#else
+     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_NUMBER_LEVEL_TIME ? level_time_value :
+     action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
+     action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
+     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);
 
-  /* (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)) :
+  int action_arg_number_old =
+    (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_VALUE ? CustomValue[x][y] :
      0);
 
-  /* (for implicit player choice, set invalid value to "all players") */
+  int action_arg_number_new =
+    getModifiedActionNumber(action_arg_number_old,
+                           action_mode, action_arg_number,
+                           action_arg_number_min, action_arg_number_max);
+
   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);
 
-  /* ---------- execute action  ---------- */
+  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)
   {
@@ -6580,67 +6813,109 @@ static void ExecuteCustomElementAction(int element, int page)
       return;
     }
 
-    case CA_EXIT_PLAYER:
+    /* ---------- level actions  ------------------------------------------- */
+
+    case CA_RESTART_LEVEL:
     {
-      for (i = 0; i < MAX_PLAYERS; i++)
-       if (action_arg_player_bits & (1 << i))
-         stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
+      game.restart_level = TRUE;
 
       break;
     }
 
-    case CA_KILL_PLAYER:
+    case CA_SHOW_ENVELOPE:
     {
-      for (i = 0; i < MAX_PLAYERS; i++)
-       if (action_arg_player_bits & (1 << i))
-         KillHero(&stored_player[i]);
+      int element = getSpecialActionElement(action_arg_element,
+                                           action_arg_number, EL_ENVELOPE_1);
+
+      if (IS_ENVELOPE(element))
+       local_player->show_envelope = element;
 
       break;
     }
 
-    case CA_RESTART_LEVEL:
+    case CA_SET_LEVEL_TIME:
     {
-      game.restart_level = TRUE;
+      if (level.time > 0)      /* only modify limited time value */
+      {
+       TimeLeft = action_arg_number_new;
+
+       DrawGameValue_Time(TimeLeft);
+
+       if (!TimeLeft && setup.time_limit)
+         for (i = 0; i < MAX_PLAYERS; i++)
+           KillPlayer(&stored_player[i]);
+      }
 
       break;
     }
 
-    case CA_SHOW_ENVELOPE:
+    case CA_SET_LEVEL_SCORE:
     {
-      int element = getSpecialActionElement(action_arg_element,
-                                           action_arg_number, EL_ENVELOPE_1);
+      local_player->score = action_arg_number_new;
 
-      if (IS_ENVELOPE(element))
-       local_player->show_envelope = element;
+      DrawGameValue_Score(local_player->score);
 
       break;
     }
 
-    case CA_ADD_KEY:
+    case CA_SET_LEVEL_GEMS:
     {
-      int element = getSpecialActionElement(action_arg_element,
-                                           action_arg_number, EL_KEY_1);
+      local_player->gems_still_needed = action_arg_number_new;
 
-      if (IS_KEY(element))
-      {
-       for (i = 0; i < MAX_PLAYERS; i++)
-       {
-         if (trigger_player_bits & (1 << i))
-         {
-           stored_player[i].key[KEY_NR(element)] = TRUE;
+      DrawGameValue_Emeralds(local_player->gems_still_needed);
 
-           DrawGameValue_Keys(stored_player[i].key);
+      break;
+    }
 
-           redraw_mask |= REDRAW_DOOR_1;
-         }
-       }
-      }
+    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_DEL_KEY:
+    /* ---------- player actions  ------------------------------------------ */
+
+    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;
+    }
+
+    case CA_EXIT_PLAYER:
+    {
+      for (i = 0; i < MAX_PLAYERS; i++)
+       if (action_arg_player_bits & (1 << i))
+         stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
+
+      break;
+    }
+
+    case CA_KILL_PLAYER:
+    {
+      for (i = 0; i < MAX_PLAYERS; i++)
+       if (action_arg_player_bits & (1 << i))
+         KillPlayer(&stored_player[i]);
+
+      break;
+    }
+
+    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);
 
@@ -6650,7 +6925,7 @@ static void ExecuteCustomElementAction(int element, int page)
        {
          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);
 
@@ -6668,131 +6943,121 @@ static void ExecuteCustomElementAction(int element, int page)
       {
        if (trigger_player_bits & (1 << i))
        {
-         if (action_arg == CA_ARG_NUMBER_RESET)
-           stored_player[i].move_delay_value = game.initial_move_delay_value;
-         else if (action_arg == CA_ARG_NUMBER_NORMAL)
-           stored_player[i].move_delay_value = MOVE_DELAY_NORMAL_SPEED;
-         else if (action_arg == CA_ARG_NUMBER_MIN)
-           stored_player[i].move_delay_value = 16;
-         else if (action_arg == CA_ARG_NUMBER_MAX)
-           stored_player[i].move_delay_value = MOVE_DELAY_HIGH_SPEED;
-         else
-         {
-#if 0
-           if (action_mode == CA_MODE_ADD)
-           {
-             action_mode = CA_MODE_DIVIDE;
-             action_arg_number = (1 << action_arg_number);
-           }
-           else if (action_mode == CA_MODE_SUBTRACT)
-           {
-             action_mode = CA_MODE_MULTIPLY;
-             action_arg_number = (1 << action_arg_number);
-           }
-
-           int mode = (action_mode == CA_MODE_MULTIPLY ? CA_MODE_DIVIDE :
-                       action_mode == CA_MODE_DIVIDE   ? CA_MODE_MULTIPLY :
-                       action_mode);
+         int move_stepsize = TILEX / stored_player[i].move_delay_value;
 
-           stored_player[i].move_delay_value =
-             getModifiedActionNumber(stored_player[i].move_delay_value,
-                                     1, 16,
-                                     action_mode, action_arg_number);
-#endif
+         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);
          }
-       }
-      }
 
-      break;
-    }
+         move_stepsize =
+           getModifiedActionNumber(move_stepsize,
+                                   action_mode,
+                                   action_arg_number,
+                                   action_arg_number_min,
+                                   action_arg_number_max);
 
-    case CA_SET_GEMS:
-    {
-      local_player->gems_still_needed =
-       getModifiedActionNumber(local_player->gems_still_needed, 0, 999,
-                               action_mode, action_arg_number);
+         /* make sure that value is power of 2 */
+         move_stepsize = (1 << log_2(move_stepsize));
 
-      DrawGameValue_Emeralds(local_player->gems_still_needed);
-
-      break;
-    }
-
-    case CA_SET_TIME:
-    {
-      if (level.time > 0)      /* only modify limited time value */
-      {
-       TimeLeft = getModifiedActionNumber(TimeLeft, 0, 9999,
-                                          action_mode, action_arg_number);
+         /* do no immediately change -- the player might just be moving */
+         stored_player[i].move_delay_value_next = TILEX / move_stepsize;
 
-       DrawGameValue_Time(TimeLeft);
+         stored_player[i].cannot_move =
+           (action_arg == CA_ARG_SPEED_NOT_MOVING ? TRUE : FALSE);
+       }
       }
 
       break;
     }
 
-    case CA_SET_SCORE:
+    case CA_SET_PLAYER_SHIELD:
     {
-      local_player->score =
-       getModifiedActionNumber(local_player->score, 0, 99999,
-                               action_mode, action_arg_number);
-
-      DrawGameValue_Score(local_player->score);
+      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_CE_SCORE:
+    case CA_SET_PLAYER_ARTWORK:
     {
-      printf("::: CA_SET_CE_SCORE -- not yet implemented\n");
+      for (i = 0; i < MAX_PLAYERS; i++)
+      {
+       if (trigger_player_bits & (1 << i))
+       {
+         int artwork_element = action_arg_element;
 
-      break;
-    }
+         if (action_arg == CA_ARG_ELEMENT_RESET)
+           artwork_element = stored_player[i].element_nr;
 
-    case CA_SET_CE_COUNT:
-    {
-      printf("::: CA_SET_CE_COUNT -- not yet implemented\n");
+         stored_player[i].artwork_element = artwork_element;
 
-      break;
-    }
+         SetPlayerWaiting(&stored_player[i], FALSE);
 
-    case CA_SET_DYNABOMB_NUMBER:
-    {
-      printf("::: CA_SET_DYNABOMB_NUMBER -- not yet implemented\n");
+         /* set number of special actions for bored and sleeping animation */
+         stored_player[i].num_special_action_bored =
+           get_num_special_action(artwork_element,
+                                  ACTION_BORING_1, ACTION_BORING_LAST);
+         stored_player[i].num_special_action_sleeping =
+           get_num_special_action(artwork_element,
+                                  ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
+       }
+      }
 
       break;
     }
 
-    case CA_SET_DYNABOMB_SIZE:
-    {
-      printf("::: CA_SET_DYNABOMB_SIZE -- not yet implemented\n");
-
-      break;
-    }
+    /* ---------- CE actions  ---------------------------------------------- */
 
-    case CA_SET_DYNABOMB_POWER:
+    case CA_SET_CE_SCORE:
     {
-      printf("::: CA_SET_DYNABOMB_POWER -- not yet implemented\n");
+      ei->collect_score = action_arg_number_new;
 
       break;
     }
 
-    case CA_TOGGLE_PLAYER_GRAVITY:
+    case CA_SET_CE_VALUE:
     {
-      game.gravity = !game.gravity;
+#if USE_NEW_CUSTOM_VALUE
+      int last_custom_value = CustomValue[x][y];
 
-      break;
-    }
+      CustomValue[x][y] = action_arg_number_new;
 
-    case CA_ENABLE_PLAYER_GRAVITY:
-    {
-      game.gravity = TRUE;
+#if 0
+      printf("::: Count == %d\n", CustomValue[x][y]);
+#endif
 
-      break;
-    }
+      if (CustomValue[x][y] == 0 && last_custom_value > 0)
+      {
+#if 0
+       printf("::: CE_VALUE_GETS_ZERO\n");
+#endif
 
-    case CA_DISABLE_PLAYER_GRAVITY:
-    {
-      game.gravity = FALSE;
+       CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
+       CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
+      }
+#endif
 
       break;
     }
@@ -6806,6 +7071,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]));
 
@@ -6833,6 +7101,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);
@@ -6851,7 +7124,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);
 }
@@ -6871,6 +7144,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
@@ -6912,7 +7187,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;
@@ -6984,7 +7259,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);
@@ -6998,7 +7273,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
@@ -7008,6 +7286,7 @@ 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 */
@@ -7016,6 +7295,100 @@ static boolean ChangeElementNow(int x, int y, int element, int 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);
@@ -7093,15 +7466,18 @@ static void ChangeElement(int x, int y, int page)
   }
 }
 
-static boolean CheckTriggeredElementChangeExt(int lx, int ly,
+#endif
+
+static boolean CheckTriggeredElementChangeExt(int x, int y,
                                              int trigger_element,
                                              int trigger_event,
                                              int trigger_player,
                                              int trigger_side,
                                              int trigger_page)
 {
-  int i, j, x, y;
+  boolean change_done_any = FALSE;
   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
+  int i;
 
   if (!(trigger_events[trigger_element][trigger_event]))
     return FALSE;
@@ -7109,15 +7485,16 @@ static boolean CheckTriggeredElementChangeExt(int lx, int ly,
   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
   {
     int element = EL_CUSTOM_START + i;
-    boolean change_found = FALSE;
+    boolean change_done = FALSE;
+    int p;
 
     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
        !HAS_ANY_CHANGE_EVENT(element, trigger_event))
       continue;
 
-    for (j = 0; j < element_info[element].num_change_pages; j++)
+    for (p = 0; p < element_info[element].num_change_pages; p++)
     {
-      struct ElementChangeInfo *change = &element_info[element].change_page[j];
+      struct ElementChangeInfo *change = &element_info[element].change_page[p];
 
       if (change->can_change_or_has_action &&
          change->has_event[trigger_event] &&
@@ -7128,29 +7505,50 @@ static boolean CheckTriggeredElementChangeExt(int lx, int ly,
       {
        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_found)
+       if ((change->can_change && !change_done) || change->has_action)
        {
-         change_found = TRUE;
+         int x, y;
 
          for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
          {
            if (Feld[x][y] == element)
            {
-             ChangeDelay[x][y] = 1;
-             ChangeEvent[x][y] = trigger_event;
-             ChangeElement(x, y, j);
+             if (change->can_change && !change_done)
+             {
+               ChangeDelay[x][y] = 1;
+               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
            }
          }
-       }
 
-       if (change->has_action)
-         ExecuteCustomElementAction(element, j);
+         if (change->can_change)
+         {
+           change_done = TRUE;
+           change_done_any = TRUE;
+         }
+       }
       }
     }
   }
 
-  return TRUE;
+  return change_done_any;
 }
 
 static boolean CheckElementChangeExt(int x, int y,
@@ -7158,10 +7556,13 @@ static boolean CheckElementChangeExt(int x, int y,
                                     int trigger_element,
                                     int trigger_event,
                                     int trigger_player,
-                                    int trigger_side,
-                                    int trigger_page)
+                                    int trigger_side)
 {
-  if (!CAN_CHANGE(element) || !HAS_ANY_CHANGE_EVENT(element, trigger_event))
+  boolean change_done = FALSE;
+  int p;
+
+  if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
+      !HAS_ANY_CHANGE_EVENT(element, trigger_event))
     return FALSE;
 
   if (Feld[x][y] == EL_BLOCKED)
@@ -7173,77 +7574,75 @@ static boolean CheckElementChangeExt(int x, int y,
   if (Feld[x][y] != element)   /* check if element has already changed */
     return FALSE;
 
-  if (trigger_page < 0)
+  for (p = 0; p < element_info[element].num_change_pages; p++)
   {
-    boolean change_element = FALSE;
-    int i;
+    struct ElementChangeInfo *change = &element_info[element].change_page[p];
 
-    for (i = 0; i < element_info[element].num_change_pages; i++)
-    {
-      struct ElementChangeInfo *change = &element_info[element].change_page[i];
+    boolean check_trigger_element =
+      (trigger_event == CE_TOUCHING_X ||
+       trigger_event == CE_HITTING_X ||
+       trigger_event == CE_HIT_BY_X);
 
-      boolean check_trigger_element =
-       (trigger_event == CE_TOUCHING_X ||
-        trigger_event == CE_HITTING_X ||
-        trigger_event == CE_HIT_BY_X);
+    if (change->can_change_or_has_action &&
+       change->has_event[trigger_event] &&
+       change->trigger_side & trigger_side &&
+       change->trigger_player & trigger_player &&
+       (!check_trigger_element ||
+        IS_EQUAL_OR_IN_GROUP(trigger_element, change->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->has_event[trigger_event] &&
-         change->trigger_side & trigger_side &&
-         change->trigger_player & trigger_player &&
-         (!check_trigger_element ||
-          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
+      if (change->can_change && !change_done)
       {
-       change_element = TRUE;
-       trigger_page = i;
-
-       change->actual_trigger_element = trigger_element;
-       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
+       ChangeDelay[x][y] = 1;
+       ChangeEvent[x][y] = trigger_event;
+       ChangeElement(x, y, p);
 
-       break;
+       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
     }
-
-    if (!change_element)
-      return FALSE;
-  }
-  else
-  {
-    struct ElementInfo *ei = &element_info[element];
-    struct ElementChangeInfo *change = &ei->change_page[trigger_page];
-
-    change->actual_trigger_element = trigger_element;
-    change->actual_trigger_player = EL_PLAYER_1;       /* unused */
   }
 
-  ChangeDelay[x][y] = 1;
-  ChangeEvent[x][y] = trigger_event;
-  ChangeElement(x, y, trigger_page);
-
-  return TRUE;
+  return change_done;
 }
 
 static void PlayPlayerSound(struct PlayerInfo *player)
 {
   int jx = player->jx, jy = player->jy;
-  int element = player->element_nr;
+  int sound_element = player->artwork_element;
   int last_action = player->last_action_waiting;
   int action = player->action_waiting;
 
   if (player->is_waiting)
   {
     if (action != last_action)
-      PlayLevelSoundElementAction(jx, jy, element, action);
+      PlayLevelSoundElementAction(jx, jy, sound_element, action);
     else
-      PlayLevelSoundElementActionIfLoop(jx, jy, element, action);
+      PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
   }
   else
   {
     if (action != last_action)
-      StopSound(element_info[element].sound[last_action]);
+      StopSound(element_info[sound_element].sound[last_action]);
 
     if (last_action == ACTION_SLEEPING)
-      PlayLevelSoundElementAction(jx, jy, element, ACTION_AWAKENING);
+      PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
   }
 }
 
@@ -7310,7 +7709,7 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
             last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
             last_special_action + 1 : ACTION_SLEEPING);
          int special_graphic =
-           el_act_dir2img(player->element_nr, special_action, move_dir);
+           el_act_dir2img(player->artwork_element, special_action, move_dir);
 
          player->anim_delay_counter =
            graphic_info[special_graphic].anim_delay_fixed +
@@ -7342,7 +7741,7 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
          int special_action =
            ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
          int special_graphic =
-           el_act_dir2img(player->element_nr, special_action, move_dir);
+           el_act_dir2img(player->artwork_element, special_action, move_dir);
 
          player->anim_delay_counter =
            graphic_info[special_graphic].anim_delay_fixed +
@@ -7457,27 +7856,36 @@ void AdvanceFrameAndPlayerCounters(int player_nr)
   for (i = 0; i < MAX_PLAYERS; i++)
   {
     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
-    int move_frames =
-      MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
+    int move_delay_value = stored_player[i].move_delay_value;
+    int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
 
     if (!advance_player_counters)      /* not all players may be affected */
       continue;
 
+#if USE_NEW_PLAYER_ANIM
+    if (move_frames == 0)      /* less than one move per game frame */
+    {
+      int stepsize = TILEX / move_delay_value;
+      int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
+      int count = (stored_player[i].is_moving ?
+                  ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
+
+      if (count % delay == 0)
+       move_frames = 1;
+    }
+#endif
+
     stored_player[i].Frame += move_frames;
 
     if (stored_player[i].MovPos != 0)
       stored_player[i].StepFrame += move_frames;
 
-#if USE_NEW_MOVE_DELAY
     if (stored_player[i].move_delay > 0)
       stored_player[i].move_delay--;
-#endif
 
-#if USE_NEW_PUSH_DELAY
     /* due to bugs in previous versions, counter must count up, not down */
     if (stored_player[i].push_delay != -1)
       stored_player[i].push_delay++;
-#endif
 
     if (stored_player[i].drop_delay > 0)
       stored_player[i].drop_delay--;
@@ -7639,7 +8047,6 @@ void GameActions()
     Changed[x][y] = FALSE;
     ChangeEvent[x][y] = -1;
 
-#if USE_NEW_BLOCK_STYLE
     /* this must be handled before main playfield loop */
     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
     {
@@ -7647,6 +8054,19 @@ void GameActions()
       if (MovDelay[x][y] <= 0)
        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);
+
+       TestIfElementTouchesCustomElement(x, y);        /* for empty space */
+      }
+    }
 #endif
 
 #if DEBUG
@@ -7722,11 +8142,25 @@ void GameActions()
     if (IS_CHANGING(x, y) &&
        (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
     {
+      int page = element_info[element].event_page_nr[CE_DELAY];
 #if 0
-      ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
-                   element_info[element].event_page_nr[CE_DELAY]);
+      ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] : page);
+#else
+
+#if 0
+      printf("::: ChangeDelay == %d\n", ChangeDelay[x][y]);
+#endif
+
+#if 1
+      ChangeElement(x, y, page);
 #else
-      ChangeElement(x, y, element_info[element].event_page_nr[CE_DELAY]);
+      if (CAN_CHANGE(element))
+       ChangeElement(x, y, page);
+
+      if (HAS_ACTION(element))
+       ExecuteCustomElementAction(x, y, element, page);
+#endif
+
 #endif
 
       element = Feld[x][y];
@@ -7789,6 +8223,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);
 
@@ -7924,6 +8366,22 @@ void GameActions()
       CloseAllOpenTimegates();
   }
 
+  if (game.lenses_time_left > 0)
+  {
+    game.lenses_time_left--;
+
+    if (game.lenses_time_left == 0)
+      RedrawAllInvisibleElementsForLenses();
+  }
+
+  if (game.magnify_time_left > 0)
+  {
+    game.magnify_time_left--;
+
+    if (game.magnify_time_left == 0)
+      RedrawAllInvisibleElementsForMagnifier();
+  }
+
   for (i = 0; i < MAX_PLAYERS; i++)
   {
     struct PlayerInfo *player = &stored_player[i];
@@ -7970,7 +8428,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);
@@ -8002,31 +8460,7 @@ void GameActions()
     redraw_mask |= REDRAW_FPS;
   }
 
-#if USE_NEW_MOVE_DELAY
   AdvanceFrameAndPlayerCounters(-1);   /* advance counters for all players */
-#else
-  FrameCounter++;
-  TimeFrames++;
-
-  for (i = 0; i < MAX_PLAYERS; i++)
-  {
-    int move_frames =
-      MOVE_DELAY_NORMAL_SPEED /  stored_player[i].move_delay_value;
-
-    stored_player[i].Frame += move_frames;
-
-    if (stored_player[i].MovPos != 0)
-      stored_player[i].StepFrame += move_frames;
-
-#if USE_NEW_MOVE_DELAY
-    if (stored_player[i].move_delay > 0)
-      stored_player[i].move_delay--;
-#endif
-
-    if (stored_player[i].drop_delay > 0)
-      stored_player[i].drop_delay--;
-  }
-#endif
 
   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
   {
@@ -8035,11 +8469,9 @@ void GameActions()
     local_player->show_envelope = 0;
   }
 
-#if USE_NEW_RANDOMIZE
   /* use random number generator in every frame to make it less predictable */
   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
     RND(1);
-#endif
 }
 
 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
@@ -8117,11 +8549,6 @@ static boolean canFallDown(struct PlayerInfo *player)
 
   return (IN_LEV_FIELD(jx, jy + 1) &&
          (IS_FREE(jx, jy + 1) ||
-#if USE_NEW_BLOCK_STYLE
-#if USE_GRAVITY_BUGFIX_OLD
-          Feld[jx][jy + 1] == EL_PLAYER_IS_LEAVING ||
-#endif
-#endif
           (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
          IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
          !IS_WALKABLE_INSIDE(Feld[jx][jy]));
@@ -8218,11 +8645,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;
 
@@ -8237,10 +8672,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;
   }
@@ -8260,6 +8695,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);
 
@@ -8295,21 +8736,13 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
     return FALSE;
   }
 
-#if USE_NEW_MOVE_DELAY
   if (player->move_delay > 0)
-#else
-  if (!FrameReached(&player->move_delay, player->move_delay_value))
-#endif
-  {
     return FALSE;
-  }
 
-#if USE_NEW_MOVE_DELAY
   player->move_delay = -1;             /* set to "uninitialized" value */
-#endif
 
   /* 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;
@@ -8334,11 +8767,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
       ScrollPlayer(player, SCROLL_GO_ON);
       ScrollScreen(NULL, SCROLL_GO_ON);
 
-#if USE_NEW_MOVE_DELAY
       AdvanceFrameAndPlayerCounters(player->index_nr);
-#else
-      FrameCounter++;
-#endif
 
       DrawAllPlayers();
       BackToFront();
@@ -8451,35 +8880,28 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
   {
     CheckGravityMovementWhenNotMoving(player);
 
-    /*
-    player->last_move_dir = MV_NO_MOVING;
-    */
     player->is_moving = FALSE;
 
-#if USE_NEW_MOVE_STYLE
-    /* player is ALLOWED to move, but CANNOT move (something blocks his way) */
-    /* ensure that the player is also allowed to move in the next frame */
-    /* (currently, the player is forced to wait eight frames before he can try
-       again!!!) */
+    /* at this point, the player is allowed to move, but cannot move right now
+       (e.g. because of something blocking the way) -- ensure that the player
+       is also allowed to move in the next frame (in old versions before 3.1.1,
+       the player was forced to wait again for eight frames before next try) */
 
     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
       player->move_delay = 0;  /* allow direct movement in the next frame */
-#endif
   }
 
-#if USE_NEW_MOVE_DELAY
   if (player->move_delay == -1)                /* not yet initialized by DigField() */
     player->move_delay = player->move_delay_value;
-#endif
 
   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;
 }
@@ -8490,16 +8912,22 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
   int last_jx = player->last_jx, last_jy = player->last_jy;
   int move_stepsize = TILEX / player->move_delay_value;
 
-  if (!player->active || !player->MovPos)
+#if USE_NEW_PLAYER_SPEED
+  if (!player->active)
+    return;
+
+  if (player->MovPos == 0 && mode == SCROLL_GO_ON)     /* player not moving */
+    return;
+#else
+  if (!player->active || player->MovPos == 0)
     return;
+#endif
 
   if (mode == SCROLL_INIT)
   {
     player->actual_frame_counter = FrameCounter;
     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
 
-#if USE_NEW_BLOCK_STYLE
-
     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
        Feld[last_jx][last_jy] == EL_EMPTY)
     {
@@ -8511,11 +8939,9 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
       {
        last_field_block_delay += player->move_delay_value;
 
-#if USE_GRAVITY_BUGFIX_NEW
        /* when blocking enabled, prevent moving up despite gravity */
        if (game.gravity && player->MovDir == MV_UP)
          block_delay_adjustment = -1;
-#endif
       }
 
       /* add block delay adjustment (also possible when not blocking) */
@@ -8524,40 +8950,48 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
     }
-#else
-#if USE_NEW_MOVE_STYLE
-    if ((game.engine_version < VERSION_IDENT(3,1,1,0) ||
-        player->block_last_field) &&
-       Feld[last_jx][last_jy] == EL_EMPTY)
-      Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
-#else
-    if (Feld[last_jx][last_jy] == EL_EMPTY)
-      Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
-#endif
-#endif
 
+#if USE_NEW_PLAYER_SPEED
+    if (player->MovPos != 0)   /* player has not yet reached destination */
+      return;
+#else
     return;
+#endif
   }
   else if (!FrameReached(&player->actual_frame_counter, 1))
     return;
 
-  player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
-  player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
+#if 0
+  printf("::: player->MovPos: %d -> %d\n",
+        player->MovPos,
+        player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
+#endif
 
-#if USE_NEW_BLOCK_STYLE
-#else
-  if (!player->block_last_field &&
-      Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
+#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);
 
-    RemoveField(last_jx, last_jy);
-#endif
+    /* 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);
 
   /* before DrawPlayer() to draw correct player graphic for this case */
   if (player->MovPos == 0)
     CheckGravityMovement(player);
+#endif
 
   if (player->MovPos == 0)     /* player reached destination field */
   {
+#if 0
+    printf("::: player reached destination field\n");
+#endif
+
     if (player->move_delay_reset_counter > 0)
     {
       player->move_delay_reset_counter--;
@@ -8572,14 +9006,6 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
       }
     }
 
-#if USE_NEW_BLOCK_STYLE
-#else
-    if (player->block_last_field &&
-       Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
-
-      RemoveField(last_jx, last_jy);
-#endif
-
     player->last_jx = jx;
     player->last_jy = jy;
 
@@ -8588,7 +9014,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]))
@@ -8621,11 +9047,14 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
                                          CE_PLAYER_ENTERS_X,
                                          player->index_bit, enter_side);
+
+      CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
+                                       CE_MOVE_OF_X, move_direction);
     }
 
     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,
@@ -8634,7 +9063,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)
@@ -8654,7 +9083,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);
@@ -8691,7 +9120,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)
@@ -8845,12 +9274,6 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
   touched_element = (IN_LEV_FIELD(hitx, hity) ?
                     MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
 
-#if !USE_HITTING_SOMETHING_BUGFIX
-  /* "hitting something" is also true when hitting the playfield border */
-  CheckElementChangeBySide(x, y, hitting_element, touched_element,
-                          CE_HITTING_SOMETHING, direction);
-#endif
-
   if (IN_LEV_FIELD(hitx, hity))
   {
     int opposite_direction = MV_DIR_OPPOSITE(direction);
@@ -8864,29 +9287,20 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
 
     if (object_hit)
     {
-#if !USE_HIT_BY_SOMETHING_BUGFIX
-      CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
-                              CE_HIT_BY_SOMETHING, opposite_direction);
-#endif
-
       CheckElementChangeBySide(x, y, hitting_element, touched_element,
                               CE_HITTING_X, touched_side);
 
       CheckElementChangeBySide(hitx, hity, touched_element,
                               hitting_element, CE_HIT_BY_X, hitting_side);
 
-#if USE_HIT_BY_SOMETHING_BUGFIX
       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
                               CE_HIT_BY_SOMETHING, opposite_direction);
-#endif
     }
   }
 
-#if USE_HITTING_SOMETHING_BUGFIX
   /* "hitting something" is also true when hitting the playfield border */
   CheckElementChangeBySide(x, y, hitting_element, touched_element,
                           CE_HITTING_SOMETHING, direction);
-#endif
 }
 
 #if 0
@@ -8982,7 +9396,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);
 
@@ -9010,7 +9424,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);
@@ -9056,7 +9470,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];
 
@@ -9103,41 +9517,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)
@@ -9174,7 +9588,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;
 
@@ -9189,36 +9603,36 @@ 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;
 
   if (!player->active)
     return;
 
-  PlayLevelSoundElementAction(jx, jy, player->element_nr, ACTION_DYING);
+  PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
   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;
@@ -9229,6 +9643,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;
@@ -9240,6 +9657,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()
@@ -9286,14 +9724,15 @@ 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];
   int element;
+  int collect_count;
 
   if (is_player)               /* function can also be called by EL_PENGUIN */
   {
@@ -9309,11 +9748,7 @@ int DigField(struct PlayerInfo *player,
     if (mode == DF_NO_PUSH)    /* player just stopped pushing */
     {
       player->is_switching = FALSE;
-#if USE_NEW_PUSH_DELAY
       player->push_delay = -1;
-#else
-      player->push_delay = 0;
-#endif
 
       return MF_NO_ACTION;
     }
@@ -9325,12 +9760,10 @@ int DigField(struct PlayerInfo *player,
   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
     old_element = Back[jx][jy];
 
-#if USE_BACK_WALKABLE_BUGFIX
   /* in case of element dropped at player position, check background */
   else if (Back[jx][jy] != EL_EMPTY &&
           game.engine_version >= VERSION_IDENT(2,2,0,0))
     old_element = Back[jx][jy];
-#endif
 
   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
     return MF_NO_ACTION;       /* field has no opening in this direction */
@@ -9339,13 +9772,43 @@ int DigField(struct PlayerInfo *player,
     return MF_NO_ACTION;       /* field has no opening in this direction */
 
   element = Feld[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 &&
+      CustomValue[x][y] != element_info[element].collect_count_initial)
+    printf("::: %d: %d != %d\n",
+          element,
+          CustomValue[x][y],
+          element_info[element].collect_count_initial);
+#endif
 
   if (!is_player && !IS_COLLECTIBLE(element))  /* penguin cannot collect it */
     return MF_NO_ACTION;
 
   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
       game.engine_version >= VERSION_IDENT(2,2,0,0))
+  {
+    CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
+                              player->index_bit, dig_side);
+    CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
+                                       player->index_bit, dig_side);
+
+    if (Feld[x][y] != element)         /* field changed by snapping */
+      return MF_ACTION;
+
     return MF_NO_ACTION;
+  }
 
   if (game.gravity && is_player && !player->is_auto_moving &&
       canFallDown(player) && move_direction != MV_DOWN &&
@@ -9367,6 +9830,11 @@ int DigField(struct PlayerInfo *player,
       if (!player->key[RND_GATE_GRAY_NR(element)])
        return MF_NO_ACTION;
     }
+    else if (IS_RND_GATE_GRAY_ACTIVE(element))
+    {
+      if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
+       return MF_NO_ACTION;
+    }
     else if (element == EL_EXIT_OPEN ||
             element == EL_SP_EXIT_OPEN ||
             element == EL_SP_EXIT_OPENING)
@@ -9382,7 +9850,7 @@ int DigField(struct PlayerInfo *player,
     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
     else
-      PlayLevelSoundElementAction(x, y, player->element_nr, sound_action);
+      PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
   }
   else if (IS_PASSABLE(element) && canPassField(x, y, move_direction))
   {
@@ -9402,6 +9870,11 @@ int DigField(struct PlayerInfo *player,
       if (!player->key[EM_GATE_GRAY_NR(element)])
        return MF_NO_ACTION;
     }
+    else if (IS_EM_GATE_GRAY_ACTIVE(element))
+    {
+      if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
+       return MF_NO_ACTION;
+    }
     else if (IS_SP_PORT(element))
     {
       if (element == EL_SP_GRAVITY_PORT_LEFT ||
@@ -9449,7 +9922,19 @@ int DigField(struct PlayerInfo *player,
                                        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);
+      else
+       TestIfElementTouchesCustomElement(x, y);        /* for empty space */
+#else
+      TestIfElementTouchesCustomElement(x, y);         /* for empty space */
+#endif
+
+      CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
+                                         player->index_bit, dig_side);
+    }
   }
   else if (IS_COLLECTIBLE(element))
   {
@@ -9467,14 +9952,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)
     {
@@ -9508,24 +9993,35 @@ int DigField(struct PlayerInfo *player,
     {
       player->show_envelope = element;
     }
+    else if (element == EL_EMC_LENSES)
+    {
+      game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
+
+      RedrawAllInvisibleElementsForLenses();
+    }
+    else if (element == EL_EMC_MAGNIFIER)
+    {
+      game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
+
+      RedrawAllInvisibleElementsForMagnifier();
+    }
     else if (IS_DROPPABLE(element) ||
             IS_THROWABLE(element))     /* can be collected and dropped */
     {
       int i;
 
-      if (element_info[element].collect_count == 0)
+      if (collect_count == 0)
        player->inventory_infinite_element = element;
       else
-       for (i = 0; i < element_info[element].collect_count; i++)
+       for (i = 0; i < collect_count; i++)
          if (player->inventory_size < MAX_INVENTORY_SIZE)
            player->inventory_element[player->inventory_size++] = element;
 
       DrawGameValue_Dynamite(local_player->inventory_size);
     }
-    else if (element_info[element].collect_count > 0)
+    else if (collect_count > 0)
     {
-      local_player->gems_still_needed -=
-       element_info[element].collect_count;
+      local_player->gems_still_needed -= collect_count;
       if (local_player->gems_still_needed < 0)
        local_player->gems_still_needed = 0;
 
@@ -9536,12 +10032,23 @@ int DigField(struct PlayerInfo *player,
     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
 
     if (is_player)
-      CheckTriggeredElementChangeByPlayer(x, y, 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);
+      else
+       TestIfElementTouchesCustomElement(x, y);        /* for empty space */
+#else
+      TestIfElementTouchesCustomElement(x, y);         /* for empty space */
+#endif
+
+      CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
+                                         player->index_bit, dig_side);
+    }
   }
   else if (IS_PUSHABLE(element))
   {
@@ -9600,32 +10107,16 @@ int DigField(struct PlayerInfo *player,
     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
       return MF_NO_ACTION;
 
-#if USE_NEW_PUSH_DELAY
     if (player->push_delay == -1)      /* new pushing; restart delay */
       player->push_delay = 0;
-#else
-    if (player->push_delay == 0)       /* new pushing; restart delay */
-      player->push_delay = FrameCounter;
-#endif
 
-#if USE_NEW_PUSH_DELAY
     if (player->push_delay < player->push_delay_value &&
        !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
        element != EL_SPRING && element != EL_BALLOON)
-#else
-    if (!FrameReached(&player->push_delay, player->push_delay_value) &&
-       !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
-       element != EL_SPRING && element != EL_BALLOON)
-#endif
     {
       /* make sure that there is no move delay before next try to push */
-#if USE_NEW_MOVE_DELAY
       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
        player->move_delay = 0;
-#else
-      if (game.engine_version >= VERSION_IDENT(3,0,7,1))
-       player->move_delay = INITIAL_MOVE_DELAY_OFF;
-#endif
 
       return MF_NO_ACTION;
     }
@@ -9681,26 +10172,20 @@ int DigField(struct PlayerInfo *player,
     else
       player->push_delay_value = -1;   /* get new value later */
 
-#if USE_PUSH_BUGFIX
-    /* now: check for element change _after_ element has been pushed! */
+    /* check for element change _after_ element has been pushed */
     if (game.use_change_when_pushing_bug)
     {
       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
                                 player->index_bit, dig_side);
-      CheckTriggeredElementChangeByPlayer(x,y, element, CE_PLAYER_PUSHES_X,
+      CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
                                          player->index_bit, dig_side);
     }
-
-#else
-    /* check for element change _after_ element has been pushed! */
-#endif
   }
   else if (IS_SWITCHABLE(element))
   {
     if (PLAYER_SWITCHING(player, x, y))
     {
-      CheckTriggeredElementChangeByPlayer(x,y, element,
-                                         CE_PLAYER_PRESSES_X,
+      CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
                                          player->index_bit, dig_side);
 
       return MF_ACTION;
@@ -9750,20 +10235,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)
     {
@@ -9776,15 +10260,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(x, y, element,
-                                       CE_SWITCH_OF_X,
+    CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
+                                       player->index_bit, dig_side);
+
+    CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
                                        player->index_bit, dig_side);
 
     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
@@ -9802,8 +10292,12 @@ int DigField(struct PlayerInfo *player,
 
       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
                                 player->index_bit, dig_side);
-      CheckTriggeredElementChangeByPlayer(x, y, element,
-                                         CE_SWITCH_OF_X,
+      CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
+                                         player->index_bit, dig_side);
+
+      CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
+                                player->index_bit, dig_side);
+      CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
                                          player->index_bit, dig_side);
     }
 
@@ -9815,15 +10309,9 @@ int DigField(struct PlayerInfo *player,
     return MF_NO_ACTION;
   }
 
-#if USE_NEW_PUSH_DELAY
   player->push_delay = -1;
-#else
-  player->push_delay = 0;
-#endif
 
-#if USE_PENGUIN_COLLECT_BUGFIX
   if (is_player)               /* function can also be called by EL_PENGUIN */
-#endif
   {
     if (Feld[x][y] != element)         /* really digged/collected something */
       player->is_collecting = !player->is_digging;
@@ -9836,10 +10324,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;
@@ -9915,14 +10403,12 @@ boolean DropElement(struct PlayerInfo *player)
                      EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
                      EL_UNDEFINED);
 
-#if USE_DROP_BUGFIX
   /* do not drop an element on top of another element; when holding drop key
      pressed without moving, dropped element must move away before the next
      element can be dropped (this is especially important if the next element
      is dynamite, which can be placed on background for historical reasons) */
   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
     return MF_ACTION;
-#endif
 
   if (IS_THROWABLE(drop_element))
   {
@@ -10029,10 +10515,8 @@ boolean DropElement(struct PlayerInfo *player)
   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
   player->is_dropping = TRUE;
 
-#if USE_DROP_BUGFIX
   player->drop_x = dropx;
   player->drop_y = dropy;
-#endif
 
   return TRUE;
 }
@@ -10375,7 +10859,7 @@ void RaiseScoreElement(int element)
       RaiseScore(level.score[SC_SHIELD]);
       break;
     case EL_EXTRA_TIME:
-      RaiseScore(level.score[SC_TIME_BONUS]);
+      RaiseScore(level.extra_time_score);
       break;
     case EL_KEY_1:
     case EL_KEY_2: