rnd-20051216-1-src
[rocksndiamonds.git] / src / game.c
index 2d9657650903c2ce6c83558e55c76ce98a474f7d..07585bb6404e1d8709026d50ed3716d40a47ec7c 100644 (file)
 #define USE_NEW_AMOEBA_CODE    FALSE
 
 /* EXPERIMENTAL STUFF */
-#define USE_NEW_STUFF                  (TRUE                           * 1)
-
-#define USE_NEW_SP_SLIPPERY            (TRUE   * USE_NEW_STUFF         * 1)
+#define USE_NEW_STUFF                  (                         1)
 
+#define USE_NEW_SP_SLIPPERY            (USE_NEW_STUFF          * 1)
+#define USE_NEW_COLLECT_COUNT          (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)
 
 /* 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)
@@ -232,8 +245,8 @@ static void InitBeltMovement(void);
 static void CloseAllOpenTimegates(void);
 static void CheckGravityMovement(struct PlayerInfo *);
 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
-static void KillHeroUnlessEnemyProtected(int, int);
-static void KillHeroUnlessExplosionProtected(int, int);
+static void KillPlayerUnlessEnemyProtected(int, int);
+static void KillPlayerUnlessExplosionProtected(int, int);
 
 static void TestIfPlayerTouchesCustomElement(int, int);
 static void TestIfElementTouchesCustomElement(int, int);
@@ -244,27 +257,23 @@ static void TestIfElementSmashesCustomElement(int, int, int);
 
 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)
-#define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)         \
-       CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
-#define CheckTriggeredElementChangeBySide(x, y, e, ev, s)              \
-       CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
-#define CheckTriggeredElementChangeByPage(x, y, e, ev, p)              \
-       CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY,      \
-                                      CH_SIDE_ANY, p)
-
-static boolean CheckElementChangeExt(int, int, int, int, int, int, int, int);
+static boolean CheckTriggeredElementChangeExt(int, int, int,int,int);
+#define CheckTriggeredElementChange(e, ev)                             \
+       CheckTriggeredElementChangeExt(e, ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
+#define CheckTriggeredElementChangeByPlayer(e, ev, p, s)               \
+       CheckTriggeredElementChangeExt(e, ev, p, s, -1)
+#define CheckTriggeredElementChangeBySide(e, ev, s)                    \
+       CheckTriggeredElementChangeExt(e, ev, CH_PLAYER_ANY, s, -1)
+#define CheckTriggeredElementChangeByPage(e, ev, p)                    \
+       CheckTriggeredElementChangeExt(e, ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
+
+static boolean 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);
@@ -573,7 +582,7 @@ access_direction_list[] =
   { EL_SP_GRAVITY_OFF_PORT_UP,                                      MV_DOWN },
   { EL_SP_GRAVITY_OFF_PORT_DOWN,                            MV_UP           },
 
-  { EL_UNDEFINED,                      MV_NO_MOVING                         }
+  { EL_UNDEFINED,                      MV_NONE                              }
 };
 
 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
@@ -648,7 +657,7 @@ static int getBeltDirFromBeltSwitchElement(int element)
   static int belt_move_dir[3] =
   {
     MV_LEFT,
-    MV_NO_MOVING,
+    MV_NONE,
     MV_RIGHT
   };
 
@@ -907,6 +916,10 @@ static void InitField(int x, int y, boolean init_game)
       }
       break;
   }
+
+#if USE_NEW_COLLECT_COUNT
+  Count[x][y] = element_info[Feld[x][y]].collect_count_initial;
+#endif
 }
 
 static inline void InitField_WithBug1(int x, int y, boolean init_game)
@@ -1250,12 +1263,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++)
   {
@@ -1263,7 +1293,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++)
@@ -1280,20 +1310,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++)
@@ -1304,6 +1320,7 @@ 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;
     }
   }
 
@@ -1321,7 +1338,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])
@@ -1400,16 +1417,23 @@ static void InitGameEngine()
     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
   }
 
-  /* ---------- initialize gem count --------------------------------------- */
+  /* ---------- initialize collect score ----------------------------------- */
 
-  /* initialize gem count values for each element */
+  /* 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 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 -------------------------------- */
@@ -1475,10 +1499,10 @@ void InitGame()
     player->dynabombs_left = 0;
     player->dynabomb_xl = FALSE;
 
-    player->MovDir = MV_NO_MOVING;
+    player->MovDir = MV_NONE;
     player->MovPos = 0;
     player->GfxPos = 0;
-    player->GfxDir = MV_NO_MOVING;
+    player->GfxDir = MV_NONE;
     player->GfxAction = ACTION_DEFAULT;
     player->Frame = 0;
     player->StepFrame = 0;
