rnd-19990822-1-src
[rocksndiamonds.git] / src / game.c
index 78b950c28033f86c51620e6341ab11c62863561b..fc0d64767d09631bef0d1fb55345d7ab2c7b26f2 100644 (file)
@@ -23,6 +23,9 @@
 #include "joystick.h"
 #include "network.h"
 
+/* this switch controls how rocks move horizontally */
+#define OLD_GAME_BEHAVIOUR     FALSE
+
 /* for DigField() */
 #define DF_NO_PUSH             0
 #define DF_DIG                 1
@@ -144,7 +147,7 @@ static unsigned int getStateCheckSum(int counter)
     checksum += mult++ * StorePlayer[x][y];
     checksum += mult++ * Frame[x][y];
     checksum += mult++ * AmoebaNr[x][y];
-    checksum += mult++ * JustHit[x][y];
+    checksum += mult++ * JustStopped[x][y];
     checksum += mult++ * Stop[x][y];
     */
   }
@@ -176,6 +179,50 @@ void GetPlayerConfig()
   InitJoysticks();
 }
 
+static int getBeltNrFromElement(int element)
+{
+  return (element < EL_BELT2_LEFT ? 0 :
+         element < EL_BELT3_LEFT ? 1 :
+         element < EL_BELT4_LEFT ? 2 : 3);
+}
+
+static int getBeltNrFromSwitchElement(int element)
+{
+  return (element < EL_BELT2_SWITCH_L ? 0 :
+         element < EL_BELT3_SWITCH_L ? 1 :
+         element < EL_BELT4_SWITCH_L ? 2 : 3);
+}
+
+static int getBeltDirNrFromSwitchElement(int element)
+{
+  static int belt_base_element[4] =
+  {
+    EL_BELT1_SWITCH_L,
+    EL_BELT2_SWITCH_L,
+    EL_BELT3_SWITCH_L,
+    EL_BELT4_SWITCH_L
+  };
+
+  int belt_nr = getBeltNrFromSwitchElement(element);
+  int belt_dir_nr = element - belt_base_element[belt_nr];
+
+  return (belt_dir_nr % 3);
+}
+
+static int getBeltDirFromSwitchElement(int element)
+{
+  static int belt_move_dir[3] =
+  {
+    MV_LEFT,
+    MV_NO_MOVING,
+    MV_RIGHT
+  };
+
+  int belt_dir_nr = getBeltDirNrFromSwitchElement(element);
+
+  return belt_move_dir[belt_dir_nr];
+}
+
 static void InitField(int x, int y, boolean init_game)
 {
   switch (Feld[x][y])
@@ -280,7 +327,7 @@ static void InitField(int x, int y, boolean init_game)
       }
       break;
 
-    case EL_DYNAMIT:
+    case EL_DYNAMITE_ACTIVE:
       MovDelay[x][y] = 96;
       break;
 
@@ -319,6 +366,46 @@ static void InitField(int x, int y, boolean init_game)
       Feld[x][y] = EL_EM_KEY_4;
       break;
 
+    case EL_BELT1_SWITCH_L:
+    case EL_BELT1_SWITCH_M:
+    case EL_BELT1_SWITCH_R:
+    case EL_BELT2_SWITCH_L:
+    case EL_BELT2_SWITCH_M:
+    case EL_BELT2_SWITCH_R:
+    case EL_BELT3_SWITCH_L:
+    case EL_BELT3_SWITCH_M:
+    case EL_BELT3_SWITCH_R:
+    case EL_BELT4_SWITCH_L:
+    case EL_BELT4_SWITCH_M:
+    case EL_BELT4_SWITCH_R:
+      if (init_game)
+      {
+       int belt_nr = getBeltNrFromSwitchElement(Feld[x][y]);
+       int belt_dir = getBeltDirFromSwitchElement(Feld[x][y]);
+       int belt_dir_nr = getBeltDirNrFromSwitchElement(Feld[x][y]);
+
+       if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
+       {
+         game.belt_dir[belt_nr] = belt_dir;
+         game.belt_dir_nr[belt_nr] = belt_dir_nr;
+       }
+       else    /* more than one switch -- set it like the first switch */
+       {
+         Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
+       }
+      }
+      break;
+
+    case EL_SWITCHGATE_SWITCH_2:       /* always start with same switch pos */
+      if (init_game)
+       Feld[x][y] = EL_SWITCHGATE_SWITCH_1;
+      break;
+
+    case EL_LIGHT_SWITCH_ON:
+      if (init_game)
+       game.light_time_left = 10 * FRAMES_PER_SECOND;
+      break;
+
     default:
       break;
   }
