rnd-20020407-2-src
[rocksndiamonds.git] / src / game.c
index a3dd01dfb41705ef8c2d743d0cb433a26c57c6f5..f0bf7dd9600b8bef766243fad5256c9fdd382372 100644 (file)
 #include "init.h"
 #include "files.h"
 #include "tape.h"
-#include "joystick.h"
 #include "network.h"
 
 /* this switch controls how rocks move horizontally */
 #define OLD_GAME_BEHAVIOUR     FALSE
 
+/* EXPERIMENTAL STUFF */
+#define USE_NEW_AMOEBA_CODE    FALSE
+
 /* for DigField() */
 #define DF_NO_PUSH             0
 #define DF_DIG                 1
 
 /* for Explode() */
 #define EX_PHASE_START         0
-#define EX_NORMAL              0
-#define EX_CENTER              1
-#define EX_BORDER              2
+#define EX_NO_EXPLOSION                0
+#define EX_NORMAL              1
+#define EX_CENTER              2
+#define EX_BORDER              3
 
 /* special positions in the game control window (relative to control window) */
 #define XX_LEVEL               37
@@ -170,10 +173,10 @@ void GetPlayerConfig()
     setup.sound = FALSE;
 
   if (!audio.loops_available)
-  {
     setup.sound_loops = FALSE;
+
+  if (!audio.music_available)
     setup.sound_music = FALSE;
-  }
 
   if (!video.fullscreen_available)
     setup.fullscreen = FALSE;
@@ -267,7 +270,7 @@ static void InitField(int x, int y, boolean init_game)
 
          StorePlayer[x][y] = Feld[x][y];
 
-         if (options.verbose)
+         if (options.debug)
          {
            printf("Player %d activated.\n", player->element_nr);
            printf("[Local player is %d and currently %s.]\n",
@@ -436,6 +439,14 @@ void InitGame()
   boolean emulate_sb = TRUE;   /* unless non-SOKOBAN     elements found */
   boolean emulate_sp = TRUE;   /* unless non-SUPAPLEX    elements found */
 
+#if DEBUG
+#if USE_NEW_AMOEBA_CODE
+  printf("Using new amoeba code.\n");
+#else
+  printf("Using old amoeba code.\n");
+#endif
+#endif
+
   /* don't play tapes over network */
   network_playing = (options.network && !tape.playing);
 
@@ -484,6 +495,7 @@ void InitGame()
 
     player->move_delay = 0;
     player->last_move_dir = MV_NO_MOVING;
+    player->is_moving = FALSE;
 
     player->move_delay_value =
       (level.double_speed ? MOVE_DELAY_HIGH_SPEED : MOVE_DELAY_NORMAL_SPEED);
@@ -533,6 +545,7 @@ void InitGame()
   game.timegate_time_left = 0;
   game.switchgate_pos = 0;
   game.balloon_dir = MV_NO_MOVING;
+  game.explosions_delayed = TRUE;
 
   for (i=0; i<4; i++)
   {
@@ -554,6 +567,7 @@ void InitGame()
       AmoebaNr[x][y] = 0;
       JustStopped[x][y] = 0;
       Stop[x][y] = FALSE;
+      ExplodeField[x][y] = EX_NO_EXPLOSION;
     }
   }
 
@@ -655,7 +669,7 @@ void InitGame()
        tape.player_participates[i] = TRUE;
   }
 