@@ -1494,7 +1518,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;
@@ -1564,6 +1588,8 @@ void InitGame()
     player->move_delay       = game.initial_move_delay;
     player->move_delay_value = game.initial_move_delay_value;
 
+    player->move_delay_value_next = -1;
+
     player->move_delay_reset_counter = 0;
 
     player->push_delay       = -1;     /* initialized when pushing starts */
@@ -1592,7 +1618,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;
@@ -1604,7 +1630,7 @@ void InitGame()
   TimeLeft = level.time;
   TapeTime = 0;
 
-  ScreenMovDir = MV_NO_MOVING;
+  ScreenMovDir = MV_NONE;
   ScreenMovPos = 0;
   ScreenGfxPos = 0;
 
@@ -1618,7 +1644,7 @@ void InitGame()
   game.light_time_left = 0;
   game.timegate_time_left = 0;
   game.switchgate_pos = 0;
-  game.balloon_dir = MV_NO_MOVING;
+  game.wind_direction = level.wind_direction_initial;
   game.gravity = level.initial_gravity;
   game.explosions_delayed = TRUE;
 
@@ -1626,7 +1652,7 @@ void InitGame()
 
   for (i = 0; i < NUM_BELTS; i++)
   {
-    game.belt_dir[i] = MV_NO_MOVING;
+    game.belt_dir[i] = MV_NONE;
     game.belt_dir_nr[i] = 3;           /* not moving, next moving left */
   }
 
@@ -1641,6 +1667,9 @@ void InitGame()
       MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
       ChangeDelay[x][y] = 0;
       ChangePage[x][y] = -1;
+#if USE_NEW_COLLECT_COUNT
+      Count[x][y] = 0;         /* initialized in InitField() */
+#endif
       Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
       AmoebaNr[x][y] = 0;
       WasJustMoving[x][y] = 0;
@@ -1663,7 +1692,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;
     }
   }
 
@@ -1688,6 +1717,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++)
   {
@@ -1718,7 +1767,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 */
@@ -1885,7 +1934,7 @@ void InitGame()
 
       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
       {
-       content = element_info[element].content[xx][yy];
+       content = element_info[element].content.e[xx][yy];
        is_player = ELEM_IS_PLAYER(content);
 
        if (is_player && (found_rating < 2 || element < found_element))
@@ -1902,7 +1951,9 @@ void InitGame()
 
        for (i = 0; i < element_info[element].num_change_pages; i++)
        {
-         content= element_info[element].change_page[i].target_content[xx][yy];
+         content =
+           element_info[element].change_page[i].target_content.e[xx][yy];
+
          is_player = ELEM_IS_PLAYER(content);
 
          if (is_player && (found_rating < 1 || element < found_element))
@@ -2097,7 +2148,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;
@@ -2288,7 +2339,7 @@ void GameWon()
     PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
   }
 
-  /* Hero disappears */
+  /* player disappears */
   if (ExitX >= 0 && ExitY >= 0)
     DrawLevelField(ExitX, ExitY);
 
@@ -2483,6 +2534,11 @@ void InitMovingField(int x, int y, int direction)
       Feld[newx][newy] = EL_BLOCKED;
 
     MovDir[newx][newy] = MovDir[x][y];
+
+#if USE_NEW_COLLECT_COUNT
+    Count[newx][newy] = Count[x][y];
+#endif
+
     GfxFrame[newx][newy] = GfxFrame[x][y];
     GfxRandom[newx][newy] = GfxRandom[x][y];
     GfxAction[newx][newy] = GfxAction[x][y];
@@ -2564,6 +2620,10 @@ static void RemoveField(int x, int y)
   MovDir[x][y] = 0;
   MovDelay[x][y] = 0;
 
+#if USE_NEW_COLLECT_COUNT
+  Count[x][y] = 0;
+#endif
+
   AmoebaNr[x][y] = 0;
   ChangeDelay[x][y] = 0;
   ChangePage[x][y] = -1;
@@ -2575,7 +2635,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)
@@ -2832,8 +2892,7 @@ void RelocatePlayer(int jx, int jy, int el_player_raw)
                               CE_LEFT_BY_PLAYER,
                               player->index_bit, leave_side);
 
-  CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
-                                     CE_PLAYER_LEAVES_X,
+  CheckTriggeredElementChangeByPlayer(old_element, CE_PLAYER_LEAVES_X,
                                      player->index_bit, leave_side);
 
   Feld[jx][jy] = el_player;
@@ -2848,15 +2907,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(element, CE_PLAYER_ENTERS_X,
                                      player->index_bit, enter_side);
 }
 
@@ -3011,10 +3069,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)
@@ -3032,7 +3090,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;
 
@@ -3100,7 +3158,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))
@@ -3148,10 +3206,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_COLLECT_COUNT
+    Count[x][y] = 0;
+#endif
+
     InitField_WithBug2(x, y, FALSE);
 
     DrawLevelField(x, y);