@@ -359,13 +446,14 @@ void InitGame()
 
     player->dynamite = 0;
     player->dynabomb_count = 0;
-    player->dynabomb_size = 0;
+    player->dynabomb_size = 1;
     player->dynabombs_left = 0;
     player->dynabomb_xl = FALSE;
 
     player->MovDir = MV_NO_MOVING;
     player->MovPos = 0;
     player->Pushing = FALSE;
+    player->Switching = FALSE;
     player->GfxPos = 0;
     player->Frame = 0;
 
@@ -419,6 +507,13 @@ void InitGame()
   AllPlayersGone = FALSE;
   game.magic_wall_active = FALSE;
   game.magic_wall_time_left = 0;
+  game.switchgate_pos = 0;
+  game.light_time_left = 0;
+  for (i=0; i<4; i++)
+  {
+    game.belt_dir[i] = MV_NO_MOVING;
+    game.belt_dir_nr[i] = 3;           /* not moving, next moving left */
+  }
 
   for (i=0; i<MAX_NUM_AMOEBA; i++)
     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
@@ -432,7 +527,7 @@ void InitGame()
       Store[x][y] = Store2[x][y] = StorePlayer[x][y] = 0;
       Frame[x][y] = 0;
       AmoebaNr[x][y] = 0;
-      JustHit[x][y] = 0;
+      JustStopped[x][y] = 0;
       Stop[x][y] = FALSE;
     }
   }
@@ -452,6 +547,11 @@ void InitGame()
     }
   }
 