-  if (options.verbose)
+  if (options.debug)
   {
     for (i=0; i<MAX_PLAYERS; i++)
     {
@@ -678,7 +692,7 @@ void InitGame()
 
   /* dynamically adjust element properties according to game engine version */
   {
-    static int ep_slippery[] =
+    static int ep_em_slippery_wall[] =
     {
       EL_BETON,
       EL_MAUERWERK,
@@ -687,14 +701,30 @@ void InitGame()
       EL_MAUER_Y,
       EL_MAUER_XY
     };
-    static int ep_slippery_num = sizeof(ep_slippery)/sizeof(int);
+#if 1
+    static int ep_em_slippery_wall_num = SIZEOF_ARRAY_INT(ep_em_slippery_wall);
+#else
+    static int ep_em_slippery_wall_num =
+      sizeof(ep_em_slippery_wall) / sizeof(int);
+#endif
 
-    for (i=0; i<ep_slippery_num; i++)
+    /*
+    printf("level %d: game.version == %06d\n", level_nr, level.game_version);
+    printf("         file_version == %06d\n", level.file_version);
+    */
+
+    for (i=0; i<ep_em_slippery_wall_num; i++)
     {
+#if 1
+      if (level.em_slippery_gems)      /* special EM style gems behaviour */
+#else
       if (game.version >= GAME_VERSION_2_0)
-       Elementeigenschaften1[ep_slippery[i]] |= EP_BIT_SLIPPERY;
+#endif
+       Elementeigenschaften2[ep_em_slippery_wall[i]] |=
+         EP_BIT_EM_SLIPPERY_WALL;
       else
-       Elementeigenschaften1[ep_slippery[i]] &= ~EP_BIT_SLIPPERY;
+       Elementeigenschaften2[ep_em_slippery_wall[i]] &=
+         ~EP_BIT_EM_SLIPPERY_WALL;
     }
   }
 
@@ -772,6 +802,7 @@ void InitGame()
           int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
 
   UnmapGameButtons();
+  UnmapTapeButtons();
   game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
   game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
   game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
@@ -789,7 +820,7 @@ void InitGame()
 
   KeyboardAutoRepeatOff();
 
-  if (options.verbose)
+  if (options.debug)
   {
     for (i=0; i<4; i++)
       printf("Player %d %sactive.\n",
@@ -938,12 +969,12 @@ void GameWon()
 
   if (TimeLeft)
   {
-    if (setup.sound_loops)
+    if (!tape.playing && setup.sound_loops)
       PlaySoundExt(SND_SIRR, PSND_MAX_VOLUME, PSND_MAX_RIGHT, PSND_LOOP);
 
     while(TimeLeft > 0)
     {
-      if (!setup.sound_loops)
+      if (!tape.playing && !setup.sound_loops)
        PlaySoundStereo(SND_SIRR, PSND_MAX_RIGHT);
       if (TimeLeft > 0 && !(TimeLeft % 10))
        RaiseScore(level.score[SC_ZEITBONUS]);
@@ -953,20 +984,22 @@ void GameWon()
        TimeLeft--;
       DrawText(DX_TIME, DY_TIME, int2str(TimeLeft, 3), FS_SMALL, FC_YELLOW);
       BackToFront();
-      Delay(10);
+
+      if (!tape.playing)
+       Delay(10);
     }
 
-    if (setup.sound_loops)
+    if (!tape.playing && setup.sound_loops)
       StopSound(SND_SIRR);
   }
   else if (level.time == 0)            /* level without time limit */
   {
-    if (setup.sound_loops)
+    if (!tape.playing && setup.sound_loops)
       PlaySoundExt(SND_SIRR, PSND_MAX_VOLUME, PSND_MAX_RIGHT, PSND_LOOP);
 
     while(TimePlayed < 999)
     {
-      if (!setup.sound_loops)
+      if (!tape.playing && !setup.sound_loops)
        PlaySoundStereo(SND_SIRR, PSND_MAX_RIGHT);
       if (TimePlayed < 999 && !(TimePlayed % 10))
        RaiseScore(level.score[SC_ZEITBONUS]);
@@ -976,10 +1009,12 @@ void GameWon()
        TimePlayed++;
       DrawText(DX_TIME, DY_TIME, int2str(TimePlayed, 3), FS_SMALL, FC_YELLOW);
       BackToFront();
-      Delay(10);
+
+      if (!tape.playing)
+       Delay(10);
     }
 
-    if (setup.sound_loops)
+    if (!tape.playing && setup.sound_loops)
       StopSound(SND_SIRR);
   }
 
@@ -1018,13 +1053,19 @@ void GameWon()
     game_status = HALLOFFAME;
     DrawHallOfFame(hi_pos);
     if (raise_level)
+    {
       level_nr++;
+      TapeErase();
+    }
   }
   else
   {
     game_status = MAINMENU;
     if (raise_level)
+    {
       level_nr++;
+      TapeErase();
+    }
     DrawMainMenu();
   }
 
@@ -1275,11 +1316,17 @@ void CheckDynamite(int x, int y)
 void Explode(int ex, int ey, int phase, int mode)
 {
   int x, y;
-  int num_phase = 9, delay = 2;
+  int num_phase = 9, delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
   int last_phase = num_phase * delay;
   int half_phase = (num_phase / 2) * delay;
   int first_phase_after_start = EX_PHASE_START + 1;
 
+  if (game.explosions_delayed)
+  {
+    ExplodeField[ex][ey] = mode;
+    return;
+  }
+
   if (phase == EX_PHASE_START)         /* initialize 'Store[][]' field */
   {
     int center_element = Feld[ex][ey];
@@ -1454,6 +1501,9 @@ void Explode(int ex, int ey, int phase, int mode)
     if (CAN_MOVE(element) || COULD_MOVE(element))
       InitMovDir(x, y);
     DrawLevelField(x, y);
+
+    if (IS_PLAYER(x, y) && !PLAYERINFO(x,y)->present)
+      StorePlayer[x][y] = 0;
   }
   else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
   {
@@ -1467,7 +1517,15 @@ void Explode(int ex, int ey, int phase, int mode)
     if (phase == delay)
       ErdreichAnbroeckeln(SCREENX(x), SCREENY(y));
 
-    DrawGraphic(SCREENX(x), SCREENY(y), graphic + (phase / delay - 1));
+    graphic += (phase / delay - 1);
+
+    if (IS_PFORTE(Store[x][y]))
+    {
+      DrawLevelElement(x, y, Store[x][y]);
+      DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic);
+    }
+    else
+      DrawGraphic(SCREENX(x), SCREENY(y), graphic);
   }
 }
 
@@ -1877,6 +1935,13 @@ void Impact(int x, int y)
        return;
       }
     }
