rnd-20071018-1-src
[rocksndiamonds.git] / src / game.c
index f7cb0fe154c970a3e8484c50516e9c303abfc869..3992e53333fc029fb257897c3ecc5587152c1ab3 100644 (file)
 #define GAME_PANEL_KEY_WHITE                   27
 #define GAME_PANEL_KEY_WHITE_COUNT             28
 #define GAME_PANEL_SCORE                       29
-#define GAME_PANEL_TIME                                30
-#define GAME_PANEL_TIME_HH                     31
-#define GAME_PANEL_TIME_MM                     32
-#define GAME_PANEL_TIME_SS                     33
-#define GAME_PANEL_SHIELD_NORMAL               34
-#define GAME_PANEL_SHIELD_NORMAL_TIME          35
-#define GAME_PANEL_SHIELD_DEADLY               36
-#define GAME_PANEL_SHIELD_DEADLY_TIME          37
-#define GAME_PANEL_EXIT                                38
-#define GAME_PANEL_EMC_MAGIC_BALL              39
-#define GAME_PANEL_EMC_MAGIC_BALL_SWITCH       40
-#define GAME_PANEL_LIGHT_SWITCH                        41
-#define GAME_PANEL_LIGHT_SWITCH_TIME           42
-#define GAME_PANEL_TIMEGATE_SWITCH             43
-#define GAME_PANEL_TIMEGATE_SWITCH_TIME                44
-#define GAME_PANEL_SWITCHGATE_SWITCH           45
-#define GAME_PANEL_EMC_LENSES                  46
-#define GAME_PANEL_EMC_LENSES_TIME             47
-#define GAME_PANEL_EMC_MAGNIFIER               48
-#define GAME_PANEL_EMC_MAGNIFIER_TIME          49
-#define GAME_PANEL_BALLOON_SWITCH              50
-#define GAME_PANEL_DYNABOMB_NUMBER             51
-#define GAME_PANEL_DYNABOMB_SIZE               52
-#define GAME_PANEL_DYNABOMB_POWER              53
-#define GAME_PANEL_PENGUINS                    54
-#define GAME_PANEL_SOKOBAN_OBJECTS             55
-#define GAME_PANEL_SOKOBAN_FIELDS              56
-#define GAME_PANEL_ROBOT_WHEEL                 57
-#define GAME_PANEL_CONVEYOR_BELT_1             58
-#define GAME_PANEL_CONVEYOR_BELT_2             59
-#define GAME_PANEL_CONVEYOR_BELT_3             60
-#define GAME_PANEL_CONVEYOR_BELT_4             61
-#define GAME_PANEL_CONVEYOR_BELT_1_SWITCH      62
-#define GAME_PANEL_CONVEYOR_BELT_2_SWITCH      63
-#define GAME_PANEL_CONVEYOR_BELT_3_SWITCH      64
-#define GAME_PANEL_CONVEYOR_BELT_4_SWITCH      65
-#define GAME_PANEL_MAGIC_WALL                  66
-#define GAME_PANEL_MAGIC_WALL_TIME             67
-#define GAME_PANEL_GRAVITY_STATE               68
-#define GAME_PANEL_CE_SCORE_1                  69
-#define GAME_PANEL_CE_SCORE_2                  70
-#define GAME_PANEL_CE_SCORE_3                  71
-#define GAME_PANEL_CE_SCORE_4                  72
-#define GAME_PANEL_CE_SCORE_5                  73
-#define GAME_PANEL_CE_SCORE_6                  74
-#define GAME_PANEL_CE_SCORE_7                  75
-#define GAME_PANEL_CE_SCORE_8                  76
-#define GAME_PANEL_CE_SCORE_1_ELEMENT          77
-#define GAME_PANEL_CE_SCORE_2_ELEMENT          78
-#define GAME_PANEL_CE_SCORE_3_ELEMENT          79
-#define GAME_PANEL_CE_SCORE_4_ELEMENT          80
-#define GAME_PANEL_CE_SCORE_5_ELEMENT          81
-#define GAME_PANEL_CE_SCORE_6_ELEMENT          82
-#define GAME_PANEL_CE_SCORE_7_ELEMENT          83
-#define GAME_PANEL_CE_SCORE_8_ELEMENT          84
-#define GAME_PANEL_PLAYER_NAME                 85
-#define GAME_PANEL_LEVEL_NAME                  86
-#define GAME_PANEL_LEVEL_AUTHOR                        87
-
-#define NUM_GAME_PANEL_CONTROLS                        88
+#define GAME_PANEL_HIGHSCORE                   30
+#define GAME_PANEL_TIME                                31
+#define GAME_PANEL_TIME_HH                     32
+#define GAME_PANEL_TIME_MM                     33
+#define GAME_PANEL_TIME_SS                     34
+#define GAME_PANEL_SHIELD_NORMAL               35
+#define GAME_PANEL_SHIELD_NORMAL_TIME          36
+#define GAME_PANEL_SHIELD_DEADLY               37
+#define GAME_PANEL_SHIELD_DEADLY_TIME          38
+#define GAME_PANEL_EXIT                                39
+#define GAME_PANEL_EMC_MAGIC_BALL              40
+#define GAME_PANEL_EMC_MAGIC_BALL_SWITCH       41
+#define GAME_PANEL_LIGHT_SWITCH                        42
+#define GAME_PANEL_LIGHT_SWITCH_TIME           43
+#define GAME_PANEL_TIMEGATE_SWITCH             44
+#define GAME_PANEL_TIMEGATE_SWITCH_TIME                45
+#define GAME_PANEL_SWITCHGATE_SWITCH           46
+#define GAME_PANEL_EMC_LENSES                  47
+#define GAME_PANEL_EMC_LENSES_TIME             48
+#define GAME_PANEL_EMC_MAGNIFIER               49
+#define GAME_PANEL_EMC_MAGNIFIER_TIME          50
+#define GAME_PANEL_BALLOON_SWITCH              51
+#define GAME_PANEL_DYNABOMB_NUMBER             52
+#define GAME_PANEL_DYNABOMB_SIZE               53
+#define GAME_PANEL_DYNABOMB_POWER              54
+#define GAME_PANEL_PENGUINS                    55
+#define GAME_PANEL_SOKOBAN_OBJECTS             56
+#define GAME_PANEL_SOKOBAN_FIELDS              57
+#define GAME_PANEL_ROBOT_WHEEL                 58
+#define GAME_PANEL_CONVEYOR_BELT_1             59
+#define GAME_PANEL_CONVEYOR_BELT_2             60
+#define GAME_PANEL_CONVEYOR_BELT_3             61
+#define GAME_PANEL_CONVEYOR_BELT_4             62
+#define GAME_PANEL_CONVEYOR_BELT_1_SWITCH      63
+#define GAME_PANEL_CONVEYOR_BELT_2_SWITCH      64
+#define GAME_PANEL_CONVEYOR_BELT_3_SWITCH      65
+#define GAME_PANEL_CONVEYOR_BELT_4_SWITCH      66
+#define GAME_PANEL_MAGIC_WALL                  67
+#define GAME_PANEL_MAGIC_WALL_TIME             68
+#define GAME_PANEL_GRAVITY_STATE               69
+#define GAME_PANEL_GRAPHIC_1                   70
+#define GAME_PANEL_GRAPHIC_2                   71
+#define GAME_PANEL_GRAPHIC_3                   72
+#define GAME_PANEL_GRAPHIC_4                   73
+#define GAME_PANEL_GRAPHIC_5                   74
+#define GAME_PANEL_GRAPHIC_6                   75
+#define GAME_PANEL_GRAPHIC_7                   76
+#define GAME_PANEL_GRAPHIC_8                   77
+#define GAME_PANEL_ELEMENT_1                   78
+#define GAME_PANEL_ELEMENT_2                   79
+#define GAME_PANEL_ELEMENT_3                   80
+#define GAME_PANEL_ELEMENT_4                   81
+#define GAME_PANEL_ELEMENT_5                   82
+#define GAME_PANEL_ELEMENT_6                   83
+#define GAME_PANEL_ELEMENT_7                   84
+#define GAME_PANEL_ELEMENT_8                   85
+#define GAME_PANEL_ELEMENT_COUNT_1             86
+#define GAME_PANEL_ELEMENT_COUNT_2             87
+#define GAME_PANEL_ELEMENT_COUNT_3             88
+#define GAME_PANEL_ELEMENT_COUNT_4             89
+#define GAME_PANEL_ELEMENT_COUNT_5             90
+#define GAME_PANEL_ELEMENT_COUNT_6             91
+#define GAME_PANEL_ELEMENT_COUNT_7             92
+#define GAME_PANEL_ELEMENT_COUNT_8             93
+#define GAME_PANEL_CE_SCORE_1                  94
+#define GAME_PANEL_CE_SCORE_2                  95
+#define GAME_PANEL_CE_SCORE_3                  96
+#define GAME_PANEL_CE_SCORE_4                  97
+#define GAME_PANEL_CE_SCORE_5                  98
+#define GAME_PANEL_CE_SCORE_6                  99
+#define GAME_PANEL_CE_SCORE_7                  100
+#define GAME_PANEL_CE_SCORE_8                  101
+#define GAME_PANEL_CE_SCORE_1_ELEMENT          102
+#define GAME_PANEL_CE_SCORE_2_ELEMENT          103
+#define GAME_PANEL_CE_SCORE_3_ELEMENT          104
+#define GAME_PANEL_CE_SCORE_4_ELEMENT          105
+#define GAME_PANEL_CE_SCORE_5_ELEMENT          106
+#define GAME_PANEL_CE_SCORE_6_ELEMENT          107
+#define GAME_PANEL_CE_SCORE_7_ELEMENT          108
+#define GAME_PANEL_CE_SCORE_8_ELEMENT          109
+#define GAME_PANEL_PLAYER_NAME                 110
+#define GAME_PANEL_LEVEL_NAME                  111
+#define GAME_PANEL_LEVEL_AUTHOR                        112
+
+#define NUM_GAME_PANEL_CONTROLS                        113
+
+struct GamePanelOrderInfo
+{
+  int nr;
+  int sort_priority;
+};
+
+static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
 
 struct GamePanelControlInfo
 {
@@ -232,6 +265,7 @@ struct GamePanelControlInfo
   int value, last_value;
   int frame, last_frame;
   int gfx_frame;
+  int gfx_random;
 };
 
 static struct GamePanelControlInfo game_panel_controls[] =