@@ -3295,7 +3357,7 @@ void Bang(int x, int y)
       break;
   }
 
-  CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
+  CheckTriggeredElementChange(element, CE_EXPLOSION_OF_X);
 }
 
 void SplashAcid(int x, int y)
@@ -3357,7 +3419,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;
@@ -3400,9 +3462,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];
@@ -3448,7 +3510,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);
 
@@ -3460,7 +3522,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);
 
@@ -3690,7 +3752,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
@@ -3732,7 +3794,7 @@ void Impact(int x, int y)
     {
       if (CAN_SMASH_PLAYER(element))
       {
-       KillHeroUnlessEnemyProtected(x, y + 1);
+       KillPlayerUnlessEnemyProtected(x, y + 1);
        return;
       }
     }
@@ -3824,8 +3886,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(smashed, CE_SWITCH_OF_X,
+                                           CH_SIDE_TOP);
        }
       }
       else
@@ -4113,7 +4175,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)
@@ -4121,7 +4183,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;
   }
@@ -4190,7 +4252,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)
@@ -4327,6 +4389,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))
@@ -4380,7 +4447,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)
@@ -4426,7 +4493,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;
   }
@@ -4454,7 +4521,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;
 
@@ -4703,7 +4770,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;
@@ -4735,11 +4802,26 @@ 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_ANY_LEFT_RIGHT && can_fall_both)
+         can_fall_right = FALSE;
+       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)
@@ -4752,7 +4834,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) &&
@@ -4762,7 +4847,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 ||
@@ -4773,6 +4870,7 @@ void StartMoving(int x, int y)
 
        can_fall_both = FALSE;
       }
+#endif
 
       if (can_fall_any)
       {
@@ -4814,7 +4912,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
@@ -4824,7 +4922,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);
@@ -4975,7 +5073,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;
     }
@@ -5011,7 +5109,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))
       {
@@ -5276,7 +5374,7 @@ void StartMoving(int x, int y)
        DrawLevelElementAnimation(x, y, element);
 
       if (DONT_TOUCH(element))
-       TestIfBadThingTouchesHero(x, y);
+       TestIfBadThingTouchesPlayer(x, y);
 
       return;
     }
@@ -5290,11 +5388,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];
@@ -5359,6 +5452,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_COLLECT_COUNT
+    InitField(newx, newy, FALSE);
+#endif
   }
   else if (element == EL_BD_MAGIC_WALL_FILLING)
   {
@@ -5373,6 +5470,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_COLLECT_COUNT
+    InitField(newx, newy, FALSE);
+#endif
   }
   else if (element == EL_AMOEBA_DROPPING)
   {
@@ -5394,6 +5495,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))
@@ -5403,6 +5505,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_COLLECT_COUNT
+    Count[newx][newy] = Count[x][y];
+#endif
   }
 
   ChangeDelay[x][y] = 0;
@@ -5410,6 +5516,10 @@ void ContinueMoving(int x, int y)
   Changed[x][y] = FALSE;
   ChangeEvent[x][y] = -1;
 
+#if USE_NEW_COLLECT_COUNT
+  Count[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 */
@@ -5486,7 +5596,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))
@@ -5509,7 +5619,7 @@ void ContinueMoving(int x, int y)
 
     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
                               player->index_bit, dig_side);
-    CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
+    CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PUSHES_X,
                                        player->index_bit, dig_side);
   }
 
@@ -5926,10 +6036,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))
@@ -5972,7 +6086,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])
@@ -5983,7 +6098,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);
@@ -6176,7 +6292,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);
     }
   }
@@ -6402,14 +6518,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 :
@@ -6417,7 +6534,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];
@@ -6437,28 +6554,69 @@ static void ExecuteCustomElementAction(int element, int page)
      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
      EL_EMPTY);
 
+  int action_arg_number_min =
+    (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MIN :
+     CA_ARG_MIN);
+
+  int action_arg_number_max =
+    (action_type == CA_SET_PLAYER_SPEED ? MOVE_STEPSIZE_MAX :
+     action_type == CA_SET_GEMS ? 999 :
+     action_type == CA_SET_TIME ? 9999 :
+     action_type == CA_SET_SCORE ? 99999 :
+     action_type == CA_SET_CE_SCORE ? 9999 :
+     action_type == CA_SET_CE_COUNT ? 9999 :
+     CA_ARG_MAX);
+
+  int action_arg_number_reset =
+    (action_type == CA_SET_PLAYER_SPEED ? TILEX/game.initial_move_delay_value :
+     action_type == CA_SET_GEMS ? level.gems_needed :
+     action_type == CA_SET_TIME ? level.time :
+     action_type == CA_SET_SCORE ? 0 :
+     action_type == CA_SET_CE_SCORE ? 0 :
+     action_type == CA_SET_CE_COUNT ? ei->collect_count_initial :
+     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_VERY_SLOW &&
+     action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
+     action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
+     action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
+     action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
+     action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
-     action_arg == CA_ARG_NUMBER_CE_COUNT ? ei->collect_count :
+#if USE_NEW_COLLECT_COUNT
+     action_arg == CA_ARG_NUMBER_CE_COUNT ? Count[x][y] :
+#else
+     action_arg == CA_ARG_NUMBER_CE_COUNT ? ei->collect_count_initial :
+#endif
      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CHANGE_DELAY(change) :
      -1);
 