+    else if ((element == EL_SP_INFOTRON || element == EL_SP_ZONK) &&
+            (smashed == EL_SP_SNIKSNAK || smashed == EL_SP_ELECTRON ||
+             smashed == EL_SP_DISK_ORANGE))
+    {
+      Bang(x, y+1);
+      return;
+    }
     else if (element == EL_FELSBROCKEN ||
             element == EL_SP_ZONK ||
             element == EL_BD_ROCK)
@@ -2040,7 +2105,7 @@ void TurnRound(int x, int y)
 
   if (element == EL_KAEFER || element == EL_BUTTERFLY)
   {
-    TestIfBadThingHitsOtherBadThing(x, y);
+    TestIfBadThingTouchesOtherBadThing(x, y);
 
     if (IN_LEV_FIELD(right_x, right_y) &&
        IS_FREE(right_x, right_y))
@@ -2057,7 +2122,7 @@ void TurnRound(int x, int y)
   else if (element == EL_FLIEGER || element == EL_FIREFLY ||
           element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
   {
-    TestIfBadThingHitsOtherBadThing(x, y);
+    TestIfBadThingTouchesOtherBadThing(x, y);
 
     if (IN_LEV_FIELD(left_x, left_y) &&
        IS_FREE(left_x, left_y))
@@ -2576,9 +2641,10 @@ void StartMoving(int x, int y)
             element != EL_DX_SUPABOMB)
 #endif
 #else
-    else if (IS_SLIPPERY(Feld[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_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
 #endif
     {
       boolean left  = (x>0 && IS_FREE(x-1, y) &&
@@ -2720,7 +2786,7 @@ void StartMoving(int x, int y)
     {
 
 #if 1
-      TestIfBadThingHitsHero(x, y);
+      TestIfBadThingRunsIntoHero(x, y, MovDir[x][y]);
       return;
 #else
       /* enemy got the player */
@@ -2910,7 +2976,7 @@ void StartMoving(int x, int y)
        DrawGraphicAnimation(x, y, GFX2_SP_ELECTRON, 8, 2, ANIM_NORMAL);
 
       if (DONT_TOUCH(element))
-       TestIfBadThingHitsHero(x, y);
+       TestIfBadThingTouchesHero(x, y);
 
       return;
     }
@@ -3052,12 +3118,12 @@ void ContinueMoving(int x, int y)
 
     if (DONT_TOUCH(element))   /* object may be nasty to player or others */
     {
-      TestIfBadThingHitsHero(newx, newy);
-      TestIfBadThingHitsFriend(newx, newy);
-      TestIfBadThingHitsOtherBadThing(newx, newy);
+      TestIfBadThingTouchesHero(newx, newy);
+      TestIfBadThingTouchesFriend(newx, newy);
+      TestIfBadThingTouchesOtherBadThing(newx, newy);
     }
     else if (element == EL_PINGUIN)
-      TestIfFriendHitsBadThing(newx, newy);
+      TestIfFriendTouchesBadThing(newx, newy);
 
     if (CAN_SMASH(element) && direction == MV_DOWN &&
        (newy == lev_fieldy-1 || !IS_FREE(x, newy+1)))
@@ -3427,14 +3493,14 @@ void AmoebeAbleger(int ax, int ay)
   if (element != EL_AMOEBE_NASS || neway < ay || !IS_FREE(newax, neway) ||
       (neway == lev_fieldy - 1 && newax != ax))
   {
-    Feld[newax][neway] = EL_AMOEBING;
+    Feld[newax][neway] = EL_AMOEBING;  /* simple growth of new amoeba tile */
     Store[newax][neway] = element;
   }
   else if (neway == ay)
-    Feld[newax][neway] = EL_TROPFEN;
+    Feld[newax][neway] = EL_TROPFEN;   /* drop left or right from amoeba */
   else
   {
-    InitMovingField(ax, ay, MV_DOWN);
+    InitMovingField(ax, ay, MV_DOWN);  /* drop dripping out of amoeba */
     Feld[ax][ay] = EL_AMOEBA_DRIPPING;
     Store[ax][ay] = EL_TROPFEN;
     ContinueMoving(ax, ay);
@@ -4172,9 +4238,10 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action)
 {
   static byte stored_player_action[MAX_PLAYERS];
   static int num_stored_actions = 0;
+#if 0
   static boolean save_tape_entry = FALSE;
+#endif
   boolean moved = FALSE, snapped = FALSE, bombed = FALSE;
-  int jx = player->jx, jy = player->jy;
   int left     = player_action & JOY_LEFT;
   int right    = player_action & JOY_RIGHT;
   int up       = player_action & JOY_UP;
@@ -4192,7 +4259,9 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action)
 
   if (player_action)
   {
+#if 0
     save_tape_entry = TRUE;
+#endif
     player->frame_reset_delay = 0;
 
     if (button1)
@@ -4204,15 +4273,20 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action)
       moved = MoveFigure(player, dx, dy);
     }
 
+#if 0
     if (tape.recording && (moved || snapped || bombed))
     {
       if (bombed && !moved)
        player_action &= JOY_BUTTON;
 
       stored_player_action[player->index_nr] = player_action;
+      save_tape_entry = TRUE;
     }
     else if (tape.playing && snapped)
       SnapField(player, 0, 0);                 /* stop snapping */
+#else
+    stored_player_action[player->index_nr] = player_action;
+#endif
   }
   else
   {
@@ -4222,20 +4296,49 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action)
     SnapField(player, 0, 0);
     CheckGravityMovement(player);
 
+#if 1
+    if (player->MovPos == 0)   /* needed for tape.playing */
+      player->is_moving = FALSE;
+#endif
+#if 0
+    if (player->MovPos == 0)   /* needed for tape.playing */
+      player->last_move_dir = MV_NO_MOVING;
+
+    /* !!! CHECK THIS AGAIN !!!
+       (Seems to be needed for some EL_ROBOT stuff, but breaks
+       tapes when walking through pipes!)
+    */
+
+    /* it seems that "player->last_move_dir" is misused as some sort of
+       "player->is_just_moving_in_this_moment", which is needed for the
+       robot stuff (robots don't kill players when they are moving)
+    */
+#endif 
+
     if (++player->frame_reset_delay > player->move_delay_value)
       player->Frame = 0;
   }
 
+#if 0
   if (tape.recording && num_stored_actions >= MAX_PLAYERS && save_tape_entry)
   {
     TapeRecordAction(stored_player_action);
     num_stored_actions = 0;
     save_tape_entry = FALSE;
   }
+#else
+  if (tape.recording && num_stored_actions >= MAX_PLAYERS)
+  {
+    TapeRecordAction(stored_player_action);
+    num_stored_actions = 0;
+  }
+#endif
 
+#if 0
   if (tape.playing && !tape.pausing && !player_action &&
       tape.counter < tape.length)
   {
+    int jx = player->jx, jy = player->jy;
     int next_joy =
       tape.pos[tape.counter].action[player->index_nr] & (JOY_LEFT|JOY_RIGHT);
 
@@ -4259,6 +4362,7 @@ static void PlayerActions(struct PlayerInfo *player, byte player_action)
       }
     }
   }
+#endif
 }
 
 void GameActions()
@@ -4276,6 +4380,9 @@ void GameActions()
   action_delay_value =
     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
 
+  if (tape.playing && tape.index_search)
+    action_delay_value = 0;
+
   /* ---------- main game synchronization point ---------- */
 
   WaitUntilDelayReached(&action_delay, action_delay_value);
@@ -4310,11 +4417,6 @@ void GameActions()
   if (tape.pausing)
     return;
 
-  if (tape.playing)
-    TapePlayDelay();
-  else if (tape.recording)
-    TapeRecordDelay();
-
   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
 
   for (i=0; i<MAX_PLAYERS; i++)
@@ -4411,21 +4513,27 @@ void GameActions()
     {
       StartMoving(x, y);
 
-      if (IS_GEM(element))
+      if (IS_GEM(element) || element == EL_SP_INFOTRON)
        EdelsteinFunkeln(x, y);
     }
     else if (IS_MOVING(x, y))
       ContinueMoving(x, y);
     else if (IS_ACTIVE_BOMB(element))
       CheckDynamite(x, y);
-    else if (element == EL_EXPLODING)
+#if 0
+    else if (element == EL_EXPLODING && !game.explosions_delayed)
       Explode(x, y, Frame[x][y], EX_NORMAL);
+#endif
     else if (element == EL_AMOEBING)
       AmoebeWaechst(x, y);
     else if (element == EL_DEAMOEBING)
       AmoebeSchrumpft(x, y);
+
+#if !USE_NEW_AMOEBA_CODE
     else if (IS_AMOEBALIVE(element))
       AmoebeAbleger(x, y);
+#endif
+
     else if (element == EL_LIFE || element == EL_LIFE_ASYNC)
       Life(x, y);
     else if (element == EL_ABLENK_EIN)
@@ -4509,6 +4617,65 @@ void GameActions()
     }
   }
 
