rnd-20060606-1-src
[rocksndiamonds.git] / src / game.c
index 98751de41aaeca52086e4695ff2c67da64730a98..ac37d91eb997b8f8f9097c144c23d8a00fdc817b 100644 (file)
@@ -41,6 +41,9 @@
 #define USE_STOP_CHANGED_ELEMENTS      (USE_NEW_STUFF          * 1)
 #define USE_ELEMENT_TOUCHING_BUGFIX    (USE_NEW_STUFF          * 1)
 #define USE_NEW_CONTINUOUS_SNAPPING    (USE_NEW_STUFF          * 1)
+#define USE_GFX_RESET_GFX_ANIMATION    (USE_NEW_STUFF          * 1)
+#define USE_BOTH_SWITCHGATE_SWITCHES   (USE_NEW_STUFF          * 1)
+#define USE_PLAYER_GRAVITY             (USE_NEW_STUFF          * 1)
 
 #define USE_QUICKSAND_IMPACT_BUGFIX    (USE_NEW_STUFF          * 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) +\
+#define GET_NEW_CE_VALUE(e)    (   (element_info[e].ce_value_fixed_initial) +\
                                 RND(element_info[e].ce_value_random_initial))
+#define GET_CE_SCORE(e)                (   (element_info[e].collect_score))
 #define GET_CHANGE_DELAY(c)    (   ((c)->delay_fixed  * (c)->delay_frames) + \
                                 RND((c)->delay_random * (c)->delay_frames))
 #define GET_CE_DELAY_VALUE(c)  (   ((c)->delay_fixed) + \
                                 RND((c)->delay_random))
 
+
 #if 1
 #define GET_VALID_RUNTIME_ELEMENT(e)                                   \
         ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
        ((e) >= NUM_FILE_ELEMENTS ? EL_UNKNOWN : (e))
 #endif
 
