rnd-20030618-2-src
[rocksndiamonds.git] / src / game.c
index d0ffb3d24e0508366d23eff83488a606c6ab2f2f..218c47f0b59bfc26ce7ec4e5e3ec8a2070500e3b 100644 (file)
@@ -114,6 +114,9 @@ static void CloseAllOpenTimegates(void);
 static void CheckGravityMovement(struct PlayerInfo *);
 static void KillHeroUnlessProtected(int, int);
 
+static void CheckTriggeredElementChange(int, int);
+static void ChangeElementDoIt(int, int, int);
+
 static void PlaySoundLevel(int, int, int);
 static void PlaySoundLevelNearest(int, int, int);
 static void PlaySoundLevelAction(int, int, int);
@@ -300,8 +303,13 @@ static struct ChangingElementInfo changing_element_list[] =
 };
 
 static struct ChangingElementInfo changing_element[MAX_NUM_ELEMENTS];
+static unsigned long trigger_events[MAX_NUM_ELEMENTS];
 
 #define IS_AUTO_CHANGING(e)  (changing_element[e].base_element != EL_UNDEFINED)
+#define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
+#define IS_CHANGING(x, y)      (IS_AUTO_CHANGING(Feld[x][y]) || \
+                                IS_JUST_CHANGING(x, y))
+#define TRIGGERS_BY_COLLECTING(e) (trigger_events[e] & CE_OTHER_COLLECTING)
 
 
 void GetPlayerConfig()
@@ -682,6 +690,16 @@ static void InitGameEngine()
     changing_element[element].change_delay = (change->delay_fixed *
                                              change->delay_frames);
   }
+
+  /* initialize trigger events information */
+  for (i=0; i<MAX_NUM_ELEMENTS; i++)
+    trigger_events[i] = EP_BITMASK_DEFAULT;
+
+  /* add trigger events from element change event properties */
+  for (i=0; i<MAX_NUM_ELEMENTS; i++)
+    if (HAS_CHANGE_EVENT(i, CE_BY_OTHER))
+      trigger_events[element_info[i].change.trigger] |=
+       element_info[i].change.events;
 }
 
 
@@ -1789,7 +1807,11 @@ void Explode(int ex, int ey, int phase, int mode)
       }
 
       Feld[x][y] = EL_EXPLOSION;
+#if 1
+      GfxElement[x][y] = center_element;
+#else
       GfxElement[x][y] = EL_UNDEFINED;
+#endif
       MovDir[x][y] = MovPos[x][y] = 0;
       AmoebaNr[x][y] = 0;
       ExplodePhase[x][y] = 1;
@@ -1844,6 +1866,7 @@ void Explode(int ex, int ey, int phase, int mode)
 
     element = Feld[x][y] = Store[x][y];
     Store[x][y] = Store2[x][y] = 0;
+    GfxElement[x][y] = EL_UNDEFINED;
 
     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
       element = Feld[x][y] = Back[x][y];