+#if USE_NEW_AMOEBA_CODE
+  /* new experimental amoeba growth stuff */
+#if 1
+  if (!(FrameCounter % 8))
+#endif
+  {
+    static unsigned long random = 1684108901;
+
+    for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
+    {
+#if 0
+      x = (random >> 10) % lev_fieldx;
+      y = (random >> 20) % lev_fieldy;
+#else
+      x = RND(lev_fieldx);
+      y = RND(lev_fieldy);
+#endif
+      element = Feld[x][y];
+
+      if (!IS_PLAYER(x,y) &&
+         (element == EL_LEERRAUM ||
+          element == EL_ERDREICH ||
+          element == EL_MORAST_LEER ||
+          element == EL_BLURB_LEFT ||
+          element == EL_BLURB_RIGHT))
+      {
+       if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBE_NASS) ||
+           (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBE_NASS) ||
+           (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBE_NASS) ||
+           (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBE_NASS))
+         Feld[x][y] = EL_TROPFEN;
+      }
+
+      random = random * 129 + 1;
+    }
+  }
+#endif
+
+#if 0
+  if (game.explosions_delayed)
+#endif
+  {
+    game.explosions_delayed = FALSE;
+
+    for (y=0; y<lev_fieldy; y++) for (x=0; x<lev_fieldx; x++)
+    {
+      element = Feld[x][y];
+
+      if (ExplodeField[x][y])
+       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
+      else if (element == EL_EXPLODING)
+       Explode(x, y, Frame[x][y], EX_NORMAL);
+
+      ExplodeField[x][y] = EX_NO_EXPLOSION;
+    }
+
+    game.explosions_delayed = TRUE;
+  }
+
   if (game.magic_wall_active)
   {
     if (!(game.magic_wall_time_left % 4))
@@ -4765,7 +4932,7 @@ boolean MoveFigureOneStep(struct PlayerInfo *player,
       BuryHero(player);
     }
     else
-      TestIfBadThingHitsHero(new_jx, new_jy);
+      TestIfHeroRunsIntoBadThing(jx, jy, player->MovDir);
 
     return MF_MOVING;
   }