-  /* (for explicit player choice, set invalid value to "no player") */
+  int action_arg_number_old =
+    (action_type == CA_SET_GEMS ? local_player->gems_still_needed :
+     action_type == CA_SET_TIME ? TimeLeft :
+     action_type == CA_SET_SCORE ? local_player->score :
+     action_type == CA_SET_CE_SCORE ? ei->collect_score :
+     action_type == CA_SET_CE_COUNT ? Count[x][y] :
+     0);
+
+  int action_arg_number_new =
+    getModifiedActionNumber(action_arg_number_old,
+                           action_mode, action_arg_number,
+                           action_arg_number_min, action_arg_number_max);
+
   int action_arg_player_bits =
-    (action_arg == CA_ARG_PLAYER_ANY ? action_arg - CA_ARG_PLAYER :
+    (action_arg == CA_ARG_PLAYER_ANY ? PLAYER_BITS_ANY :
      action_arg >= CA_ARG_PLAYER_1 &&
      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
      action_arg >= CA_ARG_1 &&
-     action_arg <= CA_ARG_PLAYER_4 ? (1 << (action_arg - 1)) :
+     action_arg <= CA_ARG_PLAYER_4 ? (1 << (action_arg - CA_ARG_1)) :
      action_arg_element >= EL_PLAYER_1 &&
      action_arg_element <= EL_PLAYER_4 ?
      (1 << (action_arg_element - EL_PLAYER_1)) :
-     0);
+     PLAYER_BITS_ANY);
 
-  /* (for implicit player choice, set invalid value to "all players") */
   int trigger_player_bits =
     (change->actual_trigger_player >= EL_PLAYER_1 &&
      change->actual_trigger_player <= EL_PLAYER_4 ?
@@ -6487,7 +6645,7 @@ static void ExecuteCustomElementAction(int element, int page)
     {
       for (i = 0; i < MAX_PLAYERS; i++)
        if (action_arg_player_bits & (1 << i))
-         KillHero(&stored_player[i]);
+         KillPlayer(&stored_player[i]);
 
       break;
     }
@@ -6556,55 +6714,9 @@ static void ExecuteCustomElementAction(int element, int page)
       break;
     }
 