-#define GET_TARGET_ELEMENT(e, ch)                                      \
-       ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player  :    \
-        (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element :    \
-        (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  : (e))
+#define GET_TARGET_ELEMENT(e, ch, cv, cs)                              \
+       ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
+        (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
+        (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
+        (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
+        (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
+        (e) == EL_CURRENT_CE_SCORE ? (cs) : (e))
 
 #define CAN_GROW_INTO(e)                                               \
        ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
@@ -358,6 +366,8 @@ void RemovePlayer(struct PlayerInfo *);
 boolean SnapField(struct PlayerInfo *, int, int);
 boolean DropElement(struct PlayerInfo *);
 
+static int getInvisibleActiveFromInvisibleElement(int);
+static int getInvisibleFromInvisibleActiveElement(int);
 
 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
 
@@ -770,6 +780,7 @@ static int get_move_delay_from_stepsize(int move_stepsize)
 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
                               boolean init_game)
 {
+  int player_nr = player->index_nr;
   int move_delay = get_move_delay_from_stepsize(move_stepsize);
   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
 
@@ -781,8 +792,8 @@ static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
 
   if (init_game)
   {
-    player->move_delay       = game.initial_move_delay;
-    player->move_delay_value = game.initial_move_delay_value;
+    player->move_delay       = game.initial_move_delay[player_nr];
+    player->move_delay_value = game.initial_move_delay_value[player_nr];
 
     player->move_delay_value_next = -1;
 
@@ -861,6 +872,32 @@ static int getBeltDirFromBeltSwitchElement(int element)
   return belt_move_dir[belt_dir_nr];
 }
 
+static int get_element_from_group_element(int element)
+{
+  if (IS_GROUP_ELEMENT(element))
+  {
+    struct ElementGroupInfo *group = element_info[element].group;
+    int last_anim_random_frame = gfx.anim_random_frame;
+    int element_pos;
+
+    if (group->choice_mode == ANIM_RANDOM)
+      gfx.anim_random_frame = RND(group->num_elements_resolved);
+
+    element_pos = getAnimationFrame(group->num_elements_resolved, 1,
+                                   group->choice_mode, 0,
+                                   group->choice_pos);
+
+    if (group->choice_mode == ANIM_RANDOM)
+      gfx.anim_random_frame = last_anim_random_frame;
+
+    group->choice_pos++;
+
+    element = group->element_resolved[element_pos];
+  }
+
+  return element;
+}
+
 static void InitPlayerField(int x, int y, int element, boolean init_game)
 {
   if (element == EL_SP_MURPHY)
@@ -1085,16 +1122,26 @@ static void InitField(int x, int y, boolean init_game)
       }
       break;
 
+#if !USE_BOTH_SWITCHGATE_SWITCHES
     case EL_SWITCHGATE_SWITCH_DOWN:    /* always start with same switch pos */
       if (init_game)
        Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
       break;
+#endif
 
     case EL_LIGHT_SWITCH_ACTIVE:
       if (init_game)
        game.light_time_left = level.time_light * FRAMES_PER_SECOND;
       break;
 
+    case EL_INVISIBLE_STEELWALL:
+    case EL_INVISIBLE_WALL:
+    case EL_INVISIBLE_SAND:
+      if (game.light_time_left > 0 ||
+         game.lenses_time_left > 0)
+        Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
+      break;
+
     case EL_EMC_MAGIC_BALL:
       if (game.ball_state)
        Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
@@ -1114,7 +1161,7 @@ static void InitField(int x, int y, boolean init_game)
 
 #if USE_NEW_CUSTOM_VALUE
        if (!element_info[element].use_last_ce_value || init_game)
-         CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
+         CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
 #endif
       }
 #else
@@ -1123,6 +1170,11 @@ static void InitField(int x, int y, boolean init_game)
 #endif
       else if (IS_GROUP_ELEMENT(element))
       {
+#if 1
+       Feld[x][y] = get_element_from_group_element(element);
+
+       InitField(x, y, init_game);
+#else
        struct ElementGroupInfo *group = element_info[element].group;
        int last_anim_random_frame = gfx.anim_random_frame;
        int element_pos;
@@ -1142,6 +1194,7 @@ static void InitField(int x, int y, boolean init_game)
        Feld[x][y] = group->element_resolved[element_pos];
 
        InitField(x, y, init_game);
+#endif
       }
       break;
   }
@@ -1156,7 +1209,7 @@ static void InitField(int x, int y, boolean init_game)
 #if USE_NEW_CUSTOM_VALUE
 
 #if 1
-  CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
+  CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
 #else
   CustomValue[x][y] = element_info[Feld[x][y]].custom_value_initial;
 #endif
@@ -1199,24 +1252,32 @@ static inline void InitField_WithBug2(int x, int y, boolean init_game)
 
 inline void DrawGameValue_Emeralds(int value)
 {
-  DrawText(DX_EMERALDS, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
+  int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
+
+  DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), FONT_TEXT_2);
 }
 
 inline void DrawGameValue_Dynamite(int value)
 {
-  DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
+  int xpos = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
+
+  DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), FONT_TEXT_2);
 }
 
 inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
 {
+  int base_key_graphic = EL_KEY_1;
   int i;
 
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+    base_key_graphic = EL_EM_KEY_1;
+
   /* currently only 4 of 8 possible keys are displayed */
   for (i = 0; i < STD_NUM_KEYS; i++)
   {
     if (key[i])
       DrawMiniGraphicExt(drawto, DX_KEYS + i * MINI_TILEX, DY_KEYS,
-                        el2edimg(EL_KEY_1 + i));
+                        el2edimg(base_key_graphic + i));
     else
       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
                 DOOR_GFX_PAGEX5 + XX_KEYS + i * MINI_TILEX, YY_KEYS,
@@ -1226,15 +1287,24 @@ inline void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
 
 inline void DrawGameValue_Score(int value)
 {
-  DrawText(DX_SCORE, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
+  int xpos = (5 * 14 - 5 * getFontWidth(FONT_TEXT_2)) / 2;
+
+  DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), FONT_TEXT_2);
 }
 
 inline void DrawGameValue_Time(int value)
 {
+  int xpos3 = (3 * 14 - 3 * getFontWidth(FONT_TEXT_2)) / 2;
+  int xpos4 = (4 * 10 - 4 * getFontWidth(FONT_LEVEL_NUMBER)) / 2;
+
+  /* clear background if value just changed its size */
+  if (value == 999 || value == 1000)
+    ClearRectangle(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
+
   if (value < 1000)
-    DrawText(DX_TIME1, DY_TIME, int2str(value, 3), FONT_TEXT_2);
+    DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), FONT_TEXT_2);
   else
-    DrawText(DX_TIME2, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
+    DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), FONT_LEVEL_NUMBER);
 }
 
 inline void DrawGameValue_Level(int value)
@@ -1282,7 +1352,9 @@ void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
 
 void DrawGameDoorValues()
 {
-  int i;
+  int dynamite_state = 0;
+  int key_bits = 0;
+  int i, j;
 
   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
   {
@@ -1291,6 +1363,7 @@ void DrawGameDoorValues()
     return;
   }
 
+#if 0
   DrawGameValue_Level(level_nr);
 
   DrawGameValue_Emeralds(local_player->gems_still_needed);
@@ -1298,8 +1371,37 @@ void DrawGameDoorValues()
   DrawGameValue_Score(local_player->score);
   DrawGameValue_Time(TimeLeft);
 
-  for (i = 0; i < MAX_PLAYERS; i++)
+#else
+
+  if (game.centered_player_nr == -1)
+  {
+    for (i = 0; i < MAX_PLAYERS; i++)
+    {
+      for (j = 0; j < MAX_NUM_KEYS; j++)
+       if (stored_player[i].key[j])
+         key_bits |= (1 << j);
+
+      dynamite_state += stored_player[i].inventory_size;
+    }
+
+#if 0
     DrawGameValue_Keys(stored_player[i].key);
+#endif
+  }
+  else
+  {
+    int player_nr = game.centered_player_nr;
+
+    for (i = 0; i < MAX_NUM_KEYS; i++)
+      if (stored_player[player_nr].key[i])
+       key_bits |= (1 << i);
+
+    dynamite_state = stored_player[player_nr].inventory_size;
+  }
+
+  DrawAllGameValues(local_player->gems_still_needed, dynamite_state,
+                   local_player->score, TimeLeft, key_bits);
+#endif
 }
 
 #if 0