@@ -4798,9 +4965,15 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
   if (!player->active || (!dx && !dy))
     return FALSE;
 
+#if 0
   if (!FrameReached(&player->move_delay, player->move_delay_value) &&
       !tape.playing)
     return FALSE;
+#else
+  if (!FrameReached(&player->move_delay, player->move_delay_value) &&
+      !(tape.playing && tape.game_version < GAME_VERSION_2_0))
+    return FALSE;
+#endif
 
   /* remove the last programmed player action */
   player->programmed_action = 0;
@@ -4929,15 +5102,19 @@ boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
     DrawLevelField(jx, jy);    /* for "ErdreichAnbroeckeln()" */
 
     player->last_move_dir = player->MovDir;
+    player->is_moving = TRUE;
   }
   else
   {
     CheckGravityMovement(player);
 
+    /*
     player->last_move_dir = MV_NO_MOVING;
+    */
+    player->is_moving = FALSE;
   }
 
-  TestIfHeroHitsBadThing(jx, jy);
+  TestIfHeroTouchesBadThing(jx, jy);
 
   if (!player->active)
     RemoveHero(player);
@@ -5032,17 +5209,17 @@ void ScrollScreen(struct PlayerInfo *player, int mode)
     ScreenMovDir = MV_NO_MOVING;
 }
 