+  /* correct non-moving belts to start moving left */
+  for (i=0; i<4; i++)
+    if (game.belt_dir[i] == MV_NO_MOVING)
+      game.belt_dir_nr[i] = 3;         /* not moving, next moving left */
+
   /* check if any connected player was not found in playfield */
   for (i=0; i<MAX_PLAYERS; i++)
   {
@@ -588,18 +688,12 @@ void InitGame()
   DrawAllPlayers();
   FadeToFront();
 
-
-#if 1
-
   if (setup.soft_scrolling)
     XCopyArea(display, fieldbuffer, backbuffer, gc,
              FX, FY, SXSIZE, SYSIZE, SX, SY);
 
   redraw_mask |= REDRAW_FROM_BACKBUFFER;
 
-#endif
-
-
   /* copy default game door content to main double buffer */
   XCopyArea(display, pix[PIX_DOOR], drawto, gc,
            DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
@@ -608,11 +702,17 @@ void InitGame()
     DrawText(DX + XX_LEVEL, DY + YY_LEVEL,
             int2str(level_nr, 2), FS_SMALL, FC_YELLOW);
   else
-    DrawText(DX + XX_LEVEL - 1, DY + YY_LEVEL + 1,
-            int2str(level_nr, 3), FS_SMALL, FC_SPECIAL3);
+  {
+    DrawTextExt(drawto, gc, DX + XX_EMERALDS, DY + YY_EMERALDS,
+               int2str(level_nr, 3), FS_SMALL, FC_SPECIAL3);
+    XCopyArea(display, drawto, drawto, gc,
+             DX + XX_EMERALDS, DY + YY_EMERALDS + 1,
+             FONT5_XSIZE * 3, FONT5_YSIZE - 1,
+             DX + XX_LEVEL - 1, DY + YY_LEVEL + 1);
+  }
 
   DrawText(DX + XX_EMERALDS, DY + YY_EMERALDS,
-          int2str(local_player->gems_still_needed,3), FS_SMALL, FC_YELLOW);
+          int2str(local_player->gems_still_needed, 3), FS_SMALL, FC_YELLOW);
   DrawText(DX + XX_DYNAMITE, DY + YY_DYNAMITE,
           int2str(local_player->dynamite, 3), FS_SMALL, FC_YELLOW);
   DrawText(DX + XX_SCORE, DY + YY_SCORE,
@@ -897,15 +997,16 @@ int NewHiScore()
 #ifdef ONE_PER_NAME
       put_into_list:
 #endif
-      strncpy(highscore[k].Name, setup.player_name, MAX_NAMELEN - 1);
-      highscore[k].Name[MAX_NAMELEN - 1] = '\0';
+      strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
+      highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
       highscore[k].Score = local_player->score; 
       position = k;
       break;
     }
 
 #ifdef ONE_PER_NAME
-    else if (!strncmp(setup.player_name, highscore[k].Name, MAX_NAMELEN - 1))
+    else if (!strncmp(setup.player_name, highscore[k].Name,
+                     MAX_PLAYER_NAME_LEN))
       break;   /* player already there with a higher score */
 #endif
 
@@ -1054,7 +1155,7 @@ void DrawDynamite(int x, int y)
   if (Store[x][y])
     DrawGraphic(sx, sy, el2gfx(Store[x][y]));
 
-  if (Feld[x][y] == EL_DYNAMIT)
+  if (Feld[x][y] == EL_DYNAMITE_ACTIVE)
   {
     if ((phase = (96 - MovDelay[x][y]) / 12) > 6)
       phase = 6;
@@ -1083,10 +1184,13 @@ void CheckDynamite(int x, int y)
       if (!(MovDelay[x][y] % 12))
        PlaySoundLevel(x, y, SND_ZISCH);
 
-      if (Feld[x][y] == EL_DYNAMIT && !(MovDelay[x][y] % 12))
-       DrawDynamite(x, y);
-      else if (Feld[x][y] == EL_DYNABOMB && !(MovDelay[x][y] % 6))
-       DrawDynamite(x, y);
+      if (IS_ACTIVE_BOMB(Feld[x][y]))
+      {
+       int delay = (Feld[x][y] == EL_DYNAMITE_ACTIVE ? 12 : 6);
+
+       if (!(MovDelay[x][y] % delay))
+         DrawDynamite(x, y);
+      }
 
       return;
     }
@@ -1110,13 +1214,22 @@ void Explode(int ex, int ey, int phase, int mode)
 
     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
     {
+      /* put moving element to center field (and let it explode there) */
       center_element = MovingOrBlocked2Element(ex, ey);
       RemoveMovingField(ex, ey);
+      Feld[ex][ey] = center_element;
     }
 
-    for (y=ey-1; y<ey+2; y++) for(x=ex-1; x<ex+2; x++)
+    for (y=ey-1; y<=ey+1; y++) for(x=ex-1; x<=ex+1; x++)
     {
-      int element = Feld[x][y];
+      int element;
+
+      if (!IN_LEV_FIELD(x, y) ||
+         ((mode != EX_NORMAL || center_element == EL_AMOEBA2DIAM) &&
+          (x != ex || y != ey)))
+       continue;
+
+      element = Feld[x][y];
 
       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
       {
@@ -1124,11 +1237,7 @@ void Explode(int ex, int ey, int phase, int mode)
        RemoveMovingField(x, y);
       }
 
-      if (!IN_LEV_FIELD(x, y) || IS_MASSIVE(element) || element == EL_BURNING)
-       continue;
-
-      if ((mode!=EX_NORMAL || center_element == EL_AMOEBA2DIAM) &&
-         (x!=ex || y!=ey))
+      if (IS_MASSIVE(element) || element == EL_BURNING)
        continue;
 
       if (element == EL_EXPLODING)
@@ -1277,7 +1386,9 @@ void Explode(int ex, int ey, int phase, int mode)
 void DynaExplode(int ex, int ey)
 {
   int i, j;
-  struct PlayerInfo *player = &stored_player[Store2[ex][ey] - EL_SPIELER1];
+  int dynabomb_size = 1;
+  boolean dynabomb_xl = FALSE;
+  struct PlayerInfo *player;
   static int xy[4][2] =
   {
     { 0, -1 },
@@ -1286,33 +1397,42 @@ void DynaExplode(int ex, int ey)
     { 0, +1 }
   };
 
-  Store2[ex][ey] = 0;  /* delete player information */
+  if (IS_ACTIVE_BOMB(Feld[ex][ey]))
+  {
+    player = &stored_player[Feld[ex][ey] - EL_DYNABOMB_ACTIVE_1];
+    dynabomb_size = player->dynabomb_size;
+    dynabomb_xl = player->dynabomb_xl;
+    player->dynabombs_left++;
+  }
 
   Explode(ex, ey, EX_PHASE_START, EX_CENTER);
 
   for (i=0; i<4; i++)
   {
-    for (j=1; j<=player->dynabomb_size; j++)
+    for (j=1; j<=dynabomb_size; j++)
     {
-      int x = ex+j*xy[i%4][0];
-      int y = ey+j*xy[i%4][1];
+      int x = ex + j * xy[i % 4][0];
+      int y = ey + j * xy[i % 4][1];
       int element;
 
       if (!IN_LEV_FIELD(x, y) || IS_MASSIVE(Feld[x][y]))
        break;
 
       element = Feld[x][y];
+
+      /* do not restart explosions of fields with active bombs */
+      if (element == EL_EXPLODING && IS_ACTIVE_BOMB(Store2[x][y]))
+       continue;
+
       Explode(x, y, EX_PHASE_START, EX_BORDER);
 
       if (element != EL_LEERRAUM &&
          element != EL_ERDREICH &&
          element != EL_EXPLODING &&
-         !player->dynabomb_xl)
+         !dynabomb_xl)
        break;
     }
   }
-
-  player->dynabombs_left++;
 }
 
 void Bang(int x, int y)
@@ -1340,7 +1460,10 @@ void Bang(int x, int y)
       RaiseScoreElement(element);
       Explode(x, y, EX_PHASE_START, EX_NORMAL);
       break;
-    case EL_DYNABOMB:
+    case EL_DYNABOMB_ACTIVE_1:
+    case EL_DYNABOMB_ACTIVE_2:
+    case EL_DYNABOMB_ACTIVE_3:
+    case EL_DYNABOMB_ACTIVE_4:
     case EL_DYNABOMB_NR:
     case EL_DYNABOMB_SZ:
     case EL_DYNABOMB_XL:
@@ -1400,6 +1523,134 @@ void Blurb(int x, int y)
   }
 }
 