@@ -1493,8 +1595,9 @@ static void InitGameEngine()
 
 #if 1
   /* dynamically adjust player properties according to level information */
-  game.initial_move_delay_value =
-    get_move_delay_from_stepsize(level.initial_player_stepsize);
+  for (i = 0; i < MAX_PLAYERS; i++)
+    game.initial_move_delay_value[i] =
+      get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
 #else
   /* dynamically adjust player properties according to level information */
   game.initial_move_delay_value =
@@ -1502,8 +1605,10 @@ static void InitGameEngine()
 #endif
 
   /* 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);
+  for (i = 0; i < MAX_PLAYERS; i++)
+    game.initial_move_delay[i] =
+      (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
+       game.initial_move_delay_value[i] : 0);
 
   /* ---------- initialize player's initial push delay --------------------- */
 
@@ -1610,6 +1715,7 @@ static void InitGameEngine()
       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;
+      ei->change_page[j].actual_trigger_ce_score = 0;
     }
   }
 
@@ -1661,8 +1767,22 @@ static void InitGameEngine()
   {
     if (!IS_CUSTOM_ELEMENT(i))
     {
+#if 1
+      /* set default push delay values (corrected since version 3.0.7-1) */
+      if (game.engine_version < VERSION_IDENT(3,0,7,1))
+      {
+       element_info[i].push_delay_fixed = 2;
+       element_info[i].push_delay_random = 8;
+      }
+      else
+      {
+       element_info[i].push_delay_fixed = 8;
+       element_info[i].push_delay_random = 8;
+      }
+#else
       element_info[i].push_delay_fixed  = game.default_push_delay_fixed;
       element_info[i].push_delay_random = game.default_push_delay_random;
+#endif
     }
   }
 
@@ -1863,6 +1983,8 @@ void InitGame()
     player->block_last_field = FALSE;  /* initialized in InitPlayerField() */
     player->block_delay_adjustment = 0;        /* initialized in InitPlayerField() */
 
+    player->gravity = level.initial_player_gravity[i];
+
     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
 
     player->actual_frame_counter = 0;
@@ -1918,7 +2040,7 @@ void InitGame()
     player->show_envelope = 0;
 
 #if 1
-    SetPlayerMoveSpeed(player, level.initial_player_stepsize, TRUE);
+    SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
 #else
     player->move_delay       = game.initial_move_delay;
     player->move_delay_value = game.initial_move_delay_value;
@@ -1984,8 +2106,15 @@ void InitGame()
   game.timegate_time_left = 0;
   game.switchgate_pos = 0;
   game.wind_direction = level.wind_direction_initial;
+
+#if !USE_PLAYER_GRAVITY
+#if 1
+  game.gravity = FALSE;
+#else
   game.gravity = level.initial_gravity;
+#endif
   game.explosions_delayed = TRUE;
+#endif
 
   game.lenses_time_left = 0;
   game.magnify_time_left = 0;
@@ -1995,9 +2124,23 @@ void InitGame()
 
   game.envelope_active = FALSE;
 
-  game.centered_player_nr = game.centered_player_nr_next = -1; /* focus all */
+  /* set focus to local player for network games, else to all players */
+  game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
+  game.centered_player_nr_next = game.centered_player_nr;
   game.set_centered_player = FALSE;
 
+  if (network_playing && tape.recording)
+  {
+    /* store client dependent player focus when recording network games */
+    tape.centered_player_nr_next = game.centered_player_nr_next;
+    tape.set_centered_player = TRUE;
+  }
+
+#if 0
+  printf("::: focus set to player %d [%d]\n",
+        game.centered_player_nr, local_player->index_nr);
+#endif
+
   for (i = 0; i < NUM_BELTS; i++)
   {
     game.belt_dir[i] = MV_NONE;
@@ -2810,7 +2953,7 @@ int NewHiScore()
 
   LoadScore(level_nr);
 
-  if (strcmp(setup.player_name, EMPTY_PLAYER_NAME) == 0 ||
+  if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
       local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score) 
     return -1;
 
@@ -2826,7 +2969,7 @@ int NewHiScore()
 
 #ifdef ONE_PER_NAME
        for (l = k; l < MAX_SCORE_ENTRIES; l++)
-         if (!strcmp(setup.player_name, highscore[l].Name))
+         if (strEqual(setup.player_name, highscore[l].Name))
            m = l;
        if (m == k)     /* player's new highscore overwrites his old one */
          goto put_into_list;
@@ -2910,10 +3053,26 @@ void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
   }
 }
 
-static void ResetRandomAnimationValue(int x, int y)
+#if USE_GFX_RESET_GFX_ANIMATION
+static void ResetGfxFrame(int x, int y, boolean redraw)
 {
-  GfxRandom[x][y] = INIT_GFX_RANDOM();
+  int element = Feld[x][y];
+  int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
+  int last_gfx_frame = GfxFrame[x][y];
+
+  if (graphic_info[graphic].anim_global_sync)
+    GfxFrame[x][y] = FrameCounter;
+  else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
+    GfxFrame[x][y] = CustomValue[x][y];
+  else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
+    GfxFrame[x][y] = element_info[element].collect_score;
+  else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
+    GfxFrame[x][y] = ChangeDelay[x][y];
+
+  if (redraw && GfxFrame[x][y] != last_gfx_frame)
+    DrawLevelGraphicAnimation(x, y, graphic);
 }