-    case CA_SET_PLAYER_SPEED:
-    {
-      for (i = 0; i < MAX_PLAYERS; i++)
-      {
-       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);
-
-           stored_player[i].move_delay_value =
-             getModifiedActionNumber(stored_player[i].move_delay_value,
-                                     1, 16,
-                                     action_mode, action_arg_number);
-#endif
-         }
-       }
-      }
-
-      break;
-    }
-
     case CA_SET_GEMS:
     {
-      local_player->gems_still_needed =
-       getModifiedActionNumber(local_player->gems_still_needed, 0, 999,
-                               action_mode, action_arg_number);
+      local_player->gems_still_needed = action_arg_number_new;
 
       DrawGameValue_Emeralds(local_player->gems_still_needed);
 
@@ -6615,10 +6727,13 @@ static void ExecuteCustomElementAction(int element, int page)
     {
       if (level.time > 0)      /* only modify limited time value */
       {
-       TimeLeft = getModifiedActionNumber(TimeLeft, 0, 9999,
-                                          action_mode, action_arg_number);
+       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;
@@ -6626,9 +6741,7 @@ static void ExecuteCustomElementAction(int element, int page)
 
     case CA_SET_SCORE:
     {
-      local_player->score =
-       getModifiedActionNumber(local_player->score, 0, 99999,
-                               action_mode, action_arg_number);
+      local_player->score = action_arg_number_new;
 
       DrawGameValue_Score(local_player->score);
 
@@ -6637,59 +6750,159 @@ static void ExecuteCustomElementAction(int element, int page)
 
     case CA_SET_CE_SCORE:
     {
-      printf("::: CA_SET_CE_SCORE -- not yet implemented\n");
+      ei->collect_score = action_arg_number_new;
 
       break;
     }
 
     case CA_SET_CE_COUNT:
     {
-      printf("::: CA_SET_CE_COUNT -- not yet implemented\n");
+#if USE_NEW_COLLECT_COUNT
+      int count_last = Count[x][y];
+
+      Count[x][y] = action_arg_number_new;
+
+#if 0
+      printf("::: Count == %d\n", Count[x][y]);
+#endif
+
+      if (Count[x][y] == 0 && count_last > 0)
+      {
+#if 0
+       printf("::: CE_COUNT_AT_ZERO\n");
+#endif
+
+       CheckElementChange(x, y, element, EL_UNDEFINED, CE_COUNT_AT_ZERO);
+       CheckTriggeredElementChange(element, CE_COUNT_AT_ZERO_OF_X);
+      }
+#endif
 
       break;
     }
 
-    case CA_SET_DYNABOMB_NUMBER:
+#if 1
+    case CA_SET_PLAYER_SPEED:
     {
-      printf("::: CA_SET_DYNABOMB_NUMBER -- not yet implemented\n");
+      for (i = 0; i < MAX_PLAYERS; i++)
+      {
+       if (trigger_player_bits & (1 << i))
+       {
+         int move_stepsize = TILEX / stored_player[i].move_delay_value;
+
+         if (action_arg == CA_ARG_SPEED_SLOWER ||
+             action_arg == CA_ARG_SPEED_FASTER)
+         {
+           action_arg_number = 2;
+           action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
+                          CA_MODE_MULTIPLY);
+         }
+
+         move_stepsize =
+           getModifiedActionNumber(move_stepsize,
+                                   action_mode,
+                                   action_arg_number,
+                                   action_arg_number_min,
+                                   action_arg_number_max);
+
+         /* make sure that value is power of 2 */
+         move_stepsize = (1 << log_2(move_stepsize));
+
+         /* do no immediately change -- the player might just be moving */
+         stored_player[i].move_delay_value_next = TILEX / move_stepsize;
+
+#if 0
+         printf("::: move_delay_value == %d [%d]\n",
+                stored_player[i].move_delay_value_next, action_arg_number);
+#endif
+       }
+      }
 
       break;
     }
-
-    case CA_SET_DYNABOMB_SIZE:
+#else
+    case CA_SET_PLAYER_SPEED:
     {
-      printf("::: CA_SET_DYNABOMB_SIZE -- not yet implemented\n");
+      for (i = 0; i < MAX_PLAYERS; i++)
+      {
+       if (trigger_player_bits & (1 << i))
+       {
+         int move_stepsize = TILEX / stored_player[i].move_delay_value;
+
+         if (action_mode == CA_MODE_ADD || action_mode == CA_MODE_SUBTRACT)
+         {
+           /* translate "+" and "-" to "*" and "/" with powers of two */
+           action_arg_number = 1 << action_arg_number;
+           action_mode = (action_mode == CA_MODE_ADD ? CA_MODE_MULTIPLY :
+                          CA_MODE_DIVIDE);
+         }
+
+         move_stepsize =
+           getModifiedActionNumber(move_stepsize,
+                                   action_mode,
+                                   action_arg_number,
+                                   action_arg_number_min,
+                                   action_arg_number_max);
+
+         /* make sure that value is power of 2 */
+         move_stepsize = (1 << log_2(move_stepsize));
+
+         /* do no immediately change -- the player might just be moving */
+         stored_player[i].move_delay_value_next = TILEX / move_stepsize;
+
+#if 0
+         printf("::: move_delay_value == %d [%d]\n",
+                stored_player[i].move_delay_value_next, action_arg_number);
+#endif
+       }
+      }
 
       break;
     }
+#endif
 
-    case CA_SET_DYNABOMB_POWER:
+    case CA_SET_PLAYER_GRAVITY:
     {
-      printf("::: CA_SET_DYNABOMB_POWER -- not yet implemented\n");
+      game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
+                     action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
+                     action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
+                     game.gravity);
+      break;
+    }
+
+    case CA_SET_WIND_DIRECTION:
+    {
+      game.wind_direction = (action_arg >= CA_ARG_DIRECTION_NONE &&
+                            action_arg <= CA_ARG_DIRECTION_DOWN ?
+                            action_arg - CA_ARG_DIRECTION :
+                            action_arg == CA_ARG_DIRECTION_TRIGGER ?
+                            MV_DIR_OPPOSITE(change->actual_trigger_side) :
+                            game.wind_direction);
 
       break;
     }
 
-    case CA_TOGGLE_PLAYER_GRAVITY:
+#if 0
+    case CA_SET_DYNABOMB_NUMBER:
     {
-      game.gravity = !game.gravity;
+      printf("::: CA_SET_DYNABOMB_NUMBER -- not yet implemented\n");
 
       break;
     }
 
-    case CA_ENABLE_PLAYER_GRAVITY:
+    case CA_SET_DYNABOMB_SIZE:
     {
-      game.gravity = TRUE;
+      printf("::: CA_SET_DYNABOMB_SIZE -- not yet implemented\n");
 
       break;
     }
 
-    case CA_DISABLE_PLAYER_GRAVITY:
+    case CA_SET_DYNABOMB_POWER:
     {
-      game.gravity = FALSE;
+      printf("::: CA_SET_DYNABOMB_POWER -- not yet implemented\n");
 
       break;
     }
+#endif
 
     default:
       break;
@@ -6745,7 +6958,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);
 }