+static void ToggleBeltSwitch(int x, int y)
+{
+  static int belt_base_element[4] =
+  {
+    EL_BELT1_SWITCH_L,
+    EL_BELT2_SWITCH_L,
+    EL_BELT3_SWITCH_L,
+    EL_BELT4_SWITCH_L
+  };
+  static int belt_move_dir[4] =
+  {
+    MV_LEFT,
+    MV_NO_MOVING,
+    MV_RIGHT,
+    MV_NO_MOVING,
+  };
+
+  int element = Feld[x][y];
+  int belt_nr = getBeltNrFromSwitchElement(element);
+  int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
+  int belt_dir = belt_move_dir[belt_dir_nr];
+  int xx, yy;
+
+  if (!IS_BELT_SWITCH(element))
+    return;
+
+  game.belt_dir_nr[belt_nr] = belt_dir_nr;
+  game.belt_dir[belt_nr] = belt_dir;
+
+  if (belt_dir_nr == 3)
+    belt_dir_nr = 1;
+
+  for (yy=0; yy<lev_fieldy; yy++)
+  {
+    for (xx=0; xx<lev_fieldx; xx++)
+    {
+      int element = Feld[xx][yy];
+
+      if (IS_BELT_SWITCH(element))
+      {
+       int e_belt_nr = getBeltNrFromSwitchElement(element);
+
+       if (e_belt_nr == belt_nr)
+       {
+         Feld[xx][yy] = belt_base_element[belt_nr] + belt_dir_nr;
+         DrawLevelField(xx, yy);
+       }
+      }
+      else if (belt_dir == MV_NO_MOVING && IS_BELT(element))
+      {
+       int e_belt_nr = getBeltNrFromElement(element);
+
+       if (e_belt_nr == belt_nr)
+         DrawLevelField(xx, yy);    /* set belt to parking position */
+      }
+    }
+  }
+}
+
+static void ToggleSwitchgateSwitch(int x, int y)
+{
+  int xx, yy;
+
+  game.switchgate_pos = !game.switchgate_pos;
+
+  for (yy=0; yy<lev_fieldy; yy++)
+  {
+    for (xx=0; xx<lev_fieldx; xx++)
+    {
+      int element = Feld[xx][yy];
+
+      if (element == EL_SWITCHGATE_SWITCH_1 ||
+         element == EL_SWITCHGATE_SWITCH_2)
+      {
+       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_1 + game.switchgate_pos;
+       DrawLevelField(xx, yy);
+      }
+      else if (element == EL_SWITCHGATE_OPEN ||
+              element == EL_SWITCHGATE_OPENING)
+      {
+       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
+       PlaySoundLevel(xx, yy, SND_OEFFNEN);
+      }
+      else if (element == EL_SWITCHGATE_CLOSED ||
+              element == EL_SWITCHGATE_CLOSING)
+      {
+       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
+       PlaySoundLevel(xx, yy, SND_OEFFNEN);
+      }
+    }
+  }
+}
+
+static void ToggleLightSwitch(int x, int y)
+{
+  int element = Feld[x][y];
+  int xx, yy;
+
+  game.light_time_left =
+    (element == EL_LIGHT_SWITCH_OFF ? 10 * FRAMES_PER_SECOND : 0);
+
+  for (yy=0; yy<lev_fieldy; yy++)
+  {
+    for (xx=0; xx<lev_fieldx; xx++)
+    {
+      int element = Feld[xx][yy];
+
+      if (element == EL_LIGHT_SWITCH_OFF &&
+         game.light_time_left > 0)
+      {
+       Feld[xx][yy] = EL_LIGHT_SWITCH_ON;
+       DrawLevelField(xx, yy);
+      }
+      else if (element == EL_LIGHT_SWITCH_ON &&
+              game.light_time_left == 0)
+      {
+       Feld[xx][yy] = EL_LIGHT_SWITCH_OFF;
+       DrawLevelField(xx, yy);
+      }
+
+      if (element == EL_INVISIBLE_STEEL ||
+         element == EL_UNSICHTBAR ||
+         element == EL_SAND_INVISIBLE)
+       DrawLevelField(xx, yy);
+    }
+  }
+}
+
 void Impact(int x, int y)
 {
   boolean lastline = (y == lev_fieldy-1);
@@ -1513,6 +1764,20 @@ void Impact(int x, int y)
          PlaySoundLevel(x, y, SND_QUIRK);
          return;
        }