+#endif
 
 static void ResetGfxAnimation(int x, int y)
 {
@@ -2921,9 +3080,9 @@ static void ResetGfxAnimation(int x, int y)
   int element, graphic;
 #endif
 
-  GfxFrame[x][y] = 0;
   GfxAction[x][y] = ACTION_DEFAULT;
   GfxDir[x][y] = MovDir[x][y];
+  GfxFrame[x][y] = 0;
 
 #if 0
   element = Feld[x][y];
@@ -2935,9 +3094,20 @@ static void ResetGfxAnimation(int x, int y)
     GfxFrame[x][y] = CustomValue[x][y];
   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
     GfxFrame[x][y] = element_info[element].collect_score;
+  else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
+    GfxFrame[x][y] = ChangeDelay[x][y];
+#endif
+
+#if USE_GFX_RESET_GFX_ANIMATION
+  ResetGfxFrame(x, y, FALSE);
 #endif
 }
 
+static void ResetRandomAnimationValue(int x, int y)
+{
+  GfxRandom[x][y] = INIT_GFX_RANDOM();
+}
+
 void InitMovingField(int x, int y, int direction)
 {
   int element = Feld[x][y];
@@ -2966,6 +3136,8 @@ void InitMovingField(int x, int y, int direction)
     GfxFrame[x][y] = CustomValue[x][y];
   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
     GfxFrame[x][y] = element_info[element].collect_score;
+  else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
+    GfxFrame[x][y] = ChangeDelay[x][y];
 #endif
 
   /* this is needed for CEs with property "can move" / "not moving" */
@@ -4284,12 +4456,25 @@ static void ToggleSwitchgateSwitch(int x, int y)
   {
     int element = Feld[xx][yy];
 
+#if !USE_BOTH_SWITCHGATE_SWITCHES
     if (element == EL_SWITCHGATE_SWITCH_UP ||
        element == EL_SWITCHGATE_SWITCH_DOWN)
     {
       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
       DrawLevelField(xx, yy);
     }
+#else
+    if (element == EL_SWITCHGATE_SWITCH_UP)
+    {
+      Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
+      DrawLevelField(xx, yy);
+    }
+    else if (element == EL_SWITCHGATE_SWITCH_DOWN)
+    {
+      Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
+      DrawLevelField(xx, yy);
+    }
+#endif
     else if (element == EL_SWITCHGATE_OPEN ||
             element == EL_SWITCHGATE_OPENING)
     {
@@ -5595,7 +5780,7 @@ inline static void TurnRoundExt(int x, int y)
 static void TurnRound(int x, int y)
 {
   int direction = MovDir[x][y];
-#if 1
+#if 0
   int element, graphic;
 #endif
 
@@ -5610,6 +5795,8 @@ static void TurnRound(int x, int y)
     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
 
 #if 1
+  ResetGfxFrame(x, y, FALSE);
+#else
   element = Feld[x][y];
   graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
 
@@ -5619,6 +5806,8 @@ static void TurnRound(int x, int y)
     GfxFrame[x][y] = CustomValue[x][y];
   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
     GfxFrame[x][y] = element_info[element].collect_score;
+  else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
+    GfxFrame[x][y] = ChangeDelay[x][y];
 #endif
 }
 
@@ -7794,6 +7983,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
 {
   struct ElementInfo *ei = &element_info[element];
   struct ElementChangeInfo *change = &ei->change_page[page];
+  int target_element = change->target_element;
   int action_type = change->action_type;
   int action_mode = change->action_mode;
   int action_arg = change->action_arg;
@@ -7832,21 +8022,21 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
      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 :
+     action_type == CA_SET_CE_SCORE ? 9999 :
      CA_ARG_MAX);
 
   int action_arg_number_reset =
-    (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize :
+    (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
      action_type == CA_SET_LEVEL_TIME ? level.time :
      action_type == CA_SET_LEVEL_SCORE ? 0 :
-     action_type == CA_SET_CE_SCORE ? 0 :
 #if 1
-     action_type == CA_SET_CE_VALUE ? GET_NEW_CUSTOM_VALUE(element) :
+     action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
 #else
      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
 #endif
+     action_type == CA_SET_CE_SCORE ? 0 :
      0);
 
   int action_arg_number =
@@ -7857,18 +8047,20 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
      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 :
 #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_SCORE ? ei->collect_score :
      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(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_CV_TARGET ? GET_NEW_CUSTOM_VALUE(change->target_element) :
+     action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
+     action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
+     action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
      -1);
@@ -7877,8 +8069,8 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
     (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] :
+     action_type == CA_SET_CE_SCORE ? ei->collect_score :
      0);
 
   int action_arg_number_new =
@@ -7961,6 +8153,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       break;
     }
 
+#if !USE_PLAYER_GRAVITY
     case CA_SET_LEVEL_GRAVITY:
     {
       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
@@ -7969,6 +8162,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
                      game.gravity);
       break;
     }
+#endif
 
     case CA_SET_LEVEL_WIND:
     {
@@ -8021,7 +8215,11 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
          {
            stored_player[i].key[KEY_NR(element)] = key_state;
 
+#if 1
+           DrawGameDoorValues();
+#else
            DrawGameValue_Keys(stored_player[i].key);
+#endif
 
            redraw_mask |= REDRAW_DOOR_1;
          }
@@ -8051,6 +8249,10 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
            action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
                           CA_MODE_MULTIPLY);
          }