@@ -6765,6 +6978,7 @@ 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;
   }
 
 #if 1
@@ -6806,7 +7020,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;
@@ -6878,7 +7092,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);
@@ -6892,7 +7106,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
@@ -6902,14 +7119,109 @@ static boolean ChangeElementNow(int x, int y, int element, int page)
     ChangeElementNowExt(change, x, y, target_element);
 
     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
+    PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
   }
 
   /* this uses direct change before indirect change */
-  CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
+  CheckTriggeredElementChangeByPage(old_element, CE_CHANGE_OF_X, page);
 
   return TRUE;
 }
 
+#if USE_NEW_DELAYED_ACTION
+
+static void ChangeElement(int x, int y, int page)
+{
+  int element = MovingOrBlocked2Element(x, y);
+  struct ElementInfo *ei = &element_info[element];
+  struct ElementChangeInfo *change = &ei->change_page[page];
+
+#ifdef DEBUG
+  if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
+      !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
+  {
+    printf("\n\n");
+    printf("ChangeElement(): %d,%d: element = %d ('%s')\n",
+          x, y, element, element_info[element].token_name);
+    printf("ChangeElement(): This should never happen!\n");
+    printf("\n\n");
+  }
+#endif
+
+  /* this can happen with classic bombs on walkable, changing elements */
+  if (!CAN_CHANGE_OR_HAS_ACTION(element))
+  {
+#if 0
+    if (!CAN_CHANGE(Back[x][y]))       /* prevent permanent repetition */
+      ChangeDelay[x][y] = 0;
+#endif
+
+    return;
+  }
+
+  if (ChangeDelay[x][y] == 0)          /* initialize element change */
+  {
+    ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
+
+    if (change->can_change)
+    {
+      ResetGfxAnimation(x, y);
+      ResetRandomAnimationValue(x, y);
+
+      if (change->pre_change_function)
+       change->pre_change_function(x, y);
+    }
+  }
+
+  ChangeDelay[x][y]--;
+
+  if (ChangeDelay[x][y] != 0)          /* continue element change */
+  {
+    if (change->can_change)
+    {
+      int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+
+      if (IS_ANIMATED(graphic))
+       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
+
+      if (change->change_function)
+       change->change_function(x, y);
+    }
+  }
+  else                                 /* finish element change */
+  {
+    if (ChangePage[x][y] != -1)                /* remember page from delayed change */
+    {
+      page = ChangePage[x][y];
+      ChangePage[x][y] = -1;
+
+      change = &ei->change_page[page];
+    }
+
+    if (IS_MOVING(x, y))               /* never change a running system ;-) */
+    {
+      ChangeDelay[x][y] = 1;           /* try change after next move step */
+      ChangePage[x][y] = page;         /* remember page to use for change */
+
+      return;
+    }
+
+    if (change->can_change)
+    {
+      if (ChangeElementNow(x, y, element, page))
+      {
+       if (change->post_change_function)
+         change->post_change_function(x, y);
+      }
+    }
+
+    if (change->has_action)
+      ExecuteCustomElementAction(x, y, element, page);
+  }
+}
+
+#else
+
 static void ChangeElement(int x, int y, int page)
 {
   int element = MovingOrBlocked2Element(x, y);
@@ -6987,15 +7299,17 @@ static void ChangeElement(int x, int y, int page)
   }
 }
 
-static boolean CheckTriggeredElementChangeExt(int lx, int ly,
-                                             int trigger_element,
+#endif
+
+static boolean CheckTriggeredElementChangeExt(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;
@@ -7003,15 +7317,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] &&
@@ -7022,29 +7337,49 @@ 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;
 
-       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,
@@ -7052,10 +7387,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)
@@ -7067,54 +7405,51 @@ 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;
 
-      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)
@@ -7351,12 +7686,25 @@ 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)
@@ -7610,11 +7958,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] : page);
+#else
+
 #if 0