-void TestIfGoodThingHitsBadThing(int goodx, int goody)
+void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
 {
-  int i, killx = goodx, killy = goody;
-  static int xy[4][2] =
+  int i, kill_x = -1, kill_y = -1;
+  static int test_xy[4][2] =
   {
     { 0, -1 },
     { -1, 0 },
     { +1, 0 },
     { 0, +1 }
   };
-  static int harmless[4] =
+  static int test_dir[4] =
   {
     MV_UP,
     MV_LEFT,
@@ -5052,57 +5229,62 @@ void TestIfGoodThingHitsBadThing(int goodx, int goody)
 
   for (i=0; i<4; i++)
   {
-    int x, y, element;
+    int test_x, test_y, test_move_dir, test_element;
 
-    x = goodx + xy[i][0];
-    y = goody + xy[i][1];
-    if (!IN_LEV_FIELD(x, y))
+    test_x = good_x + test_xy[i][0];
+    test_y = good_y + test_xy[i][1];
+    if (!IN_LEV_FIELD(test_x, test_y))
       continue;
 
+    test_move_dir =
+      (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
+
 #if 0
-    element = Feld[x][y];
+    test_element = Feld[test_x][test_y];
 #else
-    element = MovingOrBlocked2ElementIfNotLeaving(x, y);
+    test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
 #endif
 
-    if (DONT_TOUCH(element))
+    /* 1st case: good thing is moving towards DONT_GO_TO style bad thing;
+       2nd case: DONT_TOUCH style bad thing does not move away from good thing
+    */
+    if ((DONT_GO_TO(test_element) && good_move_dir == test_dir[i]) ||
+       (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
     {
-      if (MovDir[x][y] == harmless[i])
-       continue;
-
-      killx = x;
-      killy = y;
+      kill_x = test_x;
+      kill_y = test_y;
       break;
     }
   }
 
-  if (killx != goodx || killy != goody)
+  if (kill_x != -1 || kill_y != -1)
   {
-    if (IS_PLAYER(goodx, goody))
+    if (IS_PLAYER(good_x, good_y))
     {
-      struct PlayerInfo *player = PLAYERINFO(goodx, goody);
+      struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
 
       if (player->shield_active_time_left > 0)
-       Bang(killx, killy);
-      else if (!PLAYER_PROTECTED(goodx, goody))
+       Bang(kill_x, kill_y);
+      else if (!PLAYER_PROTECTED(good_x, good_y))
        KillHero(player);
     }
     else
-      Bang(goodx, goody);
+      Bang(good_x, good_y);
   }
 }
 
-void TestIfBadThingHitsGoodThing(int badx, int bady)
+void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
 {
-  int i, killx = badx, killy = bady;
-  static int xy[4][2] =
+  int i, kill_x = -1, kill_y = -1;
+  int bad_element = Feld[bad_x][bad_y];
+  static int test_xy[4][2] =
   {
     { 0, -1 },
     { -1, 0 },
     { +1, 0 },
     { 0, +1 }
   };
-  static int harmless[4] =
+  static int test_dir[4] =
   {
     MV_UP,
     MV_LEFT,
@@ -5110,76 +5292,117 @@ void TestIfBadThingHitsGoodThing(int badx, int bady)
     MV_DOWN
   };
 
-  if (Feld[badx][bady] == EL_EXPLODING)        /* skip just exploding bad things */
+  if (bad_element == EL_EXPLODING)     /* skip just exploding bad things */
     return;
 
   for (i=0; i<4; i++)
   {
-    int x, y, element;
+    int test_x, test_y, test_move_dir, test_element;
 
-    x = badx + xy[i][0];
-    y = bady + xy[i][1];
-    if (!IN_LEV_FIELD(x, y))
+    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;
 
-    element = Feld[x][y];
+    test_move_dir =
+      (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NO_MOVING);
 
-    if (IS_PLAYER(x, y))
-    {
-      killx = x;
-      killy = y;
-      break;
-    }
-    else if (element == EL_PINGUIN)
+    test_element = Feld[test_x][test_y];
+
+    /* 1st case: good thing is moving towards DONT_GO_TO style bad thing;
+       2nd case: DONT_TOUCH style bad thing does not move away from good thing
+    */
+    if ((DONT_GO_TO(bad_element) &&  bad_move_dir == test_dir[i]) ||
+       (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
     {
-      if (MovDir[x][y] == harmless[i] && IS_MOVING(x, y))
-       continue;
+      /* good thing is player or penguin that does not move away */
+      if (IS_PLAYER(test_x, test_y))
+      {
+       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
 
-      killx = x;
-      killy = y;
-      break;
+       if (bad_element == EL_ROBOT && player->is_moving)
+         continue;     /* robot does not kill player if he is moving */
+
+       kill_x = test_x;
+       kill_y = test_y;
+       break;
+      }
+      else if (test_element == EL_PINGUIN)
+      {
+       kill_x = test_x;
+       kill_y = test_y;
+       break;
+      }
     }
   }
 
-  if (killx != badx || killy != bady)
+  if (kill_x != -1 || kill_y != -1)
   {
-    if (IS_PLAYER(killx, killy))
+    if (IS_PLAYER(kill_x, kill_y))
     {
-      struct PlayerInfo *player = PLAYERINFO(killx, killy);
+      struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
+
+#if 0
+      int dir = player->MovDir;
+      int newx = player->jx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
+      int newy = player->jy + (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
+
+      if (Feld[bad_x][bad_y] == EL_ROBOT && player->is_moving &&
+         newx != bad_x && newy != bad_y)
+       ;       /* robot does not kill player if he is moving */
+      else
+       printf("-> %d\n", player->MovDir);
+
+      if (Feld[bad_x][bad_y] == EL_ROBOT && player->is_moving &&
+         newx != bad_x && newy != bad_y)
+       ;       /* robot does not kill player if he is moving */
+      else
+       ;
+#endif
 
       if (player->shield_active_time_left > 0)
-       Bang(badx, bady);
-      else if (!PLAYER_PROTECTED(killx, killy))
+       Bang(bad_x, bad_y);
+      else if (!PLAYER_PROTECTED(kill_x, kill_y))
        KillHero(player);
     }
     else
-      Bang(killx, killy);
+      Bang(kill_x, kill_y);
   }
 }
 
-void TestIfHeroHitsBadThing(int x, int y)
+void TestIfHeroTouchesBadThing(int x, int y)
+{
+  TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
+}
+
+void TestIfHeroRunsIntoBadThing(int x, int y, int move_dir)
+{
+  TestIfGoodThingHitsBadThing(x, y, move_dir);
+}
+
+void TestIfBadThingTouchesHero(int x, int y)
 {
-  TestIfGoodThingHitsBadThing(x, y);
+  TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
 }
 
-void TestIfBadThingHitsHero(int x, int y)
+void TestIfBadThingRunsIntoHero(int x, int y, int move_dir)
 {
-  TestIfBadThingHitsGoodThing(x, y);
+  TestIfBadThingHitsGoodThing(x, y, move_dir);
 }
 
-void TestIfFriendHitsBadThing(int x, int y)
+void TestIfFriendTouchesBadThing(int x, int y)
 {
-  TestIfGoodThingHitsBadThing(x, y);
+  TestIfGoodThingHitsBadThing(x, y, MV_NO_MOVING);
 }
 
-void TestIfBadThingHitsFriend(int x, int y)
+void TestIfBadThingTouchesFriend(int x, int y)
 {
-  TestIfBadThingHitsGoodThing(x, y);
+  TestIfBadThingHitsGoodThing(x, y, MV_NO_MOVING);
 }
 
-void TestIfBadThingHitsOtherBadThing(int badx, int bady)
+void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
 {
-  int i, killx = badx, killy = bady;
+  int i, kill_x = bad_x, kill_y = bad_y;
   static int xy[4][2] =
   {
     { 0, -1 },
@@ -5192,8 +5415,8 @@ void TestIfBadThingHitsOtherBadThing(int badx, int bady)
   {
     int x, y, element;
 
-    x=badx + xy[i][0];
-    y=bady + xy[i][1];
+    x = bad_x + xy[i][0];
+    y = bad_y + xy[i][1];
     if (!IN_LEV_FIELD(x, y))
       continue;
 
@@ -5201,14 +5424,14 @@ void TestIfBadThingHitsOtherBadThing(int badx, int bady)
     if (IS_AMOEBOID(element) || element == EL_LIFE ||
        element == EL_AMOEBING || element == EL_TROPFEN)
     {
-      killx = x;
-      killy = y;
+      kill_x = x;
+      kill_y = y;
       break;
     }
   }
 
-  if (killx != badx || killy != bady)
-    Bang(badx, bady);
+  if (kill_x != bad_x || kill_y != bad_y)
+    Bang(bad_x, bad_y);
 }
 
 void KillHero(struct PlayerInfo *player)
@@ -5257,7 +5480,8 @@ void RemoveHero(struct PlayerInfo *player)
   player->present = FALSE;
   player->active = FALSE;
 
-  StorePlayer[jx][jy] = 0;
+  if (!ExplodeField[jx][jy])
+    StorePlayer[jx][jy] = 0;
 
   for (i=0; i<MAX_PLAYERS; i++)
     if (stored_player[i].active)
@@ -5599,9 +5823,16 @@ int DigField(struct PlayerInfo *player,
 
       if (player->push_delay == 0)
        player->push_delay = FrameCounter;
+#if 0
       if (!FrameReached(&player->push_delay, player->push_delay_value) &&
          !tape.playing && element != EL_SPRING)
        return MF_NO_ACTION;
+#else
+      if (!FrameReached(&player->push_delay, player->push_delay_value) &&
+         !(tape.playing && tape.game_version < GAME_VERSION_2_0) &&
+         element != EL_SPRING)
+       return MF_NO_ACTION;
+#endif
 
       RemoveField(x, y);
       Feld[x+dx][y+dy] = element;
@@ -5834,9 +6065,16 @@ int DigField(struct PlayerInfo *player,
 
       if (player->push_delay == 0)
        player->push_delay = FrameCounter;
+#if 0
       if (!FrameReached(&player->push_delay, player->push_delay_value) &&
          !tape.playing && element != EL_BALLOON)
        return MF_NO_ACTION;
+#else
+      if (!FrameReached(&player->push_delay, player->push_delay_value) &&
+         !(tape.playing && tape.game_version < GAME_VERSION_2_0) &&
+         element != EL_BALLOON)
+       return MF_NO_ACTION;
+#endif
 
       if (IS_SB_ELEMENT(element))
       {
@@ -6067,6 +6305,31 @@ void RaiseScoreElement(int element)
   }
 }
 
+void RequestQuitGame(boolean ask_if_really_quit)
+{
+  if (AllPlayersGone ||
+      !ask_if_really_quit ||
+      level_editor_test_game ||
+      Request("Do you really want to quit the game ?",
+             REQ_ASK | REQ_STAY_CLOSED))
+  {
+#if defined(PLATFORM_UNIX)
+    if (options.network)
+      SendToServer_StopPlaying();
+    else
+#endif
+    {
+      game_status = MAINMENU;
+      DrawMainMenu();
+    }
+  }
+  else
+  {
+    OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
+  }
+}
+
+
 /* ---------- new game button stuff ---------------------------------------- */
 
 /* graphic position values for game buttons */
@@ -6215,30 +6478,7 @@ static void HandleGameButtons(struct GadgetInfo *gi)
   switch (id)
   {
     case GAME_CTRL_ID_STOP:
-      if (AllPlayersGone)
-      {
-       CloseDoor(DOOR_CLOSE_1);
-       game_status = MAINMENU;
-       DrawMainMenu();
-       break;
-      }
-
-      if (level_editor_test_game ||
-         Request("Do you really want to quit the game ?",
-                 REQ_ASK | REQ_STAY_CLOSED))
-      { 
-#if defined(PLATFORM_UNIX)
-       if (options.network)
-         SendToServer_StopPlaying();
-       else
-#endif
-       {
-         game_status = MAINMENU;
-         DrawMainMenu();
-       }
-      }
-      else
-       OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
+      RequestQuitGame(TRUE);
       break;
 
     case GAME_CTRL_ID_PAUSE:
@@ -6276,7 +6516,7 @@ static void HandleGameButtons(struct GadgetInfo *gi)
        setup.sound_music = FALSE;
        FadeMusic();
       }
-      else if (audio.loops_available)
+      else if (audio.music_available)
       { 
        setup.sound = setup.sound_music = TRUE;
        if (num_bg_loops)