+         else if (action_arg == CA_ARG_NUMBER_RESET)
+         {
+           action_arg_number = level.initial_player_stepsize[i];
+         }
 
          move_stepsize =
            getModifiedActionNumber(move_stepsize,
@@ -8103,6 +8305,25 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       break;
     }
 
+#if USE_PLAYER_GRAVITY
+    case CA_SET_PLAYER_GRAVITY:
+    {
+      for (i = 0; i < MAX_PLAYERS; i++)
+      {
+       if (trigger_player_bits & (1 << i))
+       {
+         stored_player[i].gravity =
+           (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
+            action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
+            action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
+            stored_player[i].gravity);
+       }
+      }
+
+      break;
+    }
+#endif
+
     case CA_SET_PLAYER_ARTWORK:
     {
       for (i = 0; i < MAX_PLAYERS; i++)
@@ -8135,37 +8356,73 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
 
     /* ---------- CE actions  ---------------------------------------------- */
 
-    case CA_SET_CE_SCORE:
+    case CA_SET_CE_VALUE:
     {
-      ei->collect_score = action_arg_number_new;
+#if USE_NEW_CUSTOM_VALUE
+      int last_ce_value = CustomValue[x][y];
+
+      CustomValue[x][y] = action_arg_number_new;
+
+#if 0
+      printf("::: CE value == %d\n", CustomValue[x][y]);
+#endif
+
+      if (CustomValue[x][y] != last_ce_value)
+      {
+       CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
+       CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
+
+       if (CustomValue[x][y] == 0)
+       {
+#if 0
+         printf("::: CE_VALUE_GETS_ZERO\n");
+#endif
+
+         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
+         CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
+
+#if 0
+         printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
+#endif
+       }
+      }
+
+#endif
 
       break;
     }
 
-    case CA_SET_CE_VALUE:
+    case CA_SET_CE_SCORE:
     {
 #if USE_NEW_CUSTOM_VALUE
-      int last_custom_value = CustomValue[x][y];
+      int last_ce_score = ei->collect_score;
 
-      CustomValue[x][y] = action_arg_number_new;
+      ei->collect_score = action_arg_number_new;
 
 #if 0
-      printf("::: Count == %d\n", CustomValue[x][y]);
+      printf("::: CE score == %d\n", ei->collect_score);
 #endif
 
-      if (CustomValue[x][y] == 0 && last_custom_value > 0)
+      if (ei->collect_score != last_ce_score)
       {
+       CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
+       CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
+
+       if (ei->collect_score == 0)
+       {
 #if 0
-       printf("::: CE_VALUE_GETS_ZERO\n");
+         printf("::: CE_SCORE_GETS_ZERO\n");
 #endif
 
-       CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
-       CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
+         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
+         CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
 
 #if 0
-       printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
+         printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
 #endif
+       }
       }
+
 #endif
 
       break;
@@ -8187,54 +8444,82 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
 
 static void CreateFieldExt(int x, int y, int element, boolean is_change)
 {
+  int old_element = Feld[x][y];
+  int new_element = get_element_from_group_element(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(element) &&
-                       IS_WALKABLE(Feld[x][y]));
+  boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
+  boolean add_player_onto_element = (new_element_is_player &&
+                                    new_element != EL_SOKOBAN_FIELD_PLAYER &&
+                                    IS_WALKABLE(old_element));
 
-  /* check if element under player changes from accessible to unaccessible
+#if 0
+  /* check if element under the player changes from accessible to unaccessible
      (needed for special case of dropping element which then changes) */
   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
-      IS_ACCESSIBLE(Feld[x][y]) && !IS_ACCESSIBLE(element))
+      IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
   {
     Bang(x, y);
 
     return;
   }
+#endif
 
-  if (!add_player)
+  if (!add_player_onto_element)
   {
     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
       RemoveMovingField(x, y);
     else
       RemoveField(x, y);
 
-    Feld[x][y] = element;
+    Feld[x][y] = new_element;
 
+#if !USE_GFX_RESET_GFX_ANIMATION
     ResetGfxAnimation(x, y);
     ResetRandomAnimationValue(x, y);
+#endif
 
-    if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
+    if (element_info[new_element].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)
+    if (element_info[new_element].use_last_ce_value)
       CustomValue[x][y] = last_ce_value;
 #endif
 
     InitField_WithBug1(x, y, FALSE);
 
+    new_element = Feld[x][y];  /* element may have changed */
+
+#if USE_GFX_RESET_GFX_ANIMATION
+    ResetGfxAnimation(x, y);
+    ResetRandomAnimationValue(x, y);
+#endif
+
     DrawLevelField(x, y);
 
-    if (GFX_CRUMBLED(Feld[x][y]))
+    if (GFX_CRUMBLED(new_element))
       DrawLevelFieldCrumbledSandNeighbours(x, y);
   }
 
+#if 1
+  /* check if element under the player changes from accessible to unaccessible
+     (needed for special case of dropping element which then changes) */
+  /* (must be checked after creating new element for walkable group elements) */
+  if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
+      IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
+  {
+    Bang(x, y);
+
+    return;
+  }
+#endif
+
   /* "ChangeCount" not set yet to allow "entered by player" change one time */
-  if (ELEM_IS_PLAYER(element))
-    RelocatePlayer(x, y, element);
+  if (new_element_is_player)
+    RelocatePlayer(x, y, new_element);
 
   if (is_change)
     ChangeCount[x][y]++;       /* count number of changes in the same frame */
@@ -8271,7 +8556,10 @@ static void CreateElementFromChange(int x, int y, int element)
 
 static boolean ChangeElement(int x, int y, int element, int page)
 {
-  struct ElementChangeInfo *change = &element_info[element].change_page[page];
+  struct ElementInfo *ei = &element_info[element];
+  struct ElementChangeInfo *change = &ei->change_page[page];
+  int ce_value = CustomValue[x][y];
+  int ce_score = ei->collect_score;
   int target_element;
   int old_element = Feld[x][y];
 
@@ -8286,6 +8574,7 @@ static boolean ChangeElement(int x, int y, int element, int page)
     change->actual_trigger_player = EL_PLAYER_1;
     change->actual_trigger_side = CH_SIDE_NONE;
     change->actual_trigger_ce_value = 0;
+    change->actual_trigger_ce_score = 0;
   }
 
   /* do not change elements more than a specified maximum number of changes */
@@ -8390,7 +8679,8 @@ static boolean ChangeElement(int x, int y, int element, int page)
          ChangeEvent[ex][ey] = ChangeEvent[x][y];
 
          content_element = change->target_content.e[xx][yy];
-         target_element = GET_TARGET_ELEMENT(content_element, change);
+         target_element = GET_TARGET_ELEMENT(content_element, change,
+                                             ce_value, ce_score);
 
          CreateElementFromChange(ex, ey, target_element);
 
@@ -8411,7 +8701,8 @@ static boolean ChangeElement(int x, int y, int element, int page)
   }
   else
   {
-    target_element = GET_TARGET_ELEMENT(change->target_element, change);
+    target_element = GET_TARGET_ELEMENT(change->target_element, change,
+                                       ce_value, ce_score);
 
     if (element == EL_DIAGONAL_GROWING ||
        element == EL_DIAGONAL_SHRINKING)
@@ -8645,6 +8936,7 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
        change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
        change->actual_trigger_side = trigger_side;
        change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
+       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
 
        if ((change->can_change && !change_done) || change->has_action)
        {
@@ -8749,6 +9041,7 @@ static boolean CheckElementChangeExt(int x, int y,
       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];
+      change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
 
       /* special case: trigger element not at (x,y) position for some events */
       if (check_trigger_element)
@@ -8771,6 +9064,7 @@ static boolean CheckElementChangeExt(int x, int y,
        int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
 
        change->actual_trigger_ce_value = CustomValue[xx][yy];
+       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
       }
 
       if (change->can_change && !change_done)
@@ -9128,6 +9422,11 @@ void AdvanceFrameAndPlayerCounters(int player_nr)
 {
   int i;
 
+#if 0
+  Error(ERR_NETWORK_CLIENT, "advancing frame counter from %d to %d",
+       FrameCounter, FrameCounter + 1);
+#endif
+
   /* advance frame counters (global frame counter and time frame counter) */
   FrameCounter++;
   TimeFrames++;
@@ -9178,15 +9477,10 @@ void AdvanceFrameAndPlayerCounters(int player_nr)
 void StartGameActions(boolean init_network_game, boolean record_tape,
                      long random_seed)
 {
-#if 1
   unsigned long new_random_seed = InitRND(random_seed);
 
   if (record_tape)
     TapeStartRecording(new_random_seed);
-#else
-  if (record_tape)
-    TapeStartRecording(random_seed);
-#endif
 
 #if defined(NETWORK_AVALIABLE)
   if (init_network_game)
@@ -9201,10 +9495,6 @@ void StartGameActions(boolean init_network_game, boolean record_tape,
 
   game_status = GAME_MODE_PLAYING;
 
-#if 0
-  InitRND(random_seed);
-#endif
-
   InitGame();
 }
 
@@ -9271,11 +9561,19 @@ void GameActions()
 
     if (!network_player_action_received)
       return;          /* failed to get network player actions in time */
+
+    /* do not yet reset "network_player_action_received" (for tape.pausing) */
   }
 
   if (tape.pausing)
     return;
 
+  /* at this point we know that we really continue executing the game */
+
+#if 1
+  network_player_action_received = FALSE;
+#endif
+
   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
 
   if (tape.set_centered_player)
@@ -9404,10 +9702,11 @@ void GameActions_RND()
       sy = stored_player[game.centered_player_nr_next].jy;
     }
 
-    DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
-
     game.centered_player_nr = game.centered_player_nr_next;
     game.set_centered_player = FALSE;
+
+    DrawRelocateScreen(sx, sy, MV_NONE, TRUE, setup.quick_switch);
+    DrawGameDoorValues();
   }
 #endif
 
@@ -9442,7 +9741,9 @@ void GameActions_RND()
     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
   }
 
+#if 0
   network_player_action_received = FALSE;
+#endif
 
   ScrollScreen(NULL, SCROLL_GO_ON);
 
@@ -9570,6 +9871,9 @@ void GameActions_RND()
       printf("::: Yo man! Rocks can fall!\n");
 #endif
 
+#if 1
+    ResetGfxFrame(x, y, TRUE);
+#else
     if (graphic_info[graphic].anim_global_sync)
       GfxFrame[x][y] = FrameCounter;
     else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
@@ -9594,6 +9898,18 @@ void GameActions_RND()
 #endif
        DrawLevelGraphicAnimation(x, y, graphic);
     }