-      ChangeElement(x, y, ChangePage[x][y] != -1 ? ChangePage[x][y] :
-                   element_info[element].event_page_nr[CE_DELAY]);
+      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];
@@ -7858,7 +8220,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);
@@ -8075,7 +8437,7 @@ boolean MovePlayerOneStep(struct PlayerInfo *player,
   player->MovDir = (dx < 0 ? MV_LEFT :
                    dx > 0 ? MV_RIGHT :
                    dy < 0 ? MV_UP :
-                   dy > 0 ? MV_DOWN :  MV_NO_MOVING);
+                   dy > 0 ? MV_DOWN :  MV_NONE);
 
   if (!IN_LEV_FIELD(new_jx, new_jy))
     return MF_NO_ACTION;
@@ -8094,10 +8456,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;
   }
@@ -8117,6 +8479,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);
 
@@ -8158,7 +8526,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
   player->move_delay = -1;             /* set to "uninitialized" value */
 
   /* store if player is automatically moved to next field */
-  player->is_auto_moving = (player->programmed_action != MV_NO_MOVING);
+  player->is_auto_moving = (player->programmed_action != MV_NONE);
 
   /* remove the last programmed player action */
   player->programmed_action = 0;
@@ -8312,12 +8680,12 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
 
   if (game.engine_version < VERSION_IDENT(3,0,7,0))
   {
-    TestIfHeroTouchesBadThing(jx, jy);
+    TestIfPlayerTouchesBadThing(jx, jy);
     TestIfPlayerTouchesCustomElement(jx, jy);
   }
 
   if (!player->active)
-    RemoveHero(player);
+    RemovePlayer(player);
 
   return moved;
 }
@@ -8328,9 +8696,17 @@ 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;
@@ -8359,20 +8735,47 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
     }
 
+#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;
 
+#if 0
+  printf("::: player->MovPos: %d -> %d\n",
+        player->MovPos,
+        player->MovPos + (player->MovPos > 0 ? -1 : 1) * move_stepsize);
+#endif
+
+#if USE_NEW_PLAYER_SPEED
+  if (player->MovPos != 0)
+  {
+    player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
+    player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
+
+    /* 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--;
@@ -8395,7 +8798,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]))
@@ -8417,22 +8820,20 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
                                   CE_LEFT_BY_PLAYER,
                                   player->index_bit, leave_side);
 
-      CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
-                                         CE_PLAYER_LEAVES_X,
+      CheckTriggeredElementChangeByPlayer(old_element, CE_PLAYER_LEAVES_X,
                                          player->index_bit, leave_side);
 
       if (IS_CUSTOM_ELEMENT(new_element))
        CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
                                   player->index_bit, enter_side);
 
-      CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
-                                         CE_PLAYER_ENTERS_X,
+      CheckTriggeredElementChangeByPlayer(new_element, CE_PLAYER_ENTERS_X,
                                          player->index_bit, enter_side);
     }
 
     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
     {
-      TestIfHeroTouchesBadThing(jx, jy);
+      TestIfPlayerTouchesBadThing(jx, jy);
       TestIfPlayerTouchesCustomElement(jx, jy);
 
       /* needed because pushed element has not yet reached its destination,
@@ -8441,7 +8842,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)
@@ -8461,7 +8862,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);
@@ -8498,7 +8899,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)
@@ -8554,8 +8955,7 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
 
       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
                                 player->index_bit, border_side);
-      CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
-                                         CE_PLAYER_TOUCHES_X,
+      CheckTriggeredElementChangeByPlayer(border_element, CE_PLAYER_TOUCHES_X,
                                          player->index_bit, border_side);
     }
     else if (IS_PLAYER(xx, yy))
@@ -8570,8 +8970,7 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
 
       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
                                 player->index_bit, center_side);
-      CheckTriggeredElementChangeByPlayer(x, y, center_element,
-                                         CE_PLAYER_TOUCHES_X,
+      CheckTriggeredElementChangeByPlayer(center_element, CE_PLAYER_TOUCHES_X,
                                          player->index_bit, center_side);
       break;
     }
@@ -8774,7 +9173,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);
 
@@ -8802,7 +9201,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);
@@ -8848,7 +9247,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];
 
@@ -8895,41 +9294,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)
@@ -8966,7 +9365,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;
 
@@ -8981,22 +9380,22 @@ void KillHero(struct PlayerInfo *player)
   player->shield_deadly_time_left = 0;
 
   Bang(jx, jy);
-  BuryHero(player);
+  BuryPlayer(player);
 }
 
-static void KillHeroUnlessEnemyProtected(int x, int y)
+static void KillPlayerUnlessEnemyProtected(int x, int y)
 {
   if (!PLAYER_ENEMY_PROTECTED(x, y))
-    KillHero(PLAYERINFO(x, y));
+    KillPlayer(PLAYERINFO(x, y));
 }
 
-static void KillHeroUnlessExplosionProtected(int x, int y)
+static void KillPlayerUnlessExplosionProtected(int x, int y)
 {
   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
-    KillHero(PLAYERINFO(x, y));
+    KillPlayer(PLAYERINFO(x, y));
 }
 
-void BuryHero(struct PlayerInfo *player)
+void BuryPlayer(struct PlayerInfo *player)
 {
   int jx = player->jx, jy = player->jy;
 
@@ -9007,10 +9406,10 @@ void BuryHero(struct PlayerInfo *player)
   PlayLevelSound(jx, jy, SND_GAME_LOSING);
 
   player->GameOver = TRUE;
-  RemoveHero(player);
+  RemovePlayer(player);
 }
 
-void RemoveHero(struct PlayerInfo *player)
+void RemovePlayer(struct PlayerInfo *player)
 {
   int jx = player->jx, jy = player->jy;
   int i, found = FALSE;
@@ -9021,6 +9420,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;
@@ -9078,14 +9480,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 */
   {
@@ -9125,6 +9528,20 @@ int DigField(struct PlayerInfo *player,
     return MF_NO_ACTION;       /* field has no opening in this direction */
 
   element = Feld[x][y];
+#if USE_NEW_COLLECT_COUNT
+  collect_count = Count[x][y];
+#else
+  collect_count = element_info[element].collect_count_initial;
+#endif
+
+#if 0
+  if (element != EL_BLOCKED &&
+      Count[x][y] != element_info[element].collect_count_initial)
+    printf("::: %d: %d != %d\n",
+          element,
+          Count[x][y],
+          element_info[element].collect_count_initial);
+#endif
 
   if (!is_player && !IS_COLLECTIBLE(element))  /* penguin cannot collect it */
     return MF_NO_ACTION;
@@ -9231,7 +9648,7 @@ int DigField(struct PlayerInfo *player,
 
     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
 
-    CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
+    CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_DIGS_X,
                                        player->index_bit, dig_side);
 
     if (mode == DF_SNAP)
@@ -9253,14 +9670,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)
     {
@@ -9299,19 +9716,18 @@ int DigField(struct PlayerInfo *player,
     {
       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;
 
@@ -9322,8 +9738,7 @@ int DigField(struct PlayerInfo *player,
     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
 
     if (is_player)
-      CheckTriggeredElementChangeByPlayer(x, y, element,
-                                         CE_PLAYER_COLLECTS_X,
+      CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_COLLECTS_X,
                                          player->index_bit, dig_side);
 
     if (mode == DF_SNAP)
@@ -9456,7 +9871,7 @@ int DigField(struct PlayerInfo *player,
     {
       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
                                 player->index_bit, dig_side);
-      CheckTriggeredElementChangeByPlayer(x,y, element, CE_PLAYER_PUSHES_X,
+      CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PUSHES_X,
                                          player->index_bit, dig_side);
     }
   }
@@ -9464,8 +9879,7 @@ int DigField(struct PlayerInfo *player,
   {
     if (PLAYER_SWITCHING(player, x, y))
     {
-      CheckTriggeredElementChangeByPlayer(x,y, element,
-                                         CE_PLAYER_PRESSES_X,
+      CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PRESSES_X,
                                          player->index_bit, dig_side);
 
       return MF_ACTION;
@@ -9515,20 +9929,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)
     {
@@ -9541,18 +9954,17 @@ int DigField(struct PlayerInfo *player,
     else if (element == EL_TIME_ORB_FULL)
     {
       Feld[x][y] = EL_TIME_ORB_EMPTY;
-      TimeLeft += 10;
+      TimeLeft += level.time_orb_time;
       DrawGameValue_Time(TimeLeft);
 
       ResetGfxAnimation(x, y);
       DrawLevelField(x, y);
     }
 
-    CheckTriggeredElementChangeByPlayer(x, y, element,
-                                       CE_SWITCH_OF_X,
+    CheckTriggeredElementChangeByPlayer(element, CE_SWITCH_OF_X,
                                        player->index_bit, dig_side);
 
-    CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
+    CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PRESSES_X,
                                        player->index_bit, dig_side);
 
     return MF_ACTION;
@@ -9567,14 +9979,13 @@ 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(element, CE_SWITCH_OF_X,
                                          player->index_bit, dig_side);
     }
 
     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
                               player->index_bit, dig_side);
-    CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
+    CheckTriggeredElementChangeByPlayer(element, CE_PLAYER_PRESSES_X,
                                        player->index_bit, dig_side);
 
     return MF_NO_ACTION;
@@ -9595,10 +10006,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;
@@ -9743,8 +10154,7 @@ boolean DropElement(struct PlayerInfo *player)
 
     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
                               player->index_bit, drop_side);
-    CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
-                                       CE_PLAYER_DROPS_X,
+    CheckTriggeredElementChangeByPlayer(new_element, CE_PLAYER_DROPS_X,
                                        player->index_bit, drop_side);
 
     TestIfElementTouchesCustomElement(dropx, dropy);