@@ -1860,10 +1883,14 @@ void Explode(int ex, int ey, int phase, int mode)
   }
   else if (phase >= delay && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
   {
+#if 1
+    int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
+#else
     int stored = Store[x][y];
     int graphic = (game.emulation != EMU_SUPAPLEX ? IMG_EXPLOSION :
                   stored == EL_SP_INFOTRON ? IMG_SP_EXPLOSION_INFOTRON :
                   IMG_SP_EXPLOSION);
+#endif
     int frame = getGraphicAnimationFrame(graphic, phase - delay);
 
     if (phase == delay)
@@ -1940,10 +1967,22 @@ void Bang(int x, int y)
 {
   int element = Feld[x][y];
 
+  if (IS_PLAYER(x, y))
+  {
+    struct PlayerInfo *player = PLAYERINFO(x, y);
+
+    element = Feld[x][y] = (player->use_murphy_graphic ? EL_SP_MURPHY :
+                           player->element_nr);
+  }
+
+#if 1
+  PlaySoundLevelAction(x, y, ACTION_EXPLODING);
+#else
   if (game.emulation == EMU_SUPAPLEX)
     PlaySoundLevel(x, y, SND_SP_ELEMENT_EXPLODING);
   else
     PlaySoundLevel(x, y, SND_ELEMENT_EXPLODING);
+#endif
 
 #if 0
   if (IS_PLAYER(x, y)) /* remove objects that might cause smaller explosion */
@@ -1985,6 +2024,8 @@ void Bang(int x, int y)
       Explode(x, y, EX_PHASE_START, EX_NORMAL);
       break;
   }
+
+  CheckTriggeredElementChange(element, CE_OTHER_EXPLODING);
 }
 
 void SplashAcid(int x, int y)
@@ -2325,14 +2366,14 @@ void Impact(int x, int y)
 
   if (!lastline)       /* check if element below was hit */
   {
-    if (Feld[x][y+1] == EL_PLAYER_IS_LEAVING)
+    if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
       return;
 
-    object_hit = (!IS_FREE(x, y+1) && (!IS_MOVING(x, y+1) ||
-                                      MovDir[x][y+1] != MV_DOWN ||
-                                      MovPos[x][y+1] <= TILEY / 2));
+    object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
+                                        MovDir[x][y + 1] != MV_DOWN ||
+                                        MovPos[x][y + 1] <= TILEY / 2));
     if (object_hit)
-      smashed = MovingOrBlocked2Element(x, y+1);
+      smashed = MovingOrBlocked2Element(x, y + 1);
 
     impact = (lastline || object_hit);
   }
@@ -2367,11 +2408,20 @@ void Impact(int x, int y)
     PlaySoundLevel(x, y, SND_PEARL_BREAKING);
     return;
   }
+  else if (impact && CAN_CHANGE(element) &&
+          HAS_CHANGE_EVENT(element, CE_IMPACT))
+  {
+    PlaySoundLevelElementAction(x, y, element, ACTION_IMPACT);
+
+    ChangeElementDoIt(x, y, element_info[element].change.successor);
+
+    return;
+  }
 
   if (impact && element == EL_AMOEBA_DROP)
   {
-    if (object_hit && IS_PLAYER(x, y+1))
-      KillHeroUnlessProtected(x, y+1);
+    if (object_hit && IS_PLAYER(x, y + 1))
+      KillHeroUnlessProtected(x, y + 1);
     else if (object_hit && smashed == EL_PENGUIN)
       Bang(x, y + 1);
     else
@@ -2417,7 +2467,7 @@ void Impact(int x, int y)
     {
       if (CAN_SMASH_PLAYER(element))
       {
-       KillHeroUnlessProtected(x, y+1);
+       KillHeroUnlessProtected(x, y + 1);
        return;
       }
     }
@@ -2484,40 +2534,45 @@ void Impact(int x, int y)
        }
        else if (smashed == EL_NUT)
        {
-         Feld[x][y+1] = EL_NUT_BREAKING;
+         Feld[x][y + 1] = EL_NUT_BREAKING;
          PlaySoundLevel(x, y, SND_NUT_BREAKING);
          RaiseScoreElement(EL_NUT);
          return;
        }
        else if (smashed == EL_PEARL)
        {
-         Feld[x][y+1] = EL_PEARL_BREAKING;
+         Feld[x][y + 1] = EL_PEARL_BREAKING;
          PlaySoundLevel(x, y, SND_PEARL_BREAKING);
          return;
        }
        else if (smashed == EL_DIAMOND)
        {
 #if 1
-         Feld[x][y+1] = EL_DIAMOND_BREAKING;
+         Feld[x][y + 1] = EL_DIAMOND_BREAKING;
 #else
-         Feld[x][y+1] = EL_EMPTY;
+         Feld[x][y + 1] = EL_EMPTY;
 #endif
          PlaySoundLevel(x, y, SND_DIAMOND_BREAKING);
          return;
        }
        else if (IS_BELT_SWITCH(smashed))
        {
-         ToggleBeltSwitch(x, y+1);
+         ToggleBeltSwitch(x, y + 1);
        }
        else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
                 smashed == EL_SWITCHGATE_SWITCH_DOWN)
        {
-         ToggleSwitchgateSwitch(x, y+1);
+         ToggleSwitchgateSwitch(x, y + 1);
        }
        else if (smashed == EL_LIGHT_SWITCH ||
                 smashed == EL_LIGHT_SWITCH_ACTIVE)
        {
-         ToggleLightSwitch(x, y+1);
+         ToggleLightSwitch(x, y + 1);
+       }
+       else if (CAN_CHANGE(smashed) &&
+                HAS_CHANGE_EVENT(smashed, CE_SMASHED))
+       {
+         ChangeElementDoIt(x, y + 1, element_info[smashed].change.successor);
        }
       }
     }