+    else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
+    {
+      int old_gfx_frame = GfxFrame[x][y];
+
+      GfxFrame[x][y] = ChangeDelay[x][y];
+
+#if 1
+      if (GfxFrame[x][y] != old_gfx_frame)
+#endif
+       DrawLevelGraphicAnimation(x, y, graphic);
+    }
+#endif
 
     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
        IS_NEXT_FRAME(GfxFrame[x][y], graphic))
@@ -10040,7 +10356,11 @@ static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
 
 static void CheckGravityMovement(struct PlayerInfo *player)
 {
+#if USE_PLAYER_GRAVITY
+  if (player->gravity && !player->programmed_action)
+#else
   if (game.gravity && !player->programmed_action)
+#endif
   {
     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
@@ -10062,7 +10382,11 @@ static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
 {
   return CheckGravityMovement(player);
 
+#if USE_PLAYER_GRAVITY
+  if (player->gravity && !player->programmed_action)
+#else
   if (game.gravity && !player->programmed_action)
+#endif
   {
     int jx = player->jx, jy = player->jy;
     boolean field_under_player_is_free =
@@ -10445,8 +10769,13 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
        last_field_block_delay += player->move_delay_value;
 
        /* when blocking enabled, prevent moving up despite gravity */
+#if USE_PLAYER_GRAVITY
+       if (player->gravity && player->MovDir == MV_UP)
+         block_delay_adjustment = -1;
+#else
        if (game.gravity && player->MovDir == MV_UP)
          block_delay_adjustment = -1;
+#endif
       }
 
       /* add block delay adjustment (also possible when not blocking) */
@@ -11368,6 +11697,8 @@ int DigField(struct PlayerInfo *player,
           game.engine_version >= VERSION_IDENT(2,2,0,0))
     old_element = Back[jx][jy];
 
+  /* checking here causes player to move into acid even if the current field
+     cannot be left to that direction */
 #if 0
 #if USE_FIXED_DONT_RUN_INTO
   if (player_can_move && DONT_RUN_INTO(element))
@@ -11389,6 +11720,43 @@ int DigField(struct PlayerInfo *player,
 #endif
 #endif
 
+#if 1  /* ------------------------------ NEW ------------------------------ */
+
+  if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
+    return MP_NO_ACTION;       /* field has no opening in this direction */
+
+  if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
+    return MP_NO_ACTION;       /* field has no opening in this direction */
+
+#if USE_FIXED_DONT_RUN_INTO
+  if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
+  {
+    SplashAcid(x, y);
+#if 1
+    Feld[jx][jy] = player->artwork_element;
+#else
+    Feld[jx][jy] = EL_PLAYER_1;
+#endif
+    InitMovingField(jx, jy, MV_DOWN);
+    Store[jx][jy] = EL_ACID;
+    ContinueMoving(jx, jy);
+    BuryPlayer(player);
+
+    return MP_DONT_RUN_INTO;
+  }
+#endif
+
+#if USE_FIXED_DONT_RUN_INTO
+  if (player_can_move && DONT_RUN_INTO(element))
+  {
+    TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
+
+    return MP_DONT_RUN_INTO;
+  }
+#endif
+
+#else  /* ------------------------------ OLD ------------------------------ */
+
 #if 1
 #if USE_FIXED_DONT_RUN_INTO
   if (player_can_move && DONT_RUN_INTO(element))
@@ -11406,6 +11774,7 @@ int DigField(struct PlayerInfo *player,
   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
     return MP_NO_ACTION;       /* field has no opening in this direction */
 
+  /* checking here causes player to explode when moving into acid */
 #if 1
 #if USE_FIXED_DONT_RUN_INTO
   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
@@ -11422,6 +11791,8 @@ int DigField(struct PlayerInfo *player,
 #endif
 #endif
 
+#endif /* ------------------------------ END ------------------------------ */
+
 #if 0
 #if USE_FIXED_DONT_RUN_INTO
   if (player_can_move && DONT_RUN_INTO(element))
@@ -11474,10 +11845,17 @@ int DigField(struct PlayerInfo *player,
     return MP_NO_ACTION;
   }
 
+#if USE_PLAYER_GRAVITY
+  if (player->gravity && is_player && !player->is_auto_moving &&
+      canFallDown(player) && move_direction != MV_DOWN &&
+      !canMoveToValidFieldWithGravity(jx, jy, move_direction))
+    return MP_NO_ACTION;       /* player cannot walk here due to gravity */
+#else
   if (game.gravity && is_player && !player->is_auto_moving &&
       canFallDown(player) && move_direction != MV_DOWN &&
       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
     return MP_NO_ACTION;       /* player cannot walk here due to gravity */
+#endif
 
   if (player_can_move &&
       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
@@ -11541,23 +11919,50 @@ int DigField(struct PlayerInfo *player,
       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
        return MP_NO_ACTION;
     }
+    else if (IS_EMC_GATE(element))
+    {
+      if (!player->key[EMC_GATE_NR(element)])
+       return MP_NO_ACTION;
+    }
+    else if (IS_EMC_GATE_GRAY(element))
+    {
+      if (!player->key[EMC_GATE_GRAY_NR(element)])
+       return MP_NO_ACTION;
+    }
+    else if (IS_EMC_GATE_GRAY_ACTIVE(element))
+    {
+      if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
+       return MP_NO_ACTION;
+    }
     else if (IS_SP_PORT(element))
     {
       if (element == EL_SP_GRAVITY_PORT_LEFT ||
          element == EL_SP_GRAVITY_PORT_RIGHT ||
          element == EL_SP_GRAVITY_PORT_UP ||
          element == EL_SP_GRAVITY_PORT_DOWN)
+#if USE_PLAYER_GRAVITY
+       player->gravity = !player->gravity;
+#else
        game.gravity = !game.gravity;
+#endif
       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
               element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
               element == EL_SP_GRAVITY_ON_PORT_UP ||
               element == EL_SP_GRAVITY_ON_PORT_DOWN)
+#if USE_PLAYER_GRAVITY
+       player->gravity = TRUE;
+#else
        game.gravity = TRUE;
+#endif
       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
               element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
               element == EL_SP_GRAVITY_OFF_PORT_UP ||
               element == EL_SP_GRAVITY_OFF_PORT_DOWN)
+#if USE_PLAYER_GRAVITY
+       player->gravity = FALSE;
+#else
        game.gravity = FALSE;
+#endif
     }
 
     /* automatically move to the next field with double speed */
@@ -11634,7 +12039,11 @@ int DigField(struct PlayerInfo *player,
       if (player->inventory_size < MAX_INVENTORY_SIZE)
        player->inventory_element[player->inventory_size++] = element;
 
+#if 1
+      DrawGameDoorValues();
+#else
       DrawGameValue_Dynamite(local_player->inventory_size);
+#endif
     }
     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
     {
@@ -11653,7 +12062,11 @@ int DigField(struct PlayerInfo *player,
     {
       player->key[KEY_NR(element)] = TRUE;
 
+#if 1
+      DrawGameDoorValues();
+#else
       DrawGameValue_Keys(player->key);
+#endif
 
       redraw_mask |= REDRAW_DOOR_1;
     }
@@ -11685,7 +12098,11 @@ int DigField(struct PlayerInfo *player,
          if (player->inventory_size < MAX_INVENTORY_SIZE)
            player->inventory_element[player->inventory_size++] = element;
 
+#if 1
+      DrawGameDoorValues();
+#else
       DrawGameValue_Dynamite(local_player->inventory_size);
+#endif
     }
     else if (collect_count > 0)
     {
@@ -12170,7 +12587,11 @@ boolean DropElement(struct PlayerInfo *player)
     {
       player->inventory_size--;
 
+#if 1
+      DrawGameDoorValues();
+#else
       DrawGameValue_Dynamite(local_player->inventory_size);
+#endif
 
       if (new_element == EL_DYNAMITE)
        new_element = EL_DYNAMITE_ACTIVE;
@@ -12616,7 +13037,7 @@ void RequestQuitGame(boolean ask_if_really_quit)
   {
 #if defined(NETWORK_AVALIABLE)
     if (options.network)
-      SendToServer_StopPlaying();
+      SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
     else
 #endif
     {
@@ -12793,7 +13214,10 @@ static void HandleGameButtons(struct GadgetInfo *gi)
   switch (id)
   {
     case GAME_CTRL_ID_STOP:
-      RequestQuitGame(TRUE);
+      if (tape.playing)
+       TapeStop();
+      else
+       RequestQuitGame(TRUE);
       break;
 
     case GAME_CTRL_ID_PAUSE:
@@ -12820,7 +13244,7 @@ static void HandleGameButtons(struct GadgetInfo *gi)
 #endif
        {
          tape.pausing = FALSE;
-         DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF,0);
+         DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
        }
       }
       break;