+       else if (IS_BELT_SWITCH(smashed))
+       {
+         ToggleBeltSwitch(x, y+1);
+       }
+       else if (smashed == EL_SWITCHGATE_SWITCH_1 ||
+                smashed == EL_SWITCHGATE_SWITCH_2)
+       {
+         ToggleSwitchgateSwitch(x, y+1);
+       }
+       else if (smashed == EL_LIGHT_SWITCH_OFF ||
+                smashed == EL_LIGHT_SWITCH_ON)
+       {
+         ToggleLightSwitch(x, y+1);
+       }
       }
     }
   }
@@ -1570,7 +1835,7 @@ void Impact(int x, int y)
         break;
     }
 
-    if (sound>=0)
+    if (sound >= 0)
       PlaySoundLevel(x, y, sound);
   }
 }
@@ -2082,7 +2347,8 @@ void StartMoving(int x, int y)
       InitMovingField(x, y, MV_DOWN);
       Store[x][y] = EL_SALZSAEURE;
     }
-    else if (CAN_SMASH(element) && Feld[x][y+1] == EL_BLOCKED && JustHit[x][y])
+    else if (CAN_SMASH(element) && Feld[x][y+1] == EL_BLOCKED &&
+            JustStopped[x][y])
     {
       Impact(x, y);
     }
@@ -2095,7 +2361,12 @@ void StartMoving(int x, int y)
       Feld[x][y] = EL_AMOEBING;
       Store[x][y] = EL_AMOEBE_NASS;
     }
+#if OLD_GAME_BEHAVIOUR
     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])
+#endif
     {
       boolean left  = (x>0 && IS_FREE(x-1, y) &&
                       (IS_FREE(x-1, y+1) || Feld[x-1][y+1] == EL_SALZSAEURE));
@@ -2110,6 +2381,17 @@ void StartMoving(int x, int y)
        InitMovingField(x, y, left ? MV_LEFT : MV_RIGHT);
       }
     }