@@ -386,6 +420,11 @@ static struct GamePanelControlInfo game_panel_controls[] =
     &game.panel.score,
     TYPE_INTEGER,
   },
+  {
+    GAME_PANEL_HIGHSCORE,
+    &game.panel.highscore,
+    TYPE_INTEGER,
+  },
   {
     GAME_PANEL_TIME,
     &game.panel.time,
@@ -581,6 +620,126 @@ static struct GamePanelControlInfo game_panel_controls[] =
     &game.panel.gravity_state,
     TYPE_STRING,
   },
+  {
+    GAME_PANEL_GRAPHIC_1,
+    &game.panel.graphic[0],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_GRAPHIC_2,
+    &game.panel.graphic[1],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_GRAPHIC_3,
+    &game.panel.graphic[2],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_GRAPHIC_4,
+    &game.panel.graphic[3],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_GRAPHIC_5,
+    &game.panel.graphic[4],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_GRAPHIC_6,
+    &game.panel.graphic[5],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_GRAPHIC_7,
+    &game.panel.graphic[6],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_GRAPHIC_8,
+    &game.panel.graphic[7],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_ELEMENT_1,
+    &game.panel.element[0],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_ELEMENT_2,
+    &game.panel.element[1],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_ELEMENT_3,
+    &game.panel.element[2],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_ELEMENT_4,
+    &game.panel.element[3],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_ELEMENT_5,
+    &game.panel.element[4],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_ELEMENT_6,
+    &game.panel.element[5],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_ELEMENT_7,
+    &game.panel.element[6],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_ELEMENT_8,
+    &game.panel.element[7],
+    TYPE_ELEMENT,
+  },
+  {
+    GAME_PANEL_ELEMENT_COUNT_1,
+    &game.panel.element_count[0],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_ELEMENT_COUNT_2,
+    &game.panel.element_count[1],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_ELEMENT_COUNT_3,
+    &game.panel.element_count[2],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_ELEMENT_COUNT_4,
+    &game.panel.element_count[3],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_ELEMENT_COUNT_5,
+    &game.panel.element_count[4],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_ELEMENT_COUNT_6,
+    &game.panel.element_count[5],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_ELEMENT_COUNT_7,
+    &game.panel.element_count[6],
+    TYPE_INTEGER,
+  },
+  {
+    GAME_PANEL_ELEMENT_COUNT_8,
+    &game.panel.element_count[7],
+    TYPE_INTEGER,
+  },
   {
     GAME_PANEL_CE_SCORE_1,
     &game.panel.ce_score[0],
@@ -743,6 +902,9 @@ static struct GamePanelControlInfo game_panel_controls[] =
         (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
         (be) + (e) - EL_SELF)
 
+#define GET_PLAYER_FROM_BITS(p)                                                \
+       (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
+
 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                          \
        ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
         (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
@@ -861,6 +1023,8 @@ static struct GamePanelControlInfo game_panel_controls[] =
 
 static void CreateField(int, int, int);
 
+static void ResetGfxAnimation(int, int);
+
 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
 static void AdvanceFrameAndPlayerCounters(int);
 
@@ -1892,6 +2056,20 @@ static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
   }
 }
 
+static int compareGamePanelOrderInfo(const void *object1, const void *object2)
+{
+  const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
+  const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
+  int compare_result;
+
+  if (gpo1->sort_priority != gpo2->sort_priority)
+    compare_result = gpo1->sort_priority - gpo2->sort_priority;
+  else
+    compare_result = gpo1->nr - gpo2->nr;
+
+  return compare_result;
+}
+
 void InitGameControlValues()
 {
   int i;
@@ -1899,13 +2077,14 @@ void InitGameControlValues()
   for (i = 0; game_panel_controls[i].nr != -1; i++)
   {
     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
+    struct GamePanelOrderInfo *gpo = &game_panel_order[i];
+    struct TextPosInfo *pos = gpc->pos;
     int nr = gpc->nr;
     int type = gpc->type;
-    struct TextPosInfo *pos = gpc->pos;
 
     if (nr != i)
     {
-      Error(ERR_INFO, "'game_panel_controls' structure corrupted");
+      Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
       Error(ERR_EXIT, "this should not happen -- please debug");
     }
 
@@ -1925,22 +2104,73 @@ void InitGameControlValues()
       pos->width = pos->size;
       pos->height = pos->size;
     }
+
+    /* fill structure for game panel draw order */
+    gpo->nr = gpc->nr;
+    gpo->sort_priority = pos->sort_priority;
   }
+
+  /* sort game panel controls according to sort_priority and control number */
+  qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
+       sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
+}
+
+void UpdatePlayfieldElementCount()
+{
+  boolean use_element_count = FALSE;
+  int i, j, x, y;
+
+  /* first check if it is needed at all to calculate playfield element count */
+  for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
+    if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
+      use_element_count = TRUE;
+
+  if (!use_element_count)
+    return;
+
+  for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+    element_info[i].element_count = 0;
+
+  SCAN_PLAYFIELD(x, y)
+  {
+    element_info[Feld[x][y]].element_count++;
+  }
+
+  for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
+    for (j = 0; j < MAX_NUM_ELEMENTS; j++)
+      if (IS_IN_GROUP(j, i))
+       element_info[EL_GROUP_START + i].element_count +=
+         element_info[j].element_count;
 }
 
 void UpdateGameControlValues()
 {
   int i, k;
-  int time = (level.time == 0 ? TimePlayed : TimeLeft);
-  int score = (local_player->LevelSolved ? local_player->score_final :
+  int time = (local_player->LevelSolved ?
+             local_player->LevelSolved_CountingTime :
+             level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+             level.native_em_level->lev->time :
+             level.time == 0 ? TimePlayed : TimeLeft);
+  int score = (local_player->LevelSolved ?
+              local_player->LevelSolved_CountingScore :
+              level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+              level.native_em_level->lev->score :
               local_player->score);
-  int exit_closed = (local_player->gems_still_needed > 0 ||
+  int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+             level.native_em_level->lev->required :
+             local_player->gems_still_needed);
+  int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+                    level.native_em_level->lev->required > 0 :
+                    local_player->gems_still_needed > 0 ||
                     local_player->sokobanfields_still_needed > 0 ||
                     local_player->lights_still_needed > 0);
 
+  UpdatePlayfieldElementCount();
+
+  /* update game panel control values */
+
   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
-  game_panel_controls[GAME_PANEL_GEMS].value =
-    local_player->gems_still_needed;
+  game_panel_controls[GAME_PANEL_GEMS].value = gems;
 
   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
   for (i = 0; i < MAX_NUM_KEYS; i++)
@@ -1953,12 +2183,24 @@ void UpdateGameControlValues()
     for (i = 0; i < MAX_PLAYERS; i++)
     {
       for (k = 0; k < MAX_NUM_KEYS; k++)
-       if (stored_player[i].key[k])
+      {
+       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+       {
+         if (level.native_em_level->ply[i]->keys & (1 << k))
+           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
+             get_key_element_from_nr(k);
+       }
+       else if (stored_player[i].key[k])
          game_panel_controls[GAME_PANEL_KEY_1 + k].value =
            get_key_element_from_nr(k);
+      }
 
-      game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
-       stored_player[i].inventory_size;
+      if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+         level.native_em_level->ply[i]->dynamite;
+      else
+       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+         stored_player[i].inventory_size;
 
       if (stored_player[i].num_white_keys > 0)
        game_panel_controls[GAME_PANEL_KEY_WHITE].value =
@@ -1973,12 +2215,24 @@ void UpdateGameControlValues()
     int player_nr = game.centered_player_nr;
 
     for (k = 0; k < MAX_NUM_KEYS; k++)
-      if (stored_player[player_nr].key[k])
+    {
+      if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+      {
+       if (level.native_em_level->ply[player_nr]->keys & (1 << k))
+         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
+           get_key_element_from_nr(k);
+      }
+      else if (stored_player[player_nr].key[k])
        game_panel_controls[GAME_PANEL_KEY_1 + k].value =
          get_key_element_from_nr(k);
+    }
 
-    game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
-      stored_player[player_nr].inventory_size;
+    if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+      game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+       level.native_em_level->ply[player_nr]->dynamite;
+    else
+      game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
+       stored_player[player_nr].inventory_size;
 
     if (stored_player[player_nr].num_white_keys > 0)
       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
@@ -1996,6 +2250,7 @@ void UpdateGameControlValues()
   }
 
   game_panel_controls[GAME_PANEL_SCORE].value = score;
+  game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
 
   game_panel_controls[GAME_PANEL_TIME].value = time;
 
@@ -2092,64 +2347,144 @@ void UpdateGameControlValues()
   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
 #endif
 
+  for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
+    game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
+
+  for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
+    game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
+      (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
+       game.panel.element[i].id : EL_UNDEFINED);
+
+  for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
+    game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
+      (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
+       element_info[game.panel.element_count[i].id].element_count : 0);
+
   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
-  {
-    if (game.panel.ce_score[i].id != EL_UNDEFINED)
-    {
-      int ce_score = element_info[game.panel.ce_score[i].id].collect_score;
+    game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
+      (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
+       element_info[game.panel.ce_score[i].id].collect_score : 0);
 
-      game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value = ce_score;
-      game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value = ce_score;
-    }
-    else
-    {
-      game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value = 0;
-      game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value = 0;
-    }
-  }
+  for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
+    game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
+      (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
+       element_info[game.panel.ce_score_element[i].id].collect_score :
+       EL_UNDEFINED);
 
   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
 
+  /* update game panel control frames */
+
   for (i = 0; game_panel_controls[i].nr != -1; i++)
   {
     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
 
     if (gpc->type == TYPE_ELEMENT)
     {
+      int last_anim_random_frame = gfx.anim_random_frame;
+      int element = gpc->value;
+      int graphic = el2panelimg(element);
+
       if (gpc->value != gpc->last_value)
+      {
        gpc->gfx_frame = 0;
+       gpc->gfx_random = INIT_GFX_RANDOM();
+      }
       else
+      {
        gpc->gfx_frame++;
 
+       if (ANIM_MODE(graphic) == ANIM_RANDOM &&
+           IS_NEXT_FRAME(gpc->gfx_frame, graphic))
+         gpc->gfx_random = INIT_GFX_RANDOM();
+      }
+
+      if (ANIM_MODE(graphic) == ANIM_RANDOM)
+       gfx.anim_random_frame = gpc->gfx_random;
+
+      if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
+       gpc->gfx_frame = element_info[element].collect_score;
+
       gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
                                            gpc->gfx_frame);
+
+      if (ANIM_MODE(graphic) == ANIM_RANDOM)
+       gfx.anim_random_frame = last_anim_random_frame;
     }
   }
 }
 
 void DisplayGameControlValues()
 {
+  boolean redraw_panel = FALSE;
   int i;
 
+  for (i = 0; game_panel_controls[i].nr != -1; i++)
+  {
+    struct GamePanelControlInfo *gpc = &game_panel_controls[i];
+
+    if (PANEL_DEACTIVATED(gpc->pos))
+      continue;
+
+    if (gpc->value == gpc->last_value &&
+       gpc->frame == gpc->last_frame)
+      continue;
+
+    redraw_panel = TRUE;
+  }
+
+  if (!redraw_panel)
+    return;
+
+  /* copy default game door content to main double buffer */
+  BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
+            DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
+
+  /* redraw game control buttons */
+#if 1
+  RedrawGameButtons();
+#else
+  UnmapGameButtons();
+  MapGameButtons();
+#endif
+
   game_status = GAME_MODE_PSEUDO_PANEL;
 
+#if 1
+  for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
+#else
   for (i = 0; game_panel_controls[i].nr != -1; i++)
+#endif
   {
+#if 1
+    int nr = game_panel_order[i].nr;
+    struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
+#else
     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
     int nr = gpc->nr;
-    int type = gpc->type;
+#endif
     struct TextPosInfo *pos = gpc->pos;
+    int type = gpc->type;
     int value = gpc->value;
     int frame = gpc->frame;
+#if 0
     int last_value = gpc->last_value;
     int last_frame = gpc->last_frame;
+#endif
     int size = pos->size;
     int font = pos->font;
+    boolean draw_masked = pos->draw_masked;
+    int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
+
+    if (PANEL_DEACTIVATED(pos))
+      continue;
 
+#if 0
     if (value == last_value && frame == last_frame)
       continue;
+#endif
 
     gpc->last_value = value;
     gpc->last_frame = frame;
@@ -2158,9 +2493,6 @@ void DisplayGameControlValues()
     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
 #endif
 
-    if (PANEL_DEACTIVATED(pos))
-      continue;
-
     if (type == TYPE_INTEGER)
     {
       if (nr == GAME_PANEL_LEVEL_NUMBER ||
@@ -2179,6 +2511,7 @@ void DisplayGameControlValues()
          size = (value < value_change ? size1 : size2);
          font = (value < value_change ? font1 : font2);
 
+#if 0
          /* clear background if value just changed its size (dynamic digits) */
          if ((last_value < value_change) != (value < value_change))
          {
@@ -2192,6 +2525,7 @@ void DisplayGameControlValues()
            ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
                                       max_width, max_height);
          }
+#endif
        }
       }
 
@@ -2204,7 +2538,8 @@ void DisplayGameControlValues()
       pos->width = size * getFontWidth(font);
 #endif
 
-      DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, size), font);
+      DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
+                 int2str(value, size), font, mask_mode);
     }
     else if (type == TYPE_ELEMENT)
     {
@@ -2215,6 +2550,39 @@ void DisplayGameControlValues()
       int dst_x = PANEL_XPOS(pos);
       int dst_y = PANEL_YPOS(pos);
 
+#if 1
+      if (value != EL_UNDEFINED && value != EL_EMPTY)
+      {
+       element = value;
+       graphic = el2panelimg(value);
+
+       // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
+
+#if 1
+       if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
+         size = TILESIZE;
+#endif
+
+       getSizedGraphicSource(graphic, frame, size, &src_bitmap,
+                             &src_x, &src_y);
+
+       width  = graphic_info[graphic].width  * size / TILESIZE;
+       height = graphic_info[graphic].height * size / TILESIZE;
+
+       if (draw_masked)
+       {
+         SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
+                       dst_x - src_x, dst_y - src_y);
+         BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
+                          dst_x, dst_y);
+       }
+       else
+       {
+         BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
+                    dst_x, dst_y);
+       }
+      }
+#else
       if (value == EL_UNDEFINED || value == EL_EMPTY)
       {
        element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
@@ -2236,6 +2604,7 @@ void DisplayGameControlValues()
       height = graphic_info[graphic].height * size / TILESIZE;
 
       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
+#endif
     }
     else if (type == TYPE_STRING)
     {
@@ -2252,6 +2621,7 @@ void DisplayGameControlValues()
       {
        int font1 = pos->font;          /* (used for normal state) */
        int font2 = pos->font_alt;      /* (used for active state) */
+#if 0
        int size1 = strlen(state_normal);
        int size2 = strlen(state_active);
        int width1 = size1 * getFontWidth(font1);
@@ -2264,6 +2634,7 @@ void DisplayGameControlValues()
        /* clear background for values that may have changed its size */
        ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
                                   max_width, max_height);
+#endif
 
        font = (active ? font2 : font1);
       }
@@ -2285,7 +2656,8 @@ void DisplayGameControlValues()
 
        s_cut = getStringCopyN(s, size);
 
-       DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), s_cut, font);
+       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
+                   s_cut, font, mask_mode);
 
        free(s_cut);
       }
@@ -2297,6 +2669,15 @@ void DisplayGameControlValues()
   game_status = GAME_MODE_PLAYING;
 }
 
+void UpdateAndDisplayGameControlValues()
+{
+  if (tape.warp_forward)
+    return;
+
+  UpdateGameControlValues();
+  DisplayGameControlValues();
+}
+
 void DrawGameValue_Emeralds(int value)
 {
   struct TextPosInfo *pos = &game.panel.gems;
@@ -2980,6 +3361,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_player_bits = CH_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;
@@ -3344,6 +3726,8 @@ void InitGame()
     player->LevelSolved_PanelOff = FALSE;
     player->LevelSolved_SaveTape = FALSE;
     player->LevelSolved_SaveScore = FALSE;
+    player->LevelSolved_CountingTime = 0;
+    player->LevelSolved_CountingScore = 0;
   }
 
   network_player_action_received = FALSE;
@@ -3459,6 +3843,8 @@ void InitGame()
       emulate_sp = FALSE;
 
     InitField(x, y, TRUE);
+
+    ResetGfxAnimation(x, y);
   }
 
   InitBeltMovement();
@@ -3766,8 +4152,10 @@ void InitGame()
                local_player->jy - MIDPOSY);
   }
 
+#if 0
   /* do not use PLAYING mask for fading out from main screen */
   game_status = GAME_MODE_MAIN;
+#endif
 
   StopAnimation();
 
@@ -3793,7 +4181,9 @@ void InitGame()
     FadeOut(REDRAW_FIELD);
 #endif
 
+#if 0
   game_status = GAME_MODE_PLAYING;
+#endif
 
   /* !!! FIX THIS (START) !!! */
   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
@@ -3839,8 +4229,12 @@ void InitGame()
   SetPanelBackground();
   SetDrawBackgroundMask(REDRAW_DOOR_1);
 
+#if 1
+  UpdateAndDisplayGameControlValues();
+#else
   UpdateGameDoorValues();
   DrawGameDoorValues();
+#endif
 
   if (!game.restart_level)
   {
@@ -4097,6 +4491,9 @@ static void PlayerWins(struct PlayerInfo *player)
 
   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
                         level.native_em_level->lev->score : player->score);
+
+  player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
+  player->LevelSolved_CountingScore = player->score_final;
 }
 
 void GameWon()
@@ -4152,6 +4549,9 @@ void GameWon()
       score = score_final;
 
 #if 1
+      local_player->LevelSolved_CountingTime = time;
+      local_player->LevelSolved_CountingScore = score;
+
       game_panel_controls[GAME_PANEL_TIME].value = time;
       game_panel_controls[GAME_PANEL_SCORE].value = score;
 
@@ -4234,6 +4634,9 @@ void GameWon()
     score += time_count_steps * level.score[SC_TIME_BONUS];
 
 #if 1
+    local_player->LevelSolved_CountingTime = time;
+    local_player->LevelSolved_CountingScore = score;
+
     game_panel_controls[GAME_PANEL_TIME].value = time;
     game_panel_controls[GAME_PANEL_SCORE].value = score;
 
@@ -7162,10 +7565,18 @@ void StartMoving(int x, int y)
       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
       {
        if (!MovDelay[x][y])
+       {
          MovDelay[x][y] = TILEY + 1;
 
+         ResetGfxAnimation(x, y);
+         ResetGfxAnimation(x, y + 1);
+       }
+
        if (MovDelay[x][y])
        {
+         DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
+         DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
+
          MovDelay[x][y]--;
          if (MovDelay[x][y])
            return;
@@ -7199,10 +7610,18 @@ void StartMoving(int x, int y)
       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
       {
        if (!MovDelay[x][y])
+       {
          MovDelay[x][y] = TILEY + 1;
 
+         ResetGfxAnimation(x, y);
+         ResetGfxAnimation(x, y + 1);
+       }
+
        if (MovDelay[x][y])
        {
+         DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
+         DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
+
          MovDelay[x][y]--;
          if (MovDelay[x][y])
            return;
@@ -8259,7 +8678,7 @@ void ContinueMoving(int x, int y)
   }
 
 #if USE_NEW_CUSTOM_VALUE
-    CustomValue[newx][newy] = CustomValue[x][y];
+  CustomValue[newx][newy] = CustomValue[x][y];
 #endif
 
   ChangeDelay[x][y] = 0;
@@ -9130,9 +9549,6 @@ void DrawTwinkleOnField(int x, int y)
   {
     MovDelay[x][y]--;
 
-    if (setup.direct_draw && MovDelay[x][y])
-      SetDrawtoField(DRAW_BUFFERED);
-
     DrawLevelElementAnimation(x, y, Feld[x][y]);
 
     if (MovDelay[x][y] != 0)
@@ -9141,18 +9557,6 @@ void DrawTwinkleOnField(int x, int y)
                                           10 - MovDelay[x][y]);
 
       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
-
-      if (setup.direct_draw)
-      {
-       int dest_x, dest_y;
-
-       dest_x = FX + SCREENX(x) * TILEX;
-       dest_y = FY + SCREENY(y) * TILEY;
-
-       BlitBitmap(drawto_field, window,
-                  dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
-       SetDrawtoField(DRAW_DIRECT);
-      }
     }
   }
 }
@@ -9411,7 +9815,7 @@ void MauerAblegerStahl(int ax, int ay)
        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
       ((links_massiv && rechts_massiv) ||
        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
-    Feld[ax][ay] = EL_WALL;
+    Feld[ax][ay] = EL_STEELWALL;
 
   if (new_wall)
     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
@@ -9646,11 +10050,15 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
                            action_mode, action_arg_number,
                            action_arg_number_min, action_arg_number_max);
 
+#if 1
+  int trigger_player_bits = change->actual_trigger_player_bits;
+#else
   int trigger_player_bits =
     (change->actual_trigger_player >= EL_PLAYER_1 &&
      change->actual_trigger_player <= EL_PLAYER_4 ?
      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
      PLAYER_BITS_ANY);
+#endif
 
   int action_arg_player_bits =
     (action_arg >= CA_ARG_PLAYER_1 &&
@@ -10156,6 +10564,7 @@ static boolean ChangeElement(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_player_bits = CH_PLAYER_1;
     change->actual_trigger_side = CH_SIDE_NONE;
     change->actual_trigger_ce_value = 0;
     change->actual_trigger_ce_score = 0;
@@ -10536,7 +10945,8 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
          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_player = GET_PLAYER_FROM_BITS(trigger_player);
+       change->actual_trigger_player_bits = 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);
@@ -10655,7 +11065,8 @@ static boolean CheckElementChangeExt(int x, int y,
         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_player = GET_PLAYER_FROM_BITS(trigger_player);
+      change->actual_trigger_player_bits = 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);
@@ -11046,8 +11457,12 @@ static void CheckLevelTime()
       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
   }
 
+#if 1
+  UpdateAndDisplayGameControlValues();
+#else
   UpdateGameDoorValues();
   DrawGameDoorValues();
+#endif
 }
 
 void AdvanceFrameAndPlayerCounters(int player_nr)
@@ -12113,7 +12528,7 @@ void ScrollLevel(int dx, int dy)
 
 #else
 
-#if 1
+#if 0
   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
   int xsize = (BX2 - BX1 + 1);
   int ysize = (BY2 - BY1 + 1);
@@ -15430,6 +15845,7 @@ void CreateGameButtons()
                      GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
                      GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
                      GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
+                     GDI_DIRECT_DRAW, FALSE,
                      GDI_EVENT_MASK, event_mask,
                      GDI_CALLBACK_ACTION, HandleGameButtons,
                      GDI_END);
@@ -15465,6 +15881,14 @@ void UnmapGameButtons()
     UnmapGadget(game_gadget[i]);
 }
 
+void RedrawGameButtons()
+{
+  int i;
+
+  for (i = 0; i < NUM_GAME_BUTTONS; i++)
+    RedrawGadget(game_gadget[i]);
+}
+
 static void HandleGameButtons(struct GadgetInfo *gi)
 {
   int id = gi->custom_id;