rnd-20080206-1-src
[rocksndiamonds.git] / src / game.c
index 76a9952991770243f7d206eb143d2a11d11adea9..057c8dd257c598303e17516e1c2ac29097d291c8 100644 (file)
 
 #define USE_FIX_KILLED_BY_NON_WALKABLE (USE_NEW_STUFF          * 1)
 #define USE_FIX_IMPACT_COLLISION       (USE_NEW_STUFF          * 1)
+#define USE_FIX_CE_ACTION_WITH_PLAYER  (USE_NEW_STUFF          * 1)
+#define USE_FIX_NO_ACTION_AFTER_CHANGE (USE_NEW_STUFF          * 1)
+
+#define USE_PLAYER_REANIMATION         (USE_NEW_STUFF          * 1)
 
 #define USE_GFX_RESET_WHEN_NOT_MOVING  (USE_NEW_STUFF          * 1)
 
+#define USE_DELAYED_GFX_REDRAW         (USE_NEW_STUFF          * 0)
+
+#if USE_DELAYED_GFX_REDRAW
+#define TEST_DrawLevelField(x, y)                              \
+       GfxRedraw[x][y] |= GFX_REDRAW_TILE
+#define TEST_DrawLevelFieldCrumbledSand(x, y)                  \
+       GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
+#define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)                \
+       GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
+#define TEST_DrawTwinkleOnField(x, y)                          \
+       GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
+#else
+#define TEST_DrawLevelField(x, y)                              \
+            DrawLevelField(x, y)
+#define TEST_DrawLevelFieldCrumbledSand(x, y)                  \
+            DrawLevelFieldCrumbledSand(x, y)
+#define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y)                \
+            DrawLevelFieldCrumbledSandNeighbours(x, y)
+#define TEST_DrawTwinkleOnField(x, y)                          \
+            DrawTwinkleOnField(x, y)
+#endif
+
 
 /* for DigField() */
 #define DF_NO_PUSH             0
@@ -1103,6 +1129,7 @@ void TestIfBadThingRunsIntoPlayer(int, int, int);
 void TestIfFriendTouchesBadThing(int, int);
 void TestIfBadThingTouchesFriend(int, int);
 void TestIfBadThingTouchesOtherBadThing(int, int);
+void TestIfGoodThingGetsHitByBadThing(int, int, int);
 
 void KillPlayer(struct PlayerInfo *);
 void BuryPlayer(struct PlayerInfo *);
@@ -1698,6 +1725,7 @@ static void InitPlayerField(int x, int y, int element, boolean init_game)
       }
       else
       {
+       stored_player[0].initial_element = element;
        stored_player[0].use_murphy = TRUE;
 
        if (!level.use_artwork_element[0])
@@ -1756,6 +1784,17 @@ static void InitPlayerField(int x, int y, int element, boolean init_game)
     player->jx = player->last_jx = x;
     player->jy = player->last_jy = y;
   }
+
+#if USE_PLAYER_REANIMATION
+  if (!init_game)
+  {
+    int player_nr = GET_PLAYER_NR(element);
+    struct PlayerInfo *player = &stored_player[player_nr];
+
+    if (player->active && player->killed)
+      player->reanimated = TRUE; /* if player was just killed, reanimate him */
+  }
+#endif
 }
 
 static void InitField(int x, int y, boolean init_game)
@@ -2411,35 +2450,38 @@ void UpdateGameControlValues()
 
     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
+      if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
       {
-       gpc->gfx_frame++;
+       int last_anim_random_frame = gfx.anim_random_frame;
+       int element = gpc->value;
+       int graphic = el2panelimg(element);
 
-       if (ANIM_MODE(graphic) == ANIM_RANDOM &&
-           IS_NEXT_FRAME(gpc->gfx_frame, graphic))
+       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_RANDOM)
+         gfx.anim_random_frame = gpc->gfx_random;
 
-      if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
-       gpc->gfx_frame = element_info[element].collect_score;
+       if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
+         gpc->gfx_frame = element_info[element].collect_score;
 
-      gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
-                                           gpc->gfx_frame);
+       gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
+                                             gpc->gfx_frame);
 
-      if (ANIM_MODE(graphic) == ANIM_RANDOM)
-       gfx.anim_random_frame = last_anim_random_frame;
+       if (ANIM_MODE(graphic) == ANIM_RANDOM)
+         gfx.anim_random_frame = last_anim_random_frame;
+      }
     }
   }
 }
@@ -3409,8 +3451,8 @@ static void InitGameEngine()
     for (j = 0; j < ei->num_change_pages; j++)
     {
       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_player = EL_EMPTY;
+      ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
       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;
@@ -3638,6 +3680,11 @@ void InitGame()
   boolean emulate_sp = TRUE;   /* unless non-SUPAPLEX    elements found */
 #if 0
   boolean do_fading = (game_status == GAME_MODE_MAIN);
+#endif
+#if 1
+  int initial_move_dir = MV_DOWN;
+#else
+  int initial_move_dir = MV_NONE;
 #endif
   int i, j, x, y;
 
@@ -3660,6 +3707,7 @@ void InitGame()
     player->present = FALSE;
     player->active = FALSE;
     player->killed = FALSE;
+    player->reanimated = FALSE;
 
     player->action = 0;
     player->effective_action = 0;
@@ -3683,18 +3731,19 @@ void InitGame()
     player->dynabombs_left = 0;
     player->dynabomb_xl = FALSE;
 
-    player->MovDir = MV_NONE;
+    player->MovDir = initial_move_dir;
     player->MovPos = 0;
     player->GfxPos = 0;
-    player->GfxDir = MV_NONE;
+    player->GfxDir = initial_move_dir;
     player->GfxAction = ACTION_DEFAULT;
     player->Frame = 0;
     player->StepFrame = 0;
 
-    player->use_murphy = FALSE;
+    player->initial_element = player->element_nr;
     player->artwork_element =
       (level.use_artwork_element[i] ? level.artwork_element[i] :
        player->element_nr);
+    player->use_murphy = FALSE;
 
     player->block_last_field = FALSE;  /* initialized in InitPlayerField() */
     player->block_delay_adjustment = 0;        /* initialized in InitPlayerField() */
@@ -3707,7 +3756,7 @@ void InitGame()
 
     player->step_counter = 0;
 
-    player->last_move_dir = MV_NONE;
+    player->last_move_dir = initial_move_dir;
 
     player->is_active = FALSE;
 
@@ -3731,7 +3780,7 @@ void InitGame()
     player->anim_delay_counter = 0;
     player->post_delay_counter = 0;
 
-    player->dir_waiting = MV_NONE;
+    player->dir_waiting = initial_move_dir;
     player->action_waiting = ACTION_DEFAULT;
     player->last_action_waiting = ACTION_DEFAULT;
     player->special_action_bored = ACTION_DEFAULT;
@@ -3764,6 +3813,26 @@ void InitGame()
     player->inventory_infinite_element = EL_UNDEFINED;
     player->inventory_size = 0;
 
+    if (level.use_initial_inventory[i])
+    {
+      for (j = 0; j < level.initial_inventory_size[i]; j++)
+      {
+       int element = level.initial_inventory_content[i][j];
+       int collect_count = element_info[element].collect_count_initial;
+       int k;
+
+       if (!IS_CUSTOM_ELEMENT(element))
+         collect_count = 1;
+
+       if (collect_count == 0)
+         player->inventory_infinite_element = element;
+       else
+         for (k = 0; k < collect_count; k++)
+           if (player->inventory_size < MAX_INVENTORY_SIZE)
+             player->inventory_element[player->inventory_size++] = element;
+      }
+    }
+
     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
     SnapField(player, 0, 0);
 
@@ -3880,6 +3949,7 @@ void InitGame()
     GfxElement[x][y] = EL_UNDEFINED;
     GfxAction[x][y] = ACTION_DEFAULT;
     GfxDir[x][y] = MV_NONE;
+    GfxRedraw[x][y] = GFX_REDRAW_NONE;
   }
 
   SCAN_PLAYFIELD(x, y)
@@ -3989,6 +4059,7 @@ void InitGame()
          some_player->present = FALSE;
          some_player->active = FALSE;
 
+         player->initial_element = some_player->initial_element;
          player->artwork_element = some_player->artwork_element;
 
          player->block_last_field       = some_player->block_last_field;
@@ -5110,6 +5181,10 @@ static void RemoveField(int x, int y)
   GfxElement[x][y] = EL_UNDEFINED;
   GfxAction[x][y] = ACTION_DEFAULT;
   GfxDir[x][y] = MV_NONE;
+#if 0
+  /* !!! this would prevent the removed tile from being redrawn !!! */
+  GfxRedraw[x][y] = GFX_REDRAW_NONE;
+#endif
 }
 
 void RemoveMovingField(int x, int y)
@@ -5135,7 +5210,7 @@ void RemoveMovingField(int x, int y)
 
       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
 
-      DrawLevelField(oldx, oldy);
+      TEST_DrawLevelField(oldx, oldy);
 
       return;
     }
@@ -5164,8 +5239,8 @@ void RemoveMovingField(int x, int y)
   if (next_element != EL_UNDEFINED)
     Feld[oldx][oldy] = next_element;
 
-  DrawLevelField(oldx, oldy);
-  DrawLevelField(newx, newy);
+  TEST_DrawLevelField(oldx, oldy);
+  TEST_DrawLevelField(newx, newy);
 }
 
 void DrawDynamite(int x, int y)
@@ -5828,12 +5903,12 @@ void Explode(int ex, int ey, int phase, int mode)
 
     InitField_WithBug2(x, y, FALSE);
 
-    DrawLevelField(x, y);
+    TEST_DrawLevelField(x, y);
 
     TestIfElementTouchesCustomElement(x, y);
 
     if (GFX_CRUMBLED(element))
-      DrawLevelFieldCrumbledSandNeighbours(x, y);
+      TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
 
     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
       StorePlayer[x][y] = 0;
@@ -5847,7 +5922,7 @@ void Explode(int ex, int ey, int phase, int mode)
     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
 
     if (phase == delay)
-      DrawLevelFieldCrumbledSand(x, y);
+      TEST_DrawLevelFieldCrumbledSand(x, y);
 
     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
     {
@@ -5924,8 +5999,12 @@ void Bang(int x, int y)
   {
     struct PlayerInfo *player = PLAYERINFO(x, y);
 
+#if USE_FIX_CE_ACTION_WITH_PLAYER
+    element = Feld[x][y] = player->initial_element;
+#else
     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
                            player->element_nr);
+#endif
 
     if (level.use_explosion_element[player->index_nr])
     {
@@ -6150,7 +6229,7 @@ static void ToggleBeltSwitch(int x, int y)
       if (e_belt_nr == belt_nr)
       {
        Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
-       DrawLevelField(xx, yy);
+       TEST_DrawLevelField(xx, yy);
       }
     }
     else if (IS_BELT(element) && belt_dir != MV_NONE)
@@ -6162,7 +6241,7 @@ static void ToggleBeltSwitch(int x, int y)
        int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
 
        Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
-       DrawLevelField(xx, yy);
+       TEST_DrawLevelField(xx, yy);
       }
     }
     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
@@ -6174,7 +6253,7 @@ static void ToggleBeltSwitch(int x, int y)
        int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
 
        Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
-       DrawLevelField(xx, yy);
+       TEST_DrawLevelField(xx, yy);
       }
     }
   }
@@ -6195,34 +6274,34 @@ static void ToggleSwitchgateSwitch(int x, int y)
        element == EL_SWITCHGATE_SWITCH_DOWN)
     {
       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
-      DrawLevelField(xx, yy);
+      TEST_DrawLevelField(xx, yy);
     }
     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
             element == EL_DC_SWITCHGATE_SWITCH_DOWN)
     {
       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
-      DrawLevelField(xx, yy);
+      TEST_DrawLevelField(xx, yy);
     }
 #else
     if (element == EL_SWITCHGATE_SWITCH_UP)
     {
       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
-      DrawLevelField(xx, yy);
+      TEST_DrawLevelField(xx, yy);
     }
     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
     {
       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
-      DrawLevelField(xx, yy);
+      TEST_DrawLevelField(xx, yy);
     }
     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
     {
       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
-      DrawLevelField(xx, yy);
+      TEST_DrawLevelField(xx, yy);
     }
     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
     {
       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
-      DrawLevelField(xx, yy);
+      TEST_DrawLevelField(xx, yy);
     }
 #endif
     else if (element == EL_SWITCHGATE_OPEN ||
@@ -6270,25 +6349,25 @@ static void RedrawAllLightSwitchesAndInvisibleElements()
        game.light_time_left > 0)
     {
       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
             game.light_time_left == 0)
     {
       Feld[x][y] = EL_LIGHT_SWITCH;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_EMC_DRIPPER &&
             game.light_time_left > 0)
     {
       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_EMC_DRIPPER_ACTIVE &&
             game.light_time_left == 0)
     {
       Feld[x][y] = EL_EMC_DRIPPER;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_INVISIBLE_STEELWALL ||
             element == EL_INVISIBLE_WALL ||
@@ -6297,11 +6376,11 @@ static void RedrawAllLightSwitchesAndInvisibleElements()
       if (game.light_time_left > 0)
        Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
 
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
 
       /* uncrumble neighbour fields, if needed */
       if (element == EL_INVISIBLE_SAND)
-       DrawLevelFieldCrumbledSandNeighbours(x, y);
+       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
     }
     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
             element == EL_INVISIBLE_WALL_ACTIVE ||