+    else if (IS_BELT(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 = getBeltNrFromElement(Feld[x][y+1]);
+      int belt_dir = game.belt_dir[belt_nr];
+
+      if ((belt_dir == MV_LEFT  && left_is_free) ||
+         (belt_dir == MV_RIGHT && right_is_free))
+       InitMovingField(x, y, belt_dir);
+    }
   }
   else if (CAN_MOVE(element))
   {
@@ -2406,14 +2688,20 @@ void ContinueMoving(int x, int y)
   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
   int horiz_move = (dx!=0);
   int newx = x + dx, newy = y + dy;
-  int step = (horiz_move ? dx : dy) * TILEX/8;
+  int step = (horiz_move ? dx : dy) * TILEX / 8;
 
-  if (CAN_FALL(element) && horiz_move && !IS_SP_ELEMENT(element))
-    step*=2;
-  else if (element == EL_TROPFEN)
-    step/=2;
+  if (element == EL_TROPFEN)
+    step /= 2;
   else if (Store[x][y] == EL_MORAST_VOLL || Store[x][y] == EL_MORAST_LEER)
-    step/=4;
+    step /= 4;
+  else if (CAN_FALL(element) && horiz_move &&
+          y < lev_fieldy-1 && IS_BELT(Feld[x][y+1]))
+    step /= 2;
+
+#if OLD_GAME_BEHAVIOUR
+  else if (CAN_FALL(element) && horiz_move && !IS_SP_ELEMENT(element))
+    step*=2;
+#endif
 
   MovPos[x][y] += step;
 
@@ -2477,7 +2765,7 @@ void ContinueMoving(int x, int y)
     DrawLevelField(newx, newy);
 
     Stop[newx][newy] = TRUE;
-    JustHit[x][newy] = 3;
+    JustStopped[newx][newy] = 3;
 
     if (DONT_TOUCH(element))   /* object may be nasty to player or others */
     {
@@ -3037,6 +3325,54 @@ void AusgangstuerBlinken(int x, int y)
   DrawGraphicAnimation(x, y, GFX_AUSGANG_AUF, 4, 4, ANIM_OSCILLATE);
 }
 
+void OpenSwitchgate(int x, int y)
+{
+  int delay = 6;
+
+  if (!MovDelay[x][y])         /* next animation frame */
+    MovDelay[x][y] = 5 * delay;
+
+  if (MovDelay[x][y])          /* wait some time before next frame */
+  {
+    int phase;
+
+    MovDelay[x][y]--;
+    phase = MovDelay[x][y] / delay;
+    if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+      DrawGraphic(SCREENX(x), SCREENY(y), GFX_SWITCHGATE_OPEN - phase);
+
+    if (!MovDelay[x][y])
+    {
+      Feld[x][y] = EL_SWITCHGATE_OPEN;
+      DrawLevelField(x, y);
+    }
+  }
+}
+
+void CloseSwitchgate(int x, int y)
+{
+  int delay = 6;
+
+  if (!MovDelay[x][y])         /* next animation frame */
+    MovDelay[x][y] = 5 * delay;
+
+  if (MovDelay[x][y])          /* wait some time before next frame */
+  {
+    int phase;
+
+    MovDelay[x][y]--;
+    phase = MovDelay[x][y] / delay;
+    if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+      DrawGraphic(SCREENX(x), SCREENY(y), GFX_SWITCHGATE_CLOSED + phase);
+
+    if (!MovDelay[x][y])
+    {
+      Feld[x][y] = EL_SWITCHGATE_CLOSED;
+      DrawLevelField(x, y);
+    }
+  }
+}
+
 void EdelsteinFunkeln(int x, int y)
 {
   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
@@ -3332,6 +3668,21 @@ static void CheckBuggyBase(int x, int y)
   }
 }
 