@@ -2525,12 +2580,12 @@ void Impact(int x, int y)
 
   /* play sound of magic wall / mill */
   if (!lastline &&
-      (Feld[x][y+1] == EL_MAGIC_WALL_ACTIVE ||
-       Feld[x][y+1] == EL_BD_MAGIC_WALL_ACTIVE))
+      (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
+       Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
   {
-    if (Feld[x][y+1] == EL_MAGIC_WALL_ACTIVE)
+    if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
       PlaySoundLevel(x, y, SND_MAGIC_WALL_FILLING);
-    else if (Feld[x][y+1] == EL_BD_MAGIC_WALL_ACTIVE)
+    else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
       PlaySoundLevel(x, y, SND_BD_MAGIC_WALL_FILLING);
 
     return;
@@ -3145,7 +3200,7 @@ void StartMoving(int x, int y)
 
     if (element == EL_QUICKSAND_FULL)
     {
-      if (IS_FREE(x, y+1))
+      if (IS_FREE(x, y + 1))
       {
        InitMovingField(x, y, MV_DOWN);
        started_moving = TRUE;
@@ -3158,7 +3213,7 @@ void StartMoving(int x, int y)
        PlaySoundLevel(x, y, SND_QUICKSAND_EMPTYING);
 #endif
       }
-      else if (Feld[x][y+1] == EL_QUICKSAND_EMPTY)
+      else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
       {
        if (!MovDelay[x][y])
          MovDelay[x][y] = TILEY + 1;
@@ -3171,8 +3226,8 @@ void StartMoving(int x, int y)
        }
 
        Feld[x][y] = EL_QUICKSAND_EMPTY;
-       Feld[x][y+1] = EL_QUICKSAND_FULL;
-       Store[x][y+1] = Store[x][y];
+       Feld[x][y + 1] = EL_QUICKSAND_FULL;
+       Store[x][y + 1] = Store[x][y];
        Store[x][y] = 0;
 #if 1
        PlaySoundLevelAction(x, y, ACTION_FILLING);
@@ -3182,7 +3237,7 @@ void StartMoving(int x, int y)
       }
     }
     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
-            Feld[x][y+1] == EL_QUICKSAND_EMPTY)
+            Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
     {
       InitMovingField(x, y, MV_DOWN);
       started_moving = TRUE;
@@ -3197,7 +3252,7 @@ void StartMoving(int x, int y)
     }
     else if (element == EL_MAGIC_WALL_FULL)
     {
-      if (IS_FREE(x, y+1))
+      if (IS_FREE(x, y + 1))
       {
        InitMovingField(x, y, MV_DOWN);
        started_moving = TRUE;
@@ -3205,7 +3260,7 @@ void StartMoving(int x, int y)
        Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
        Store[x][y] = EL_CHANGED(Store[x][y]);
       }
-      else if (Feld[x][y+1] == EL_MAGIC_WALL_ACTIVE)
+      else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
       {
        if (!MovDelay[x][y])
          MovDelay[x][y] = TILEY/4 + 1;
@@ -3218,14 +3273,14 @@ void StartMoving(int x, int y)
        }
 
        Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
-       Feld[x][y+1] = EL_MAGIC_WALL_FULL;
-       Store[x][y+1] = EL_CHANGED(Store[x][y]);
+       Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
+       Store[x][y + 1] = EL_CHANGED(Store[x][y]);
        Store[x][y] = 0;
       }
     }
     else if (element == EL_BD_MAGIC_WALL_FULL)
     {
-      if (IS_FREE(x, y+1))
+      if (IS_FREE(x, y + 1))
       {
        InitMovingField(x, y, MV_DOWN);
        started_moving = TRUE;
@@ -3233,7 +3288,7 @@ void StartMoving(int x, int y)
        Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
        Store[x][y] = EL_CHANGED2(Store[x][y]);
       }
-      else if (Feld[x][y+1] == EL_BD_MAGIC_WALL_ACTIVE)
+      else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
       {
        if (!MovDelay[x][y])
          MovDelay[x][y] = TILEY/4 + 1;
@@ -3246,27 +3301,27 @@ void StartMoving(int x, int y)
        }
 
        Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
-       Feld[x][y+1] = EL_BD_MAGIC_WALL_FULL;
-       Store[x][y+1] = EL_CHANGED2(Store[x][y]);
+       Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
+       Store[x][y + 1] = EL_CHANGED2(Store[x][y]);
        Store[x][y] = 0;
       }
     }
     else if (CAN_PASS_MAGIC_WALL(element) &&
-            (Feld[x][y+1] == EL_MAGIC_WALL_ACTIVE ||
-             Feld[x][y+1] == EL_BD_MAGIC_WALL_ACTIVE))
+            (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
+             Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE))
     {
       InitMovingField(x, y, MV_DOWN);
       started_moving = TRUE;
 
       Feld[x][y] =
-       (Feld[x][y+1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
+       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
         EL_BD_MAGIC_WALL_FILLING);
       Store[x][y] = element;
     }
 #if 0
-    else if (CAN_SMASH(element) && Feld[x][y+1] == EL_ACID)
+    else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_ACID)
 #else
-    else if (CAN_FALL(element) && Feld[x][y+1] == EL_ACID)
+    else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
 #endif
     {
       SplashAcid(x, y);
@@ -3277,15 +3332,15 @@ void StartMoving(int x, int y)
       Store[x][y] = EL_ACID;
 #if 0
       /* !!! TEST !!! better use "_FALLING" etc. !!! */
-      GfxAction[x][y+1] = ACTION_ACTIVE;
+      GfxAction[x][y + 1] = ACTION_ACTIVE;
 #endif
     }
-    else if (CAN_SMASH(element) && Feld[x][y+1] == EL_BLOCKED &&
+    else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
             JustStopped[x][y])
     {
       Impact(x, y);
     }
-    else if (IS_FREE(x, y+1) && element == EL_SPRING && use_spring_bug)
+    else if (IS_FREE(x, y + 1) && element == EL_SPRING && use_spring_bug)
     {
       if (MovDir[x][y] == MV_NO_MOVING)
       {
@@ -3293,7 +3348,7 @@ void StartMoving(int x, int y)
        started_moving = TRUE;
       }
     }
-    else if (IS_FREE(x, y+1) || Feld[x][y+1] == EL_DIAMOND_BREAKING)
+    else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
     {
       if (JustStopped[x][y])   /* prevent animation from being restarted */
        MovDir[x][y] = MV_DOWN;
@@ -3306,28 +3361,28 @@ void StartMoving(int x, int y)
       Feld[x][y] = EL_AMOEBA_GROWING;
       Store[x][y] = EL_AMOEBA_WET;
     }
-    /* Store[x][y+1] must be zero, because:
-       (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y+1] == EL_QUICKSAND_EMPTY
+    /* Store[x][y + 1] must be zero, because:
+       (EL_QUICKSAND_FULL -> EL_ROCK): Store[x][y + 1] == EL_QUICKSAND_EMPTY
     */
 #if 0
 #if OLD_GAME_BEHAVIOUR
-    else if (IS_SLIPPERY(Feld[x][y+1]) && !Store[x][y+1])
+    else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1])
 #else
-    else if (IS_SLIPPERY(Feld[x][y+1]) && !Store[x][y+1] &&
-            !IS_FALLING(x, y+1) && !JustStopped[x][y+1] &&
+    else if (IS_SLIPPERY(Feld[x][y + 1]) && !Store[x][y + 1] &&
+            !IS_FALLING(x, y + 1) && !JustStopped[x][y + 1] &&
             element != EL_DX_SUPABOMB)
 #endif
 #else
-    else if ((IS_SLIPPERY(Feld[x][y+1]) ||
-             (IS_EM_SLIPPERY_WALL(Feld[x][y+1]) && IS_GEM(element))) &&
-            !IS_FALLING(x, y+1) && !JustStopped[x][y+1] &&
+    else if ((IS_SLIPPERY(Feld[x][y + 1]) ||
+             (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
+            !IS_FALLING(x, y + 1) && !JustStopped[x][y + 1] &&
             element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
 #endif
     {
       boolean left  = (x>0 && IS_FREE(x-1, y) &&
-                      (IS_FREE(x-1, y+1) || Feld[x-1][y+1] == EL_ACID));
+                      (IS_FREE(x-1, y + 1) || Feld[x-1][y + 1] == EL_ACID));
       boolean right = (x<lev_fieldx-1 && IS_FREE(x+1, y) &&
-                      (IS_FREE(x+1, y+1) || Feld[x+1][y+1] == EL_ACID));
+                      (IS_FREE(x+1, y + 1) || Feld[x+1][y + 1] == EL_ACID));
 
       if (left || right)
       {
@@ -3340,11 +3395,11 @@ void StartMoving(int x, int y)
        started_moving = TRUE;
       }
     }
-    else if (IS_BELT_ACTIVE(Feld[x][y+1]))
+    else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
     {
       boolean left_is_free  = (x>0 && IS_FREE(x-1, y));
       boolean right_is_free = (x<lev_fieldx-1 && IS_FREE(x+1, y));
-      int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y+1]);
+      int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
       int belt_dir = game.belt_dir[belt_nr];
 
       if ((belt_dir == MV_LEFT  && left_is_free) ||
@@ -3896,9 +3951,15 @@ void ContinueMoving(int x, int y)
     else if (element == EL_PENGUIN)
       TestIfFriendTouchesBadThing(newx, newy);
 
+#if 1
+    if (CAN_FALL(element) && direction == MV_DOWN &&
+       (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
+      Impact(x, newy);
+#else
     if (CAN_SMASH(element) && direction == MV_DOWN &&
        (newy == lev_fieldy - 1 || !IS_FREE(x, newy + 1)))
       Impact(x, newy);
+#endif
   }
   else                         /* still moving on */
   {
@@ -4790,6 +4851,52 @@ static void ChangeActiveTrap(int x, int y)
     DrawLevelFieldCrumbledSand(x, y);
 }
 
+static void ChangeElementDoIt(int x, int y, int element_new)
+{
+  CheckTriggeredElementChange(Feld[x][y], CE_OTHER_CHANGING);
+
+  RemoveField(x, y);
+  Feld[x][y] = element_new;
+
+  ResetGfxAnimation(x, y);
+  ResetRandomAnimationValue(x, y);
+
+  InitField(x, y, FALSE);
+  if (CAN_MOVE(Feld[x][y]))
+    InitMovDir(x, y);
+
+  DrawLevelField(x, y);
+
+  if (CAN_BE_CRUMBLED(Feld[x][y]))
+  {
+    int sx = SCREENX(x), sy = SCREENY(y);
+    static int xy[4][2] =
+    {
+      { 0, -1 },
+      { -1, 0 },
+      { +1, 0 },
+      { 0, +1 }
+    };
+    int i;
+
+    for(i=0; i<4; i++)
+    {
+      int xx = x + xy[i][0];
+      int yy = y + xy[i][1];
+      int sxx = sx + xy[i][0];
+      int syy = sy + xy[i][1];
+
+      if (!IN_LEV_FIELD(xx, yy) ||
+         !IN_SCR_FIELD(sxx, syy) ||
+         !CAN_BE_CRUMBLED(Feld[xx][yy]) ||
+         IS_MOVING(xx, yy))
+       continue;
+
+      DrawLevelField(xx, yy);
+    }
+  }
+}
+
 static void ChangeElement(int x, int y)
 {
   int element = Feld[x][y];
@@ -4827,6 +4934,8 @@ static void ChangeElement(int x, int y)
   }
   else                                 /* finish element change */
   {
+    int next_element = changing_element[element].next_element;
+
     if (IS_MOVING(x, y))               /* never change a running system ;-) */
     {
       ChangeDelay[x][y] = 1;           /* try change after next move step */
@@ -4834,49 +4943,37 @@ static void ChangeElement(int x, int y)
       return;
     }
 
-    RemoveField(x, y);
-    Feld[x][y] = changing_element[element].next_element;
+    if (next_element != EL_UNDEFINED)
+      ChangeElementDoIt(x, y, next_element);
+    else
+      ChangeElementDoIt(x, y, element_info[element].change.successor);
 
-    ResetGfxAnimation(x, y);
-    ResetRandomAnimationValue(x, y);
+    if (changing_element[element].post_change_function)
+      changing_element[element].post_change_function(x, y);
+  }
+}
 
-    InitField(x, y, FALSE);
-    if (CAN_MOVE(Feld[x][y]))
-      InitMovDir(x, y);
+static void CheckTriggeredElementChange(int trigger_element, int trigger_event)
+{
+  int i, x, y;
 
-    DrawLevelField(x, y);
+  if (!(trigger_events[trigger_element] & CH_EVENT_BIT(trigger_event)))
+    return;
 
-    if (CAN_BE_CRUMBLED(Feld[x][y]))
-    {
-      int sx = SCREENX(x), sy = SCREENY(y);
-      static int xy[4][2] =
-      {
-       { 0, -1 },
-       { -1, 0 },
-       { +1, 0 },
-       { 0, +1 }
-      };
-      int i;
+  for (i=0; i<MAX_NUM_ELEMENTS; i++)
+  {
+    if (!HAS_CHANGE_EVENT(i, trigger_event) ||
+       element_info[i].change.trigger != trigger_element)
+      continue;
 
-      for(i=0; i<4; i++)
+    for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
+    {
+      if (Feld[x][y] == i)
       {
-       int xx = x + xy[i][0];
-       int yy = y + xy[i][1];
-       int sxx = sx + xy[i][0];
-       int syy = sy + xy[i][1];
-
-       if (!IN_LEV_FIELD(xx, yy) ||
-           !IN_SCR_FIELD(sxx, syy) ||
-           !CAN_BE_CRUMBLED(Feld[xx][yy]) ||
-           IS_MOVING(xx, yy))
-         continue;
-
-       DrawLevelField(xx, yy);
+       ChangeDelay[x][y] = 1;
+       ChangeElement(x, y);
       }
     }
-
-    if (changing_element[element].post_change_function)
-      changing_element[element].post_change_function(x, y);
   }
 }
 
@@ -5112,7 +5209,7 @@ void GameActions()
 
 #if 1
     /* this may take place after moving, so 'element' may have changed */
-    if (IS_AUTO_CHANGING(element))
+    if (IS_CHANGING(x, y))
     {
       ChangeElement(x, y);
       element = Feld[x][y];
@@ -5202,7 +5299,7 @@ void GameActions()
 #endif
     else if (element == EL_EXPLOSION)
       ;        /* drawing of correct explosion animation is handled separately */
-    else if (IS_ANIMATED(graphic) && !IS_AUTO_CHANGING(element))
+    else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
 
 #if 0
@@ -6140,7 +6237,11 @@ void BuryHero(struct PlayerInfo *player)
   if (!player->active)
     return;
 
+#if 1
+  PlaySoundLevelElementAction(jx, jy, player->element_nr, ACTION_DYING);
+#else
   PlaySoundLevel(jx, jy, SND_CLASS_PLAYER_DYING);
+#endif
   PlaySoundLevel(jx, jy, SND_GAME_LOSING);
 
   player->GameOver = TRUE;
@@ -6279,7 +6380,13 @@ int DigField(struct PlayerInfo *player,
 
   switch (element)
   {
+#if 0
     case EL_EMPTY:
+      PlaySoundLevelElementAction(x, y, player->element_nr, ACTION_MOVING);
+      break;
+#endif
+
+#if 0
     case EL_SAND:
     case EL_INVISIBLE_SAND:
     case EL_INVISIBLE_SAND_ACTIVE:
@@ -6288,15 +6395,16 @@ int DigField(struct PlayerInfo *player,
     case EL_SP_BUGGY_BASE:
     case EL_SP_BUGGY_BASE_ACTIVATING:
       RemoveField(x, y);
-#if 1
+
       if (mode != DF_SNAP && element != EL_EMPTY)
       {
        GfxElement[x][y] = (CAN_BE_CRUMBLED(element) ? EL_SAND : element);
        player->is_digging = TRUE;
       }
-#endif
+
       PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING);
       break;
+#endif
 
     case EL_EMERALD:
     case EL_BD_DIAMOND:
@@ -6308,13 +6416,13 @@ int DigField(struct PlayerInfo *player,
     case EL_PEARL:
     case EL_CRYSTAL:
       RemoveField(x, y);
-#if 1
+
       if (mode != DF_SNAP)
       {
        GfxElement[x][y] = element;
        player->is_collecting = TRUE;
       }
-#endif
+
       local_player->gems_still_needed -= (element == EL_DIAMOND ? 3 :
                                          element == EL_PEARL ? 5 :
                                          element == EL_CRYSTAL ? 8 : 1);
@@ -6324,18 +6432,31 @@ int DigField(struct PlayerInfo *player,
       DrawText(DX_EMERALDS, DY_EMERALDS,
               int2str(local_player->gems_still_needed, 3), FONT_TEXT_2);
       PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
 
     case EL_SPEED_PILL:
       RemoveField(x, y);
       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundLevel(x, y, SND_SPEED_PILL_COLLECTING);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
 
+#if 0
     case EL_ENVELOPE:
       Feld[x][y] = EL_EMPTY;
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundLevel(x, y, SND_ENVELOPE_COLLECTING);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
+#endif
 
     case EL_EXTRA_TIME:
       RemoveField(x, y);
@@ -6344,20 +6465,35 @@ int DigField(struct PlayerInfo *player,
        TimeLeft += 10;
        DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FONT_TEXT_2);
       }
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundStereo(SND_EXTRA_TIME_COLLECTING, SOUND_MIDDLE);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
 
     case EL_SHIELD_NORMAL:
       RemoveField(x, y);
       player->shield_normal_time_left += 10;
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundLevel(x, y, SND_SHIELD_NORMAL_COLLECTING);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
 
     case EL_SHIELD_DEADLY:
       RemoveField(x, y);
       player->shield_normal_time_left += 10;
       player->shield_deadly_time_left += 10;
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundLevel(x, y, SND_SHIELD_DEADLY_COLLECTING);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
 
     case EL_DYNAMITE:
@@ -6369,6 +6505,7 @@ int DigField(struct PlayerInfo *player,
       DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(local_player->dynamite, 3),
               FONT_TEXT_2);
       PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
 
     case EL_DYNABOMB_INCREASE_NUMBER:
@@ -6376,21 +6513,36 @@ int DigField(struct PlayerInfo *player,
       player->dynabomb_count++;
       player->dynabombs_left++;
       RaiseScoreElement(EL_DYNAMITE);
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundLevel(x, y, SND_DYNABOMB_INCREASE_NUMBER_COLLECTING);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
 
     case EL_DYNABOMB_INCREASE_SIZE:
       RemoveField(x, y);
       player->dynabomb_size++;
       RaiseScoreElement(EL_DYNAMITE);
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundLevel(x, y, SND_DYNABOMB_INCREASE_SIZE_COLLECTING);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
 
     case EL_DYNABOMB_INCREASE_POWER:
       RemoveField(x, y);
       player->dynabomb_xl = TRUE;
       RaiseScoreElement(EL_DYNAMITE);
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundLevel(x, y, SND_DYNABOMB_INCREASE_POWER_COLLECTING);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
 
     case EL_KEY_1:
@@ -6408,7 +6560,12 @@ int DigField(struct PlayerInfo *player,
                         graphic);
       DrawMiniGraphicExt(window, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
                         graphic);
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundLevel(x, y, SND_CLASS_KEY_COLLECTING);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
     }
 
@@ -6427,7 +6584,12 @@ int DigField(struct PlayerInfo *player,
                         graphic);
       DrawMiniGraphicExt(window, DX_KEYS + key_nr * MINI_TILEX, DY_KEYS,
                         graphic);
+#if 1
+      PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
+#else
       PlaySoundLevel(x, y, SND_CLASS_KEY_COLLECTING);
+#endif
+      CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
       break;
     }
 
@@ -6599,6 +6761,9 @@ int DigField(struct PlayerInfo *player,
 
       DrawLevelField(x + dx, y + dy);
       PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
+
+      CheckTriggeredElementChange(element, CE_OTHER_PUSHING);
+
       break;
 
     case EL_GATE_1:
@@ -6785,8 +6950,10 @@ int DigField(struct PlayerInfo *player,
       return MF_ACTION;
       break;
 
+#if 0
     case EL_SOKOBAN_FIELD_EMPTY:
       break;
+#endif
 
     case EL_SOKOBAN_OBJECT:
     case EL_SOKOBAN_FIELD_FULL:
@@ -6884,6 +7051,8 @@ int DigField(struct PlayerInfo *player,
        PlaySoundLevel(x, y, SND_GAME_SOKOBAN_SOLVING);
       }
 
+      CheckTriggeredElementChange(element, CE_OTHER_PUSHING);
+
       break;
 
     case EL_PENGUIN:
@@ -6895,19 +7064,20 @@ int DigField(struct PlayerInfo *player,
 
       if (IS_WALKABLE(element))
       {
+       PlaySoundLevelElementAction(x, y, player->element_nr, ACTION_MOVING);
        break;
       }
       else if (IS_DIGGABLE(element))
       {
        RemoveField(x, y);
-#if 1
+
        if (mode != DF_SNAP)
        {
          GfxElement[x][y] =
            (CAN_BE_CRUMBLED(element) ? EL_SAND : GFX_ELEMENT(element));
          player->is_digging = TRUE;
        }
-#endif
+
        PlaySoundLevelElementAction(x, y, element, ACTION_DIGGING);
 
        break;
@@ -6915,15 +7085,17 @@ int DigField(struct PlayerInfo *player,
       else if (IS_COLLECTIBLE(element))
       {
        RemoveField(x, y);
-#if 1
+
        if (mode != DF_SNAP)
        {
          GfxElement[x][y] = element;
          player->is_collecting = TRUE;
        }
-#endif
+
        PlaySoundLevelElementAction(x, y, element, ACTION_COLLECTING);
 
+       CheckTriggeredElementChange(element, CE_OTHER_COLLECTING);
+
        break;
       }
       else if (IS_PUSHABLE(element))
@@ -6966,6 +7138,8 @@ int DigField(struct PlayerInfo *player,
        DrawLevelField(x + dx, y + dy);
        PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
 
+       CheckTriggeredElementChange(element, CE_OTHER_PUSHING);
+
        break;
       }