@@ -6310,11 +6389,11 @@ static void RedrawAllLightSwitchesAndInvisibleElements()
       if (game.light_time_left == 0)
        Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
 
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
 
       /* re-crumble neighbour fields, if needed */
       if (element == EL_INVISIBLE_SAND)
-       DrawLevelFieldCrumbledSandNeighbours(x, y);
+       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
     }
   }
 }
@@ -6331,13 +6410,13 @@ static void RedrawAllInvisibleElementsForLenses()
        game.lenses_time_left > 0)
     {
       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_EMC_DRIPPER_ACTIVE &&
             game.lenses_time_left == 0)
     {
       Feld[x][y] = EL_EMC_DRIPPER;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_INVISIBLE_STEELWALL ||
             element == EL_INVISIBLE_WALL ||
@@ -6346,11 +6425,11 @@ static void RedrawAllInvisibleElementsForLenses()
       if (game.lenses_time_left > 0)
        Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
 
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
 
       /* uncrumble neighbour fields, if needed */
       if (element == EL_INVISIBLE_SAND)
-       DrawLevelFieldCrumbledSandNeighbours(x, y);
+       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
     }
     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
             element == EL_INVISIBLE_WALL_ACTIVE ||
@@ -6359,11 +6438,11 @@ static void RedrawAllInvisibleElementsForLenses()
       if (game.lenses_time_left == 0)
        Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
 
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
 
       /* re-crumble neighbour fields, if needed */
       if (element == EL_INVISIBLE_SAND)
-       DrawLevelFieldCrumbledSandNeighbours(x, y);
+       TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
     }
   }
 }
@@ -6380,13 +6459,13 @@ static void RedrawAllInvisibleElementsForMagnifier()
        game.magnify_time_left > 0)
     {
       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
             game.magnify_time_left == 0)
     {
       Feld[x][y] = EL_EMC_FAKE_GRASS;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (IS_GATE_GRAY(element) &&
             game.magnify_time_left > 0)
@@ -6397,8 +6476,10 @@ static void RedrawAllInvisibleElementsForMagnifier()
                    element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
                    IS_EMC_GATE_GRAY(element) ?
                    element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
+                   IS_DC_GATE_GRAY(element) ?
+                   EL_DC_GATE_WHITE_GRAY_ACTIVE :
                    element);
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (IS_GATE_GRAY_ACTIVE(element) &&
             game.magnify_time_left == 0)
@@ -6409,8 +6490,10 @@ static void RedrawAllInvisibleElementsForMagnifier()
                    element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
                    IS_EMC_GATE_GRAY_ACTIVE(element) ?
                    element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
+                   IS_DC_GATE_GRAY_ACTIVE(element) ?
+                   EL_DC_GATE_WHITE_GRAY :
                    element);
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
   }
 }
@@ -6447,7 +6530,7 @@ static void ActivateTimegateSwitch(int x, int y)
     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
     {
       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
-      DrawLevelField(xx, yy);
+      TEST_DrawLevelField(xx, yy);
     }
     */
 
@@ -6489,7 +6572,7 @@ void Impact(int x, int y)
       RemoveMovingField(x, y + 1);
       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
       Feld[x][y + 2] = EL_ROCK;
-      DrawLevelField(x, y + 2);
+      TEST_DrawLevelField(x, y + 2);
 
       object_hit = TRUE;
     }
@@ -6499,7 +6582,7 @@ void Impact(int x, int y)
       RemoveMovingField(x, y + 1);
       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
       Feld[x][y + 2] = EL_ROCK;
-      DrawLevelField(x, y + 2);
+      TEST_DrawLevelField(x, y + 2);
 
       object_hit = TRUE;
     }
@@ -6523,7 +6606,7 @@ void Impact(int x, int y)
       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
   {
     ResetGfxAnimation(x, y);
-    DrawLevelField(x, y);
+    TEST_DrawLevelField(x, y);
   }
 
   if (impact && CAN_EXPLODE_IMPACT(element))
@@ -6997,7 +7080,7 @@ inline static void TurnRoundExt(int x, int y)
       {
        Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
        ResetGfxAnimation(move_x, move_y);
-       DrawLevelField(move_x, move_y);
+       TEST_DrawLevelField(move_x, move_y);
 
        MovDir[x][y] = back_dir;
       }
@@ -8102,7 +8185,7 @@ void StartMoving(int x, int y)
                               element == EL_SP_SNIKSNAK ||
                               element == EL_SP_ELECTRON ||
                               element == EL_MOLE))
-         DrawLevelField(x, y);
+         TEST_DrawLevelField(x, y);
       }
     }
 
@@ -8136,7 +8219,7 @@ void StartMoving(int x, int y)
        if (IS_PLAYER(x, y))
          DrawPlayerField(x, y);
        else
-         DrawLevelField(x, y);
+         TEST_DrawLevelField(x, y);
 
        PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
 
@@ -8176,7 +8259,7 @@ void StartMoving(int x, int y)
 
            if (IN_SCR_FIELD(sx, sy))
            {
-             DrawLevelFieldCrumbledSand(xx, yy);
+             TEST_DrawLevelFieldCrumbledSand(xx, yy);
              DrawGraphic(sx, sy, flame_graphic, frame);
            }
          }
@@ -8184,7 +8267,7 @@ void StartMoving(int x, int y)
          {
            if (Feld[xx][yy] == EL_FLAMES)
              Feld[xx][yy] = EL_EMPTY;
-           DrawLevelField(xx, yy);
+           TEST_DrawLevelField(xx, yy);
          }
        }
       }
@@ -8227,7 +8310,7 @@ void StartMoving(int x, int y)
          Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
       {
        RemoveField(x, y);
-       DrawLevelField(x, y);
+       TEST_DrawLevelField(x, y);
 
        PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
        if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
@@ -8243,7 +8326,7 @@ void StartMoving(int x, int y)
       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
       {
        if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
-         DrawLevelField(newx, newy);
+         TEST_DrawLevelField(newx, newy);
        else
          GfxDir[x][y] = MovDir[x][y] = MV_NONE;
       }
@@ -8254,7 +8337,7 @@ void StartMoving(int x, int y)
        if (IS_PLAYER(x, y))
          DrawPlayerField(x, y);
        else
-         DrawLevelField(x, y);
+         TEST_DrawLevelField(x, y);
 
        return;
       }
@@ -8268,7 +8351,7 @@ void StartMoving(int x, int y)
        else
        {
          Feld[newx][newy] = EL_EMPTY;
-         DrawLevelField(newx, newy);
+         TEST_DrawLevelField(newx, newy);
        }
 
        PlayLevelSound(x, y, SND_PIG_DIGGING);
@@ -8278,7 +8361,7 @@ void StartMoving(int x, int y)
        if (IS_PLAYER(x, y))
          DrawPlayerField(x, y);
        else
-         DrawLevelField(x, y);
+         TEST_DrawLevelField(x, y);
 
        return;
       }
@@ -8357,7 +8440,7 @@ void StartMoving(int x, int y)
        else
        {
          Feld[newx][newy] = EL_EMPTY;
-         DrawLevelField(newx, newy);
+         TEST_DrawLevelField(newx, newy);
 
          PlayLevelSoundAction(x, y, ACTION_DIGGING);
        }
@@ -8368,7 +8451,7 @@ void StartMoving(int x, int y)
        if (IS_PLAYER(x, y))
          DrawPlayerField(x, y);
        else
-         DrawLevelField(x, y);
+         TEST_DrawLevelField(x, y);
 #endif
 
        return;
@@ -8409,7 +8492,7 @@ void StartMoving(int x, int y)
        else
        {
          RemoveField(newx, newy);
-         DrawLevelField(newx, newy);
+         TEST_DrawLevelField(newx, newy);
        }
 
        /* if digged element was about to explode, prevent the explosion */
@@ -8450,7 +8533,7 @@ void StartMoving(int x, int y)
        if (IS_PLAYER(x, y))
          DrawPlayerField(x, y);
        else
-         DrawLevelField(x, y);
+         TEST_DrawLevelField(x, y);
 
        return;
       }
@@ -8477,7 +8560,7 @@ void StartMoving(int x, int y)
          if (IS_PLAYER(x, y))
            DrawPlayerField(x, y);
          else
-           DrawLevelField(x, y);
+           TEST_DrawLevelField(x, y);
 
          PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
 
@@ -8515,7 +8598,7 @@ void StartMoving(int x, int y)
       else
       {
        Feld[newx][newy] = EL_EMPTY;
-       DrawLevelField(newx, newy);
+       TEST_DrawLevelField(newx, newy);
       }
 
       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
@@ -8546,7 +8629,7 @@ void StartMoving(int x, int y)
       else
       {
        Feld[newx][newy] = EL_EMPTY;
-       DrawLevelField(newx, newy);
+       TEST_DrawLevelField(newx, newy);
       }
 
       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
@@ -8569,7 +8652,7 @@ void StartMoving(int x, int y)
 
        ResetGfxAnimation(x, y);
        GfxAction[x][y] = ACTION_DIGGING;
-       DrawLevelField(x, y);
+       TEST_DrawLevelField(x, y);
 
        MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
 
@@ -8578,7 +8661,7 @@ void StartMoving(int x, int y)
       else     /* element == EL_PACMAN */
       {
        Feld[newx][newy] = EL_EMPTY;
-       DrawLevelField(newx, newy);
+       TEST_DrawLevelField(newx, newy);
        PlayLevelSound(x, y, SND_PACMAN_DIGGING);
       }
     }
@@ -8660,7 +8743,7 @@ void ContinueMoving(int x, int y)
           GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
 #endif
 
-    DrawLevelField(x, y);
+    TEST_DrawLevelField(x, y);
 
     return;    /* element is still moving */
   }
@@ -8679,7 +8762,7 @@ void ContinueMoving(int x, int y)
   {
     Feld[x][y] = EL_SAND;
 
-    DrawLevelFieldCrumbledSandNeighbours(x, y);
+    TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
   }
   else if (element == EL_QUICKSAND_FILLING)
   {
@@ -8846,7 +8929,7 @@ void ContinueMoving(int x, int y)
     InitField(x, y, FALSE);
 
     if (GFX_CRUMBLED(Feld[x][y]))
-      DrawLevelFieldCrumbledSandNeighbours(x, y);
+      TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
 
     if (ELEM_IS_PLAYER(move_leave_element))
       RelocatePlayer(x, y, move_leave_element);
@@ -8862,8 +8945,8 @@ void ContinueMoving(int x, int y)
        element_info[element].move_pattern == MV_WHEN_DROPPED)))
     GfxDir[x][y] = MovDir[newx][newy] = 0;
 
-  DrawLevelField(x, y);
-  DrawLevelField(newx, newy);
+  TEST_DrawLevelField(x, y);
+  TEST_DrawLevelField(newx, newy);
 
   Stop[newx][newy] = TRUE;     /* ignore this element until the next frame */
 
@@ -8908,6 +8991,11 @@ void ContinueMoving(int x, int y)
   else if (element == EL_PENGUIN)
     TestIfFriendTouchesBadThing(newx, newy);
 
+  if (DONT_GET_HIT_BY(element))
+  {
+    TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
+  }
+
   /* give the player one last chance (one more frame) to move away */
   if (CAN_FALL(element) && direction == MV_DOWN &&
       (last_line || (!IS_FREE(x, newy + 1) &&
@@ -9125,7 +9213,7 @@ void AmoebeUmwandelnBD(int ax, int ay, int new_element)
       AmoebaNr[x][y] = 0;
       Feld[x][y] = new_element;
       InitField(x, y, FALSE);
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
       done = TRUE;
     }
   }
@@ -9167,7 +9255,7 @@ void AmoebeWaechst(int x, int y)
     {
       Feld[x][y] = Store[x][y];
       Store[x][y] = 0;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
   }
 }
@@ -9199,7 +9287,7 @@ void AmoebaDisappearing(int x, int y)
     if (!MovDelay[x][y])
     {
       Feld[x][y] = EL_EMPTY;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
 
       /* don't let mole enter this field in this cycle;
         (give priority to objects falling to this field from above) */
@@ -9226,7 +9314,7 @@ void AmoebeAbleger(int ax, int ay)
   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
   {
     Feld[ax][ay] = EL_AMOEBA_DEAD;
-    DrawLevelField(ax, ay);
+    TEST_DrawLevelField(ax, ay);
     return;
   }
 
@@ -9296,7 +9384,7 @@ void AmoebeAbleger(int ax, int ay)
       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
       {
        Feld[ax][ay] = EL_AMOEBA_DEAD;
-       DrawLevelField(ax, ay);
+       TEST_DrawLevelField(ax, ay);
        AmoebaCnt[AmoebaNr[ax][ay]]--;
 
        if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
@@ -9360,7 +9448,7 @@ void AmoebeAbleger(int ax, int ay)
     return;
   }
 
-  DrawLevelField(newax, neway);
+  TEST_DrawLevelField(newax, neway);
 }
 
 void Life(int ax, int ay)
@@ -9418,7 +9506,7 @@ void Life(int ax, int ay)
       {
        Feld[xx][yy] = EL_EMPTY;
        if (!Stop[xx][yy])
-         DrawLevelField(xx, yy);
+         TEST_DrawLevelField(xx, yy);
        Stop[xx][yy] = TRUE;
        changed = TRUE;
       }
@@ -9431,7 +9519,7 @@ void Life(int ax, int ay)
        Feld[xx][yy] = element;
        MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
        if (!Stop[xx][yy])
-         DrawLevelField(xx, yy);
+         TEST_DrawLevelField(xx, yy);
        Stop[xx][yy] = TRUE;
        changed = TRUE;
       }
@@ -9695,28 +9783,28 @@ void MauerWaechst(int x, int y)
       if (MovDir[x][y] == MV_LEFT)
       {
        if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
-         DrawLevelField(x - 1, y);
+         TEST_DrawLevelField(x - 1, y);
       }
       else if (MovDir[x][y] == MV_RIGHT)
       {
        if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
-         DrawLevelField(x + 1, y);
+         TEST_DrawLevelField(x + 1, y);
       }
       else if (MovDir[x][y] == MV_UP)
       {
        if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
-         DrawLevelField(x, y - 1);
+         TEST_DrawLevelField(x, y - 1);
       }
       else
       {
        if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
-         DrawLevelField(x, y + 1);
+         TEST_DrawLevelField(x, y + 1);
       }
 
       Feld[x][y] = Store[x][y];
       Store[x][y] = 0;
       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
   }
 }
@@ -9807,7 +9895,7 @@ void MauerAbleger(int ax, int ay)
   }
 
   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
-    DrawLevelField(ax, ay);
+    TEST_DrawLevelField(ax, ay);
 
   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
     oben_massiv = TRUE;
@@ -9971,7 +10059,7 @@ void CheckForDragon(int x, int y)
        if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
        {
          Feld[xx][yy] = EL_EMPTY;
-         DrawLevelField(xx, yy);
+         TEST_DrawLevelField(xx, yy);
        }
        else
          break;
@@ -10035,7 +10123,7 @@ static void ChangeActiveTrap(int x, int y)
 
   /* if new animation frame was drawn, correct crumbled sand border */
   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
-    DrawLevelFieldCrumbledSand(x, y);
+    TEST_DrawLevelFieldCrumbledSand(x, y);
 }
 
 static int getSpecialActionElement(int element, int number, int base_element)
@@ -10069,6 +10157,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
   int action_type = change->action_type;
   int action_mode = change->action_mode;
   int action_arg = change->action_arg;
+  int action_element = change->action_element;
   int i;
 
   if (!change->has_action)
@@ -10080,11 +10169,18 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
     (level.time > 0 ? TimeLeft :
      TimePlayed);
 
-  int action_arg_element =
+  int action_arg_element_raw =
     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
+     action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
      EL_EMPTY);
+  int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
+
+#if 0
+  if (action_arg_element_raw == EL_GROUP_START)
+    printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
+#endif
 
   int action_arg_direction =
     (action_arg >= CA_ARG_DIRECTION_LEFT &&
@@ -10141,10 +10237,13 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
      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_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
      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_CS_ACTION ? GET_CE_SCORE(action_element) :
      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
+     action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
      -1);
 
   int action_arg_number_old =
@@ -10161,7 +10260,9 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
                            action_arg_number_min, action_arg_number_max);
 
 #if 1
-  int trigger_player_bits = change->actual_trigger_player_bits;
+  int trigger_player_bits =
+    (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
+     change->actual_trigger_player_bits : change->trigger_player);
 #else
   int trigger_player_bits =
     (change->actual_trigger_player >= EL_PLAYER_1 &&
@@ -10174,6 +10275,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
     (action_arg >= CA_ARG_PLAYER_1 &&
      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
+     action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
      PLAYER_BITS_ANY);
 
   /* ---------- execute action  -------------------------------------------- */
@@ -10329,6 +10431,10 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
 
     case CA_SET_PLAYER_SPEED:
     {
+#if 0
+      printf("::: trigger_player_bits == %d\n", trigger_player_bits);
+#endif
+
       for (i = 0; i < MAX_PLAYERS; i++)
       {
        if (trigger_player_bits & (1 << i))
@@ -10446,6 +10552,104 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       break;
     }
 
+    case CA_SET_PLAYER_INVENTORY:
+    {
+      for (i = 0; i < MAX_PLAYERS; i++)
+      {
+       struct PlayerInfo *player = &stored_player[i];
+       int j, k;
+
+       if (trigger_player_bits & (1 << i))
+       {
+         int inventory_element = action_arg_element;
+
+         if (action_arg == CA_ARG_ELEMENT_TARGET ||
+             action_arg == CA_ARG_ELEMENT_TRIGGER ||
+             action_arg == CA_ARG_ELEMENT_ACTION)
+         {
+           int element = inventory_element;
+           int collect_count = element_info[element].collect_count_initial;
+
+           if (!IS_CUSTOM_ELEMENT(element))
+             collect_count = 1;
+
+           if (collect_count == 0)
+             player->inventory_infinite_element = element;
+           else
+             for (k = 0; k < collect_count; k++)
+               if (player->inventory_size < MAX_INVENTORY_SIZE)
+                 player->inventory_element[player->inventory_size++] =
+                   element;
+         }
+         else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
+                  action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
+                  action_arg == CA_ARG_INVENTORY_RM_ACTION)
+         {
+           if (player->inventory_infinite_element != EL_UNDEFINED &&
+               IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
+                                    action_arg_element_raw))
+             player->inventory_infinite_element = EL_UNDEFINED;
+
+           for (k = 0, j = 0; j < player->inventory_size; j++)
+           {
+             if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
+                                       action_arg_element_raw))
+               player->inventory_element[k++] = player->inventory_element[j];
+           }
+
+           player->inventory_size = k;
+         }
+         else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
+         {
+           if (player->inventory_size > 0)
+           {
+             for (j = 0; j < player->inventory_size - 1; j++)
+               player->inventory_element[j] = player->inventory_element[j + 1];
+
+             player->inventory_size--;
+           }
+         }
+         else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
+         {
+           if (player->inventory_size > 0)
+             player->inventory_size--;
+         }
+         else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
+         {
+           player->inventory_infinite_element = EL_UNDEFINED;
+           player->inventory_size = 0;
+         }
+         else if (action_arg == CA_ARG_ELEMENT_RESET)
+         {
+           player->inventory_infinite_element = EL_UNDEFINED;
+           player->inventory_size = 0;
+
+           if (level.use_initial_inventory[i])
+           {
+             for (j = 0; j < level.initial_inventory_size[i]; j++)
+             {
+               int element = level.initial_inventory_content[i][j];
+               int collect_count = element_info[element].collect_count_initial;
+
+               if (!IS_CUSTOM_ELEMENT(element))
+                 collect_count = 1;
+
+               if (collect_count == 0)
+                 player->inventory_infinite_element = element;
+               else
+                 for (k = 0; k < collect_count; k++)
+                   if (player->inventory_size < MAX_INVENTORY_SIZE)
+                     player->inventory_element[player->inventory_size++] =
+                       element;
+             }
+           }
+         }
+       }
+      }
+
+      break;
+    }
+
     /* ---------- CE actions  ---------------------------------------------- */
 
     case CA_SET_CE_VALUE:
@@ -10514,6 +10718,38 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
       break;
     }
 
+    case CA_SET_CE_ARTWORK:
+    {
+      int artwork_element = action_arg_element;
+      boolean reset_frame = FALSE;
+      int xx, yy;
+
+      if (action_arg == CA_ARG_ELEMENT_RESET)
+       artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
+                          element);
+
+      if (ei->gfx_element != artwork_element)
+       reset_frame = TRUE;
+
+      ei->gfx_element = artwork_element;
+
+      SCAN_PLAYFIELD(xx, yy)
+      {
+       if (Feld[xx][yy] == element)
+       {
+         if (reset_frame)
+         {
+           ResetGfxAnimation(xx, yy);
+           ResetRandomAnimationValue(xx, yy);
+         }
+
+         TEST_DrawLevelField(xx, yy);
+       }
+      }
+
+      break;
+    }
+
     /* ---------- engine actions  ------------------------------------------ */
 
     case CA_SET_ENGINE_SCAN_MODE:
@@ -10590,10 +10826,10 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change)
     ResetRandomAnimationValue(x, y);
 #endif
 
-    DrawLevelField(x, y);
+    TEST_DrawLevelField(x, y);
 
     if (GFX_CRUMBLED(new_element))
-      DrawLevelFieldCrumbledSandNeighbours(x, y);
+      TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
   }
 
 #if 1
@@ -10673,8 +10909,8 @@ 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_player = EL_EMPTY;
+    change->actual_trigger_player_bits = CH_PLAYER_NONE;
     change->actual_trigger_side = CH_SIDE_NONE;
     change->actual_trigger_ce_value = 0;
     change->actual_trigger_ce_score = 0;
@@ -11061,6 +11297,11 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
        change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
        change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
 
+#if 0
+       printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
+              element, EL_NAME(element), p);
+#endif
+
        if ((change->can_change && !change_done) || change->has_action)
        {
          int x, y;
@@ -11071,6 +11312,21 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
            {
              if (change->can_change && !change_done)
              {
+#if USE_FIX_NO_ACTION_AFTER_CHANGE
+               /* if element already changed in this frame, not only prevent
+                  another element change (checked in ChangeElement()), but
+                  also prevent additional element actions for this element */
+
+               if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
+                   !level.use_action_after_change_bug)
+                 continue;
+#endif
+
+#if 0
+               printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
+                      element, EL_NAME(element), p);
+#endif
+
                ChangeDelay[x][y] = 1;
                ChangeEvent[x][y] = trigger_event;
 
@@ -11079,6 +11335,22 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
 #if USE_NEW_DELAYED_ACTION
              else if (change->has_action)
              {
+#if USE_FIX_NO_ACTION_AFTER_CHANGE
+               /* if element already changed in this frame, not only prevent
+                  another element change (checked in ChangeElement()), but
+                  also prevent additional element actions for this element */
+
+               if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
+                   !level.use_action_after_change_bug)
+                 continue;
+#endif
+
+
+#if 0
+               printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
+                      element, EL_NAME(element), p);
+#endif
+
                ExecuteCustomElementAction(x, y, element, p);
                PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
              }
@@ -11096,6 +11368,12 @@ static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
          {
            change_done = TRUE;
            change_done_any = TRUE;
+
+#if 0
+           printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
+                  element, EL_NAME(element), p);
+#endif
+
          }
        }
       }
@@ -11150,6 +11428,10 @@ static boolean CheckElementChangeExt(int x, int y,
 
   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
 
+#if 0
+  printf("::: X: trigger_player_bits == %d\n", trigger_player);
+#endif
+
   for (p = 0; p < element_info[element].num_change_pages; p++)
   {
     struct ElementChangeInfo *change = &element_info[element].change_page[p];
@@ -11961,7 +12243,7 @@ void GameActions_RND()
       if (MovDelay[x][y] <= 0)
       {
        RemoveField(x, y);
-       DrawLevelField(x, y);
+       TEST_DrawLevelField(x, y);
 
        TestIfElementTouchesCustomElement(x, y);        /* for empty space */
       }
@@ -11995,7 +12277,7 @@ void GameActions_RND()
     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
     {
       ResetGfxAnimation(x, y);
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
 
 #if DEBUG
@@ -12130,7 +12412,7 @@ void GameActions_RND()
        DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
 
       if (IS_GEM(element) || element == EL_SP_INFOTRON)
-       DrawTwinkleOnField(x, y);
+       TEST_DrawTwinkleOnField(x, y);
     }
     else if (IS_MOVING(x, y))
       ContinueMoving(x, y);
@@ -12265,7 +12547,7 @@ void GameActions_RND()
        DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
 
       if (IS_GEM(element) || element == EL_SP_INFOTRON)
-       DrawTwinkleOnField(x, y);
+       TEST_DrawTwinkleOnField(x, y);
     }
     else if ((element == EL_ACID ||
              element == EL_EXIT_OPEN ||
@@ -12450,19 +12732,19 @@ void GameActions_RND()
              element == EL_MAGIC_WALL_FULL)
          {
            Feld[x][y] = EL_MAGIC_WALL_DEAD;
-           DrawLevelField(x, y);
+           TEST_DrawLevelField(x, y);
          }
          else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
                   element == EL_BD_MAGIC_WALL_FULL)
          {
            Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
-           DrawLevelField(x, y);
+           TEST_DrawLevelField(x, y);
          }
          else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
                   element == EL_DC_MAGIC_WALL_FULL)
          {
            Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
-           DrawLevelField(x, y);
+           TEST_DrawLevelField(x, y);
          }
        }
 
@@ -12516,6 +12798,36 @@ void GameActions_RND()
     }
   }
 
+#if USE_DELAYED_GFX_REDRAW
+  SCAN_PLAYFIELD(x, y)
+  {
+#if 1
+    if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
+#else
+    if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
+       GfxRedraw[x][y] != GFX_REDRAW_NONE)
+#endif
+    {
+      /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
+        !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
+
+      if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
+       DrawLevelField(x, y);
+
+      if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
+       DrawLevelFieldCrumbledSand(x, y);
+
+      if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
+       DrawLevelFieldCrumbledSandNeighbours(x, y);
+
+      if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
+       DrawTwinkleOnField(x, y);
+    }
+
+    GfxRedraw[x][y] = GFX_REDRAW_NONE;
+  }
+#endif
+
   CheckLevelTime();
 
   DrawAllPlayers();
@@ -12600,12 +12912,13 @@ static boolean AllPlayersInVisibleScreen()
 
 void ScrollLevel(int dx, int dy)
 {
-#if 1
+#if 0
+  /* (directly solved in BlitBitmap() now) */
   static Bitmap *bitmap_db_field2 = NULL;
   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
   int x, y;
 #else
-  int i, x, y;
+  int x, y;
 #endif
 
 #if 0
@@ -12615,7 +12928,8 @@ void ScrollLevel(int dx, int dy)
     return;
 #endif
 
-#if 1
+#if 0
+  /* (directly solved in BlitBitmap() now) */
   if (bitmap_db_field2 == NULL)
     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
 
@@ -13073,7 +13387,7 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
     else if (old_jx == jx && old_jy != jy)
       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
 
-    DrawLevelField(jx, jy);    /* for "crumbled sand" */
+    TEST_DrawLevelField(jx, jy);       /* for "crumbled sand" */
 
     player->last_move_dir = player->MovDir;
     player->is_moving = TRUE;
@@ -13258,8 +13572,13 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
                                          CE_PLAYER_ENTERS_X,
                                          player->index_bit, enter_side);
 
+#if USE_FIX_CE_ACTION_WITH_PLAYER
+      CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
+                                       CE_MOVE_OF_X, move_direction);
+#else
       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
                                        CE_MOVE_OF_X, move_direction);
+#endif
     }
 
     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
@@ -13386,7 +13705,7 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
     if (!IN_LEV_FIELD(xx, yy))
       continue;
 
-    if (IS_PLAYER(x, y))
+    if (IS_PLAYER(x, y))               /* player found at center element */
     {
       struct PlayerInfo *player = PLAYERINFO(x, y);
 
@@ -13404,8 +13723,21 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
                                          CE_PLAYER_TOUCHES_X,
                                          player->index_bit, border_side);
+
+#if USE_FIX_CE_ACTION_WITH_PLAYER
+      {
+       /* use player element that is initially defined in the level playfield,
+          not the player element that corresponds to the runtime player number
+          (example: a level that contains EL_PLAYER_3 as the only player would
+          incorrectly give EL_PLAYER_1 for "player->element_nr") */
+       int player_element = PLAYERINFO(x, y)->initial_element;
+
+       CheckElementChangeBySide(xx, yy, border_element, player_element,
+                                CE_TOUCHING_X, border_side);
+      }
+#endif
     }
-    else if (IS_PLAYER(xx, yy))
+    else if (IS_PLAYER(xx, yy))                /* player found at border element */
     {
       struct PlayerInfo *player = PLAYERINFO(xx, yy);
 
@@ -13420,6 +13752,20 @@ void TestIfPlayerTouchesCustomElement(int x, int y)
       CheckTriggeredElementChangeByPlayer(x, y, center_element,
                                          CE_PLAYER_TOUCHES_X,
                                          player->index_bit, center_side);
+
+#if USE_FIX_CE_ACTION_WITH_PLAYER
+      {
+       /* use player element that is initially defined in the level playfield,
+          not the player element that corresponds to the runtime player number
+          (example: a level that contains EL_PLAYER_3 as the only player would
+          incorrectly give EL_PLAYER_1 for "player->element_nr") */
+       int player_element = PLAYERINFO(xx, yy)->initial_element;
+
+       CheckElementChangeBySide(x, y, center_element, player_element,
+                                CE_TOUCHING_X, center_side);
+      }
+#endif
+
       break;
     }
   }
@@ -13492,10 +13838,14 @@ void TestIfElementTouchesCustomElement(int x, int y)
     /* check for change of border element */
     CheckElementChangeBySide(xx, yy, border_element, center_element,
                             CE_TOUCHING_X, center_side);
+
+    /* (center element cannot be player, so we dont have to check this here) */
   }
 
   for (i = 0; i < NUM_DIRECTIONS; i++)
   {
+    int xx = x + xy[i][0];
+    int yy = y + xy[i][1];
     int border_side = trigger_sides[i][1];
     int border_element = border_element_old[i];
 
@@ -13507,6 +13857,20 @@ void TestIfElementTouchesCustomElement(int x, int y)
       change_center_element =
        CheckElementChangeBySide(x, y, center_element, border_element,
                                 CE_TOUCHING_X, border_side);
+
+#if USE_FIX_CE_ACTION_WITH_PLAYER
+    if (IS_PLAYER(xx, yy))
+    {
+      /* use player element that is initially defined in the level playfield,
+        not the player element that corresponds to the runtime player number
+        (example: a level that contains EL_PLAYER_3 as the only player would
+        incorrectly give EL_PLAYER_1 for "player->element_nr") */
+      int player_element = PLAYERINFO(xx, yy)->initial_element;
+
+      CheckElementChangeBySide(x, y, center_element, player_element,
+                              CE_TOUCHING_X, border_side);
+    }
+#endif
   }
 }
 
@@ -13604,11 +13968,25 @@ void TestIfElementHitsCustomElement(int x, int y, int direction)
       CheckElementChangeBySide(x, y, hitting_element, touched_element,
                               CE_HITTING_X, touched_side);
 
-      CheckElementChangeBySide(hitx, hity, touched_element,
-                              hitting_element, CE_HIT_BY_X, hitting_side);
+      CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
+                              CE_HIT_BY_X, hitting_side);
 
       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
                               CE_HIT_BY_SOMETHING, opposite_direction);
+
+#if USE_FIX_CE_ACTION_WITH_PLAYER
+      if (IS_PLAYER(hitx, hity))
+      {
+       /* use player element that is initially defined in the level playfield,
+          not the player element that corresponds to the runtime player number
+          (example: a level that contains EL_PLAYER_3 as the only player would
+          incorrectly give EL_PLAYER_1 for "player->element_nr") */
+       int player_element = PLAYERINFO(hitx, hity)->initial_element;
+
+       CheckElementChangeBySide(x, y, hitting_element, player_element,
+                                CE_HITTING_X, touched_side);
+      }
+#endif
     }
   }
 
@@ -13781,6 +14159,7 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
 
     test_x = bad_x + test_xy[i][0];
     test_y = bad_y + test_xy[i][1];
+
     if (!IN_LEV_FIELD(test_x, test_y))
       continue;
 
@@ -13811,12 +14190,14 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
 
        kill_x = test_x;
        kill_y = test_y;
+
        break;
       }
       else if (test_element == EL_PENGUIN)
       {
        kill_x = test_x;
        kill_y = test_y;
+
        break;
       }
     }
@@ -13839,6 +14220,63 @@ void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
   }
 }
 
+void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
+{
+  int bad_element = Feld[bad_x][bad_y];
+  int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
+  int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
+  int test_x = bad_x + dx, test_y = bad_y + dy;
+  int test_move_dir, test_element;
+  int kill_x = -1, kill_y = -1;
+
+  if (!IN_LEV_FIELD(test_x, test_y))
+    return;
+
+  test_move_dir =
+    (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
+
+  test_element = Feld[test_x][test_y];
+
+  if (test_move_dir != bad_move_dir)
+  {
+    /* good thing can be player or penguin that does not move away */
+    if (IS_PLAYER(test_x, test_y))
+    {
+      struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
+
+      /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
+        player as being hit when he is moving towards the bad thing, because
+        the "get hit by" condition would be lost after the player stops) */
+      if (player->MovPos != 0 && player->MovDir == bad_move_dir)
+       return;         /* player moves away from bad thing */
+
+      kill_x = test_x;
+      kill_y = test_y;
+    }
+    else if (test_element == EL_PENGUIN)
+    {
+      kill_x = test_x;
+      kill_y = test_y;
+    }
+  }
+
+  if (kill_x != -1 || kill_y != -1)
+  {
+    if (IS_PLAYER(kill_x, kill_y))
+    {
+      struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
+
+      if (player->shield_deadly_time_left > 0 &&
+         !IS_INDESTRUCTIBLE(bad_element))
+       Bang(bad_x, bad_y);
+      else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
+       KillPlayer(player);
+    }
+    else
+      Bang(kill_x, kill_y);
+  }
+}
+
 void TestIfPlayerTouchesBadThing(int x, int y)
 {
   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
@@ -13910,6 +14348,11 @@ void KillPlayer(struct PlayerInfo *player)
   if (!player->active)
     return;
 
+#if 0
+  printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
+        player->killed, player->active, player->reanimated);
+#endif
+
   /* the following code was introduced to prevent an infinite loop when calling
      -> Bang()
      -> CheckTriggeredElementChangeExt()
@@ -13934,8 +14377,31 @@ void KillPlayer(struct PlayerInfo *player)
   player->shield_normal_time_left = 0;
   player->shield_deadly_time_left = 0;
 
+#if 0
+  printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
+        player->killed, player->active, player->reanimated);
+#endif
+
   Bang(jx, jy);
+
+#if 0
+  printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
+        player->killed, player->active, player->reanimated);
+#endif
+
+#if USE_PLAYER_REANIMATION
+#if 1
+  if (player->reanimated)      /* killed player may have been reanimated */
+    player->killed = player->reanimated = FALSE;
+  else
+    BuryPlayer(player);
+#else
+  if (player->killed)          /* player may have been reanimated */
+    BuryPlayer(player);
+#endif
+#else
   BuryPlayer(player);
+#endif
 }
 
 static void KillPlayerUnlessEnemyProtected(int x, int y)
@@ -13976,7 +14442,7 @@ void RemovePlayer(struct PlayerInfo *player)
     StorePlayer[jx][jy] = 0;
 
   if (player->is_moving)
-    DrawLevelField(player->last_jx, player->last_jy);
+    TEST_DrawLevelField(player->last_jx, player->last_jy);
 
   for (i = 0; i < MAX_PLAYERS; i++)
     if (stored_player[i].active)
@@ -14534,13 +15000,6 @@ static int DigField(struct PlayerInfo *player,
            CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
       return MP_NO_ACTION;
 
-    if (IS_CUSTOM_ELEMENT(element) &&
-       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
-    {
-      if (!DigFieldByCE(nextx, nexty, element))
-       return MP_NO_ACTION;
-    }
-
     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
       return MP_NO_ACTION;
 
@@ -14558,6 +15017,13 @@ static int DigField(struct PlayerInfo *player,
       return MP_NO_ACTION;
     }
 
+    if (IS_CUSTOM_ELEMENT(element) &&
+       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
+    {
+      if (!DigFieldByCE(nextx, nexty, element))
+       return MP_NO_ACTION;
+    }
+
     if (IS_SB_ELEMENT(element))
     {
       if (element == EL_SOKOBAN_FIELD_FULL)
@@ -14643,7 +15109,7 @@ static int DigField(struct PlayerInfo *player,
 
       game.robot_wheel_active = TRUE;
 
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_SP_TERMINAL)
     {
@@ -14698,7 +15164,7 @@ static int DigField(struct PlayerInfo *player,
       local_player->lights_still_needed--;
 
       ResetGfxAnimation(x, y);
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_TIME_ORB_FULL)
     {
@@ -14718,7 +15184,7 @@ static int DigField(struct PlayerInfo *player,
       }
 
       ResetGfxAnimation(x, y);
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
             element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
@@ -14830,7 +15296,7 @@ static boolean DigFieldByCE(int x, int y, int digging_element)
     else
     {
       RemoveField(x, y);
-      DrawLevelField(x, y);
+      TEST_DrawLevelField(x, y);
     }
 
     /* if digged element was about to explode, prevent the explosion */
@@ -14932,9 +15398,9 @@ static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
   }
 
   if (player->MovPos != 0)     /* prevent graphic bugs in versions < 2.2.0 */
-    DrawLevelField(player->last_jx, player->last_jy);
+    TEST_DrawLevelField(player->last_jx, player->last_jy);
 
-  DrawLevelField(x, y);
+  TEST_DrawLevelField(x, y);
 
   return TRUE;
 }