+static void DrawBeltAnimation(int x, int y, int element)
+{
+  int belt_nr = getBeltNrFromElement(element);
+  int belt_dir = game.belt_dir[belt_nr];
+
+  if (belt_dir != MV_NO_MOVING)
+  {
+    int delay = 2;
+    int mode = (belt_dir == MV_LEFT ? ANIM_NORMAL : ANIM_REVERSE);
+    int graphic = el2gfx(element) + (belt_dir == MV_LEFT ? 0 : 7);
+
+    DrawGraphicAnimation(x, y, graphic, 8, delay, mode);
+  }
+}
+
 static void PlayerActions(struct PlayerInfo *player, byte player_action)
 {
   static byte stored_player_action[MAX_PLAYERS];
@@ -3543,8 +3894,8 @@ void GameActions()
   for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
   {
     Stop[x][y] = FALSE;
-    if (JustHit[x][y]>0)
-      JustHit[x][y]--;
+    if (JustStopped[x][y] > 0)
+      JustStopped[x][y]--;
 
 #if DEBUG
     if (IS_BLOCKED(x, y))
@@ -3579,7 +3930,7 @@ void GameActions()
     }
     else if (IS_MOVING(x, y))
       ContinueMoving(x, y);
-    else if (element == EL_DYNAMIT || element == EL_DYNABOMB)
+    else if (IS_ACTIVE_BOMB(element))
       CheckDynamite(x, y);
     else if (element == EL_EXPLODING)
       Explode(x, y, Frame[x][y], EX_NORMAL);
@@ -3618,6 +3969,16 @@ void GameActions()
       DrawGraphicAnimation(x, y, GFX2_SP_TERMINAL, 7, 12, ANIM_NORMAL);
     else if (element == EL_SP_TERMINAL_ACTIVE)
       DrawGraphicAnimation(x, y, GFX2_SP_TERMINAL_ACTIVE, 7, 4, ANIM_NORMAL);
+    else if (IS_BELT(element))
+      DrawBeltAnimation(x, y, element);
+    else if (element == EL_SWITCHGATE_OPENING)
+      OpenSwitchgate(x, y);
+    else if (element == EL_SWITCHGATE_CLOSING)
+      CloseSwitchgate(x, y);
+    else if (element == EL_EXTRA_TIME)
+      DrawGraphicAnimation(x, y, GFX_EXTRA_TIME, 6, 4, ANIM_NORMAL);
+    else if (element == EL_FORCE_FIELD)
+      DrawGraphicAnimation(x, y, GFX_FORCE_FIELD, 6, 4, ANIM_NORMAL);
 
     if (game.magic_wall_active)
     {
@@ -3659,6 +4020,7 @@ void GameActions()
        for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
        {
          element = Feld[x][y];
+
          if (element == EL_SIEB_LEER || element == EL_SIEB_VOLL)
          {
            Feld[x][y] = EL_SIEB_TOT;
@@ -3676,6 +4038,29 @@ void GameActions()
     }
   }
 
+  if (game.light_time_left > 0)
+  {
+    game.light_time_left--;
+
+    if (game.light_time_left == 0)
+    {
+      for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
+      {
+       element = Feld[x][y];
+
+       if (element == EL_LIGHT_SWITCH_ON)
+       {
+         Feld[x][y] = EL_LIGHT_SWITCH_OFF;
+         DrawLevelField(x, y);
+       }
+       else if (element == EL_INVISIBLE_STEEL ||
+                element == EL_UNSICHTBAR ||
+                element == EL_SAND_INVISIBLE)
+         DrawLevelField(x, y);
+      }
+    }
+  }
+
   if (TimeFrames >= (1000 / GameFrameDelay) && !tape.pausing)
   {
     TimeFrames = 0;
@@ -4332,6 +4717,7 @@ int DigField(struct PlayerInfo *player,
 
   if (mode == DF_NO_PUSH)
   {
+    player->Switching = FALSE;
     player->push_delay = 0;
     return MF_NO_ACTION;
   }
@@ -4341,13 +4727,14 @@ int DigField(struct PlayerInfo *player,
 
   element = Feld[x][y];
 
-  switch(element)
+  switch (element)
   {
     case EL_LEERRAUM:
       PlaySoundLevel(x, y, SND_EMPTY);
       break;
 
     case EL_ERDREICH:
+    case EL_SAND_INVISIBLE:
       Feld[x][y] = EL_LEERRAUM;
       PlaySoundLevel(x, y, SND_SCHLURF);
       break;
@@ -4385,11 +4772,31 @@ int DigField(struct PlayerInfo *player,
       PlaySoundLevel(x, y, SND_PONG);
       break;
 
-    case EL_DYNAMIT_AUS:
+    case EL_ENVELOPE:
+      Feld[x][y] = EL_LEERRAUM;
+      PlaySoundLevel(x, y, SND_PONG);
+      break;
+
+    case EL_EXTRA_TIME:
+      RemoveField(x, y);
+      if (level.time > 0)
+      {
+       TimeLeft += 10;
+       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
+      }
+      PlaySoundStereo(SND_GONG, PSND_MAX_RIGHT);
+      break;
+
+    case EL_FORCE_FIELD:
+      RemoveField(x, y);
+      PlaySoundLevel(x, y, SND_PONG);
+      break;
+
+    case EL_DYNAMITE_INACTIVE:
     case EL_SP_DISK_RED:
       RemoveField(x, y);
       player->dynamite++;
-      RaiseScoreElement(EL_DYNAMIT);
+      RaiseScoreElement(EL_DYNAMITE_INACTIVE);
       DrawText(DX_DYNAMITE, DY_DYNAMITE,
               int2str(local_player->dynamite, 3),
               FS_SMALL, FC_YELLOW);
@@ -4403,21 +4810,21 @@ int DigField(struct PlayerInfo *player,
       RemoveField(x, y);
       player->dynabomb_count++;
       player->dynabombs_left++;
-      RaiseScoreElement(EL_DYNAMIT);
+      RaiseScoreElement(EL_DYNAMITE_INACTIVE);
       PlaySoundLevel(x, y, SND_PONG);
       break;
 
     case EL_DYNABOMB_SZ:
       RemoveField(x, y);
       player->dynabomb_size++;
-      RaiseScoreElement(EL_DYNAMIT);
+      RaiseScoreElement(EL_DYNAMITE_INACTIVE);
       PlaySoundLevel(x, y, SND_PONG);
       break;
 
     case EL_DYNABOMB_XL:
       RemoveField(x, y);
       player->dynabomb_xl = TRUE;
-      RaiseScoreElement(EL_DYNAMIT);
+      RaiseScoreElement(EL_DYNAMITE_INACTIVE);
       PlaySoundLevel(x, y, SND_PONG);
       break;
 
@@ -4488,6 +4895,46 @@ int DigField(struct PlayerInfo *player,
       }
       break;
 
+    case EL_BELT1_SWITCH_L:
+    case EL_BELT1_SWITCH_M:
+    case EL_BELT1_SWITCH_R:
+    case EL_BELT2_SWITCH_L:
+    case EL_BELT2_SWITCH_M:
+    case EL_BELT2_SWITCH_R:
+    case EL_BELT3_SWITCH_L:
+    case EL_BELT3_SWITCH_M:
+    case EL_BELT3_SWITCH_R:
+    case EL_BELT4_SWITCH_L:
+    case EL_BELT4_SWITCH_M:
+    case EL_BELT4_SWITCH_R:
+      if (!player->Switching)
+      {
+       player->Switching = TRUE;
+       ToggleBeltSwitch(x, y);
+      }
+      return MF_ACTION;
+      break;
+
+    case EL_SWITCHGATE_SWITCH_1:
+    case EL_SWITCHGATE_SWITCH_2:
+      if (!player->Switching)
+      {
+       player->Switching = TRUE;
+       ToggleSwitchgateSwitch(x, y);
+      }
+      return MF_ACTION;
+      break;
+
+    case EL_LIGHT_SWITCH_OFF:
+    case EL_LIGHT_SWITCH_ON:
+      if (!player->Switching)
+      {
+       player->Switching = TRUE;
+       ToggleLightSwitch(x, y);
+      }
+      return MF_ACTION;
+      break;
+
     case EL_SP_EXIT:
       if (local_player->gems_still_needed > 0)
        return MF_NO_ACTION;
@@ -4567,6 +5014,8 @@ int DigField(struct PlayerInfo *player,
       player->programmed_action = move_direction;
       DOUBLE_PLAYER_SPEED(player);
 
+      PlaySoundLevel(x, y, SND_GATE);
+
       break;
 
     case EL_EM_GATE_1X:
@@ -4582,6 +5031,20 @@ int DigField(struct PlayerInfo *player,
       player->programmed_action = move_direction;
       DOUBLE_PLAYER_SPEED(player);
 
+      PlaySoundLevel(x, y, SND_GATE);
+
+      break;
+
+    case EL_SWITCHGATE_OPEN:
+      if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy))
+       return MF_NO_ACTION;
+
+      /* automatically move to the next field with double speed */
+      player->programmed_action = move_direction;
+      DOUBLE_PLAYER_SPEED(player);
+
+      PlaySoundLevel(x, y, SND_GATE);
+
       break;
 
     case EL_SP_PORT1_LEFT:
@@ -4623,6 +5086,8 @@ int DigField(struct PlayerInfo *player,
       player->programmed_action = move_direction;
       DOUBLE_PLAYER_SPEED(player);
 
+      PlaySoundLevel(x, y, SND_GATE);
+
       break;
 
     case EL_AUSGANG_ZU:
@@ -4794,8 +5259,7 @@ boolean PlaceBomb(struct PlayerInfo *player)
   element = Feld[jx][jy];
 
   if ((player->dynamite == 0 && player->dynabombs_left == 0) ||
-      element == EL_DYNAMIT || element == EL_DYNABOMB ||
-      element == EL_EXPLODING)
+      IS_ACTIVE_BOMB(element) || element == EL_EXPLODING)
     return FALSE;
 
   if (element != EL_LEERRAUM)
@@ -4803,7 +5267,7 @@ boolean PlaceBomb(struct PlayerInfo *player)
 
   if (player->dynamite)
   {
-    Feld[jx][jy] = EL_DYNAMIT;
+    Feld[jx][jy] = EL_DYNAMITE_ACTIVE;
     MovDelay[jx][jy] = 96;
     player->dynamite--;
     DrawText(DX_DYNAMITE, DY_DYNAMITE, int2str(local_player->dynamite, 3),
@@ -4818,8 +5282,7 @@ boolean PlaceBomb(struct PlayerInfo *player)
   }
   else
   {
-    Feld[jx][jy] = EL_DYNABOMB;
-    Store2[jx][jy] = player->element_nr;       /* for DynaExplode() */
+    Feld[jx][jy] = EL_DYNABOMB_ACTIVE_1 + (player->element_nr - EL_SPIELER1);
     MovDelay[jx][jy] = 96;
     player->dynabombs_left--;
     if (IN_SCR_FIELD(SCREENX(jx), SCREENY(jy)))
@@ -4909,7 +5372,7 @@ void RaiseScoreElement(int element)
     case EL_KOKOSNUSS:
       RaiseScore(level.score[SC_KOKOSNUSS]);
       break;
-    case EL_DYNAMIT:
+    case EL_DYNAMITE_INACTIVE:
       RaiseScore(level.score[SC_DYNAMIT]);
       break;
     case EL_SCHLUESSEL: