rnd-20061003-1-src
[rocksndiamonds.git] / src / game.c
index 72ae34a87ddde26fa8a438a02ec3e2db33fc6dca..a6e1449d700868daf5494ad911fe74cca9cdd6ba 100644 (file)
@@ -52,7 +52,6 @@
 #define USE_CODE_THAT_BREAKS_SNAKE_BITE        (USE_NEW_STUFF          * 1)
 
 #define USE_UFAST_PLAYER_EXIT_BUGFIX   (USE_NEW_STUFF          * 1)
-#define USE_NEW_GAME_WON               (USE_NEW_STUFF          * 1)
 
 
 /* for DigField() */
 #define GET_DX_FROM_DIR(d)     ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
 #define GET_DY_FROM_DIR(d)     ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
 
-#define        INIT_GFX_RANDOM()       (SimpleRND(1000000))
+#define        INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
 
 #define GET_NEW_PUSH_DELAY(e)  (   (element_info[e].push_delay_fixed) + \
                                 RND(element_info[e].push_delay_random))
         (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
         (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
         (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
-        (e) >= EL_LAST_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
+        (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
         RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
         (e))
 
@@ -1298,6 +1297,11 @@ void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
   int key[MAX_NUM_KEYS];
   int i;
 
+  /* prevent EM engine from updating time/score values parallel to GameWon() */
+  if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
+      local_player->LevelSolved)
+    return;
+
   for (i = 0; i < MAX_NUM_KEYS; i++)
     key[i] = key_bits & (1 << i);
 
@@ -1309,14 +1313,15 @@ void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
   DrawGameValue_Time(time);
 
   DrawGameValue_Keys(key);
-
-  redraw_mask |= REDRAW_DOOR_1;
 }
 
 void DrawGameDoorValues()
 {
   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
-  int dynamite_state = 0;
+  int dynamite_value = 0;
+  int score_value = (local_player->LevelSolved ? local_player->score_final :
+                    local_player->score);
+  int gems_value = local_player->gems_still_needed;
   int key_bits = 0;
   int i, j;
 
@@ -1335,7 +1340,7 @@ void DrawGameDoorValues()
        if (stored_player[i].key[j])
          key_bits |= (1 << j);
 
-      dynamite_state += stored_player[i].inventory_size;
+      dynamite_value += stored_player[i].inventory_size;
     }
   }
   else
@@ -1346,11 +1351,11 @@ void DrawGameDoorValues()
       if (stored_player[player_nr].key[i])
        key_bits |= (1 << i);
 
-    dynamite_state = stored_player[player_nr].inventory_size;
+    dynamite_value = stored_player[player_nr].inventory_size;
   }
 
-  DrawAllGameValues(local_player->gems_still_needed, dynamite_state,
-                   local_player->score, time_value, key_bits);
+  DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
+                   key_bits);
 }
 
 
@@ -1839,6 +1844,8 @@ void InitGame()
     player->programmed_action = 0;
 
     player->score = 0;
+    player->score_final = 0;
+
     player->gems_still_needed = level.gems_needed;
     player->sokobanfields_still_needed = 0;
     player->lights_still_needed = 0;
@@ -2423,7 +2430,7 @@ void InitGame()
 
     OpenDoor(DOOR_OPEN_ALL);
 
-    PlaySoundStereo(SND_GAME_STARTING, SOUND_MIDDLE);
+    PlaySound(SND_GAME_STARTING);
 
     if (setup.sound_music)
       PlayLevelMusic();
@@ -2438,6 +2445,13 @@ void InitGame()
     }
   }
 
+#if 1
+  UnmapAllGadgets();
+
+  MapGameButtons();
+  MapTapeButtons();
+#endif
+
   game.restart_level = FALSE;
 }
 
@@ -2648,119 +2662,129 @@ void InitAmoebaNr(int x, int y)
   AmoebaCnt2[group_nr]++;
 }
 
-#if USE_NEW_GAME_WON
+static void PlayerWins(struct PlayerInfo *player)
+{
+  player->LevelSolved = TRUE;
+  player->GameOver = TRUE;
+
+  player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+                        level.native_em_level->lev->score : player->score);
+}
 
 void GameWon()
 {
-  static boolean score_done = FALSE;
-  static boolean player_done = FALSE;
+  static int time, time_final;
+  static int score, score_final;
   static int game_over_delay = 0;
   int game_over_delay_value = 50;
 
-  /* do not start end game actions before the player stops moving (to exit) */
-  if (local_player->MovPos)
-    return;
-
-  if (tape.auto_play)          /* tape might already be stopped here */
-    tape.auto_play_level_solved = TRUE;
-
   if (!local_player->LevelSolved_GameEnd)
   {
+    int i;
+
+    /* do not start end game actions before the player stops moving (to exit) */
+    if (local_player->MovPos)
+      return;
+
     local_player->LevelSolved_GameEnd = TRUE;
     local_player->LevelSolved_SaveTape = tape.recording;
     local_player->LevelSolved_SaveScore = !tape.playing;
 
-    score_done = FALSE;
-    player_done = FALSE;
-    game_over_delay = 0;
-  }
+    if (tape.auto_play)                /* tape might already be stopped here */
+      tape.auto_play_level_solved = TRUE;
+
+#if 1
+    TapeStop();
+#endif
 
-  PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
+    game_over_delay = game_over_delay_value;
 
-  if (TimeLeft > 0)
-  {
-    if (!tape.playing)
-    {
-      if (setup.sound_loops)
-       PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
-                    SND_CTRL_PLAY_LOOP);
-      else
-       PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
-    }
+    time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
+    score = score_final = local_player->score_final;
 
-    if (TimeLeft > 100 && TimeLeft % 10 == 0)
+    if (TimeLeft > 0)
     {
-      TimeLeft -= 10;
-      RaiseScore(level.score[SC_TIME_BONUS] * 10);
+      time_final = 0;
+      score_final += TimeLeft * level.score[SC_TIME_BONUS];
     }
-    else
+    else if (level.time == 0 && TimePlayed < 999)
     {
-      TimeLeft--;
-      RaiseScore(level.score[SC_TIME_BONUS]);
+      time_final = 999;
+      score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
     }
 
-    DrawGameValue_Time(TimeLeft);
+    local_player->score_final = score_final;
 
-    if (TimeLeft <= 0 && !tape.playing && setup.sound_loops)
-      StopSound(SND_GAME_LEVELTIME_BONUS);
-  }
-  else if (level.time == 0 && TimePlayed < 999)        /* level without time limit */
-  {
-    if (!tape.playing)
+    if (level_editor_test_game)
     {
-      if (setup.sound_loops)
-       PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
-                    SND_CTRL_PLAY_LOOP);
-      else
-       PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
+      time = time_final;
+      score = score_final;
+
+      DrawGameValue_Time(time);
+      DrawGameValue_Score(score);
     }
 
-    if (TimePlayed < 900 && TimePlayed % 10 == 0)
+    if (ExitX >= 0 && ExitY >= 0)      /* local player has left the level */
     {
-      TimePlayed += 10;
-      RaiseScore(level.score[SC_TIME_BONUS] * 10);
+      /* close exit door after last player */
+      if (AllPlayersGone &&
+         (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
+          Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
+      {
+       int element = Feld[ExitX][ExitY];
+
+       Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
+                             EL_SP_EXIT_CLOSING);
+
+       PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
+      }
+
+      /* player disappears */
+      DrawLevelField(ExitX, ExitY);
     }
-    else
+
+    for (i = 0; i < MAX_PLAYERS; i++)
     {
-      TimePlayed++;
-      RaiseScore(level.score[SC_TIME_BONUS]);
-    }
+      struct PlayerInfo *player = &stored_player[i];
 
-    DrawGameValue_Time(TimePlayed);
+      if (player->present)
+      {
+       RemovePlayer(player);
 
-    if (TimePlayed >= 999 && !tape.playing && setup.sound_loops)
-      StopSound(SND_GAME_LEVELTIME_BONUS);
-  }
-  else
-  {
-    score_done = TRUE;
+       /* player disappears */
+       DrawLevelField(player->jx, player->jy);
+      }
+    }
+
+    PlaySound(SND_GAME_WINNING);
   }
 
-  /* close exit door after last player */
-  if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
-      (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
-       Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
+  if (game_over_delay > 0)
   {
-    int element = Feld[ExitX][ExitY];
-
-    Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
-                         EL_SP_EXIT_CLOSING);
+    game_over_delay--;
 
-    PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
+    return;
   }
 
-  /* player disappears */
-  if (ExitX >= 0 && ExitY >= 0 && !player_done)
+  if (time != time_final)
   {
-    DrawLevelField(ExitX, ExitY);
+    int time_to_go = ABS(time_final - time);
+    int time_count_dir = (time < time_final ? +1 : -1);
+    int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
 
-    player_done = TRUE;
-  }
+    time  += time_count_steps * time_count_dir;
+    score += time_count_steps * level.score[SC_TIME_BONUS];
 
-  game_over_delay++;
+    DrawGameValue_Time(time);
+    DrawGameValue_Score(score);
 
-  if (game_over_delay < game_over_delay_value || !score_done)
-    return;
+    if (time == time_final)
+      StopSound(SND_GAME_LEVELTIME_BONUS);
+    else if (setup.sound_loops)
+      PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
+    else
+      PlaySound(SND_GAME_LEVELTIME_BONUS);
+  }
 }
 
 void GameEnd()
@@ -2772,9 +2796,20 @@ void GameEnd()
 
   if (local_player->LevelSolved_SaveTape)
   {
+#if 0
     TapeStop();
+#endif
 
-    SaveTape(tape.level_nr);           /* Ask to save tape */
+    SaveTape(tape.level_nr);           /* ask to save tape */
+  }
+
+  if (level_editor_test_game)
+  {
+    game_status = GAME_MODE_MAIN;
+
+    DrawMainMenu();
+
+    return;
   }
 
   if (!local_player->LevelSolved_SaveScore)
@@ -2794,10 +2829,8 @@ void GameEnd()
     SaveLevelSetup_SeriesInfo();
   }
 
-  if (level_editor_test_game)
-    local_player->score = -1;  /* no highscore when playing from editor */
-  else if (level_nr < leveldir_current->last_level)
-    raise_level = TRUE;                /* advance to next level */
+  if (level_nr < leveldir_current->last_level)
+    raise_level = TRUE;                        /* advance to next level */
 
   if ((hi_pos = NewHiScore()) >= 0) 
   {
@@ -2825,161 +2858,8 @@ void GameEnd()
 
     DrawAndFadeInMainMenu(REDRAW_FIELD);
   }
-
-  local_player->LevelSolved_SaveScore = FALSE;
 }
 
-#else
-
-void GameWon()
-{
-  int hi_pos;
-  boolean raise_level = FALSE;
-
-  if (local_player->MovPos)
-    return;
-
-  if (tape.auto_play)          /* tape might already be stopped here */
-    tape.auto_play_level_solved = TRUE;
-
-  local_player->LevelSolved = FALSE;
-
-  PlaySoundStereo(SND_GAME_WINNING, SOUND_MIDDLE);
-
-  if (TimeLeft)
-  {
-    if (!tape.playing && setup.sound_loops)
-      PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
-                  SND_CTRL_PLAY_LOOP);
-
-    while (TimeLeft > 0)
-    {
-      if (!tape.playing && !setup.sound_loops)
-       PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
-
-      if (TimeLeft > 100 && TimeLeft % 10 == 0)
-      {
-       TimeLeft -= 10;
-       RaiseScore(level.score[SC_TIME_BONUS] * 10);
-      }
-      else
-      {
-       TimeLeft--;
-       RaiseScore(level.score[SC_TIME_BONUS]);
-      }
-
-      DrawGameValue_Time(TimeLeft);
-
-      BackToFront();
-
-      if (!tape.playing)
-       Delay(10);
-    }
-
-    if (!tape.playing && setup.sound_loops)
-      StopSound(SND_GAME_LEVELTIME_BONUS);
-  }
-  else if (level.time == 0)            /* level without time limit */
-  {
-    if (!tape.playing && setup.sound_loops)
-      PlaySoundExt(SND_GAME_LEVELTIME_BONUS, SOUND_MAX_VOLUME, SOUND_MIDDLE,
-                  SND_CTRL_PLAY_LOOP);
-
-    while (TimePlayed < 999)
-    {
-      if (!tape.playing && !setup.sound_loops)
-       PlaySoundStereo(SND_GAME_LEVELTIME_BONUS, SOUND_MIDDLE);
-
-      if (TimePlayed < 900 && TimePlayed % 10 == 0)
-      {
-       TimePlayed += 10;
-       RaiseScore(level.score[SC_TIME_BONUS] * 10);
-      }
-      else
-      {
-       TimePlayed++;
-       RaiseScore(level.score[SC_TIME_BONUS]);
-      }
-
-      DrawGameValue_Time(TimePlayed);
-
-      BackToFront();
-
-      if (!tape.playing)
-       Delay(10);
-    }
-
-    if (!tape.playing && setup.sound_loops)
-      StopSound(SND_GAME_LEVELTIME_BONUS);
-  }
-
-  /* close exit door after last player */
-  if (AllPlayersGone && ExitX >= 0 && ExitY >= 0 &&
-      (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
-       Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN))
-  {
-    int element = Feld[ExitX][ExitY];
-
-    Feld[ExitX][ExitY] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
-                         EL_SP_EXIT_CLOSING);
-
-    PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
-  }
-
-  /* player disappears */
-  if (ExitX >= 0 && ExitY >= 0)
-    DrawLevelField(ExitX, ExitY);
-
-  BackToFront();
-
-  if (tape.playing)
-    return;
-
-  CloseDoor(DOOR_CLOSE_1);
-
-  if (tape.recording)
-  {
-    TapeStop();
-    SaveTape(tape.level_nr);           /* Ask to save tape */
-  }
-
-  if (level_nr == leveldir_current->handicap_level)
-  {
-    leveldir_current->handicap_level++;
-    SaveLevelSetup_SeriesInfo();
-  }
-
-  if (level_editor_test_game)
-    local_player->score = -1;  /* no highscore when playing from editor */
-  else if (level_nr < leveldir_current->last_level)
-    raise_level = TRUE;                /* advance to next level */
-
-  if ((hi_pos = NewHiScore()) >= 0) 
-  {
-    game_status = GAME_MODE_SCORES;
-    DrawHallOfFame(hi_pos);
-    if (raise_level)
-    {
-      level_nr++;
-      TapeErase();
-    }
-  }
-  else
-  {
-    game_status = GAME_MODE_MAIN;
-    if (raise_level)
-    {
-      level_nr++;
-      TapeErase();
-    }
-    DrawMainMenu();
-  }
-
-  BackToFront();
-}
-
-#endif
-
 int NewHiScore()
 {
   int k, l;
@@ -2988,12 +2868,12 @@ int NewHiScore()
   LoadScore(level_nr);
 
   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
-      local_player->score < highscore[MAX_SCORE_ENTRIES - 1].Score) 
+      local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
     return -1;
 
   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
   {
-    if (local_player->score > highscore[k].Score)
+    if (local_player->score_final > highscore[k].Score)
     {
       /* player has made it to the hall of fame */
 
@@ -3021,7 +2901,7 @@ int NewHiScore()
 #endif
       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; 
+      highscore[k].Score = local_player->score_final
       position = k;
       break;
     }
@@ -6064,7 +5944,7 @@ void StartMoving(int x, int y)
        local_player->friends_still_needed--;
        if (!local_player->friends_still_needed &&
            !local_player->GameOver && AllPlayersGone)
-         local_player->LevelSolved = local_player->GameOver = TRUE;
+         PlayerWins(local_player);
 
        return;
       }
@@ -7355,7 +7235,7 @@ void EdelsteinFunkeln(int x, int y)
     return;
 
   if (MovDelay[x][y] == 0)     /* next animation frame */
-    MovDelay[x][y] = 11 * !SimpleRND(500);
+    MovDelay[x][y] = 11 * !GetSimpleRandom(500);
 
   if (MovDelay[x][y] != 0)     /* wait some time before next frame */
   {
@@ -7884,7 +7764,7 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page)
     {
       for (i = 0; i < MAX_PLAYERS; i++)
        if (action_arg_player_bits & (1 << i))
-         stored_player[i].LevelSolved = stored_player[i].GameOver = TRUE;
+         PlayerWins(&stored_player[i]);
 
       break;
     }
@@ -8820,11 +8700,11 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
       player->frame_counter_bored =
        FrameCounter +
        game.player_boring_delay_fixed +
-       SimpleRND(game.player_boring_delay_random);
+       GetSimpleRandom(game.player_boring_delay_random);
       player->frame_counter_sleeping =
        FrameCounter +
        game.player_sleeping_delay_fixed +
-       SimpleRND(game.player_sleeping_delay_random);
+       GetSimpleRandom(game.player_sleeping_delay_random);
 
       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
     }
@@ -8880,10 +8760,10 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
 
          player->anim_delay_counter =
            graphic_info[special_graphic].anim_delay_fixed +
-           SimpleRND(graphic_info[special_graphic].anim_delay_random);
+           GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
          player->post_delay_counter =
            graphic_info[special_graphic].post_delay_fixed +
-           SimpleRND(graphic_info[special_graphic].post_delay_random);
+           GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
 
          player->special_action_sleeping = special_action;
        }
@@ -8906,16 +8786,16 @@ static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
        if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
        {
          int special_action =
-           ACTION_BORING_1 + SimpleRND(player->num_special_action_bored);
+           ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
          int special_graphic =
            el_act_dir2img(player->artwork_element, special_action, move_dir);
 
          player->anim_delay_counter =
            graphic_info[special_graphic].anim_delay_fixed +
-           SimpleRND(graphic_info[special_graphic].anim_delay_random);
+           GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
          player->post_delay_counter =
            graphic_info[special_graphic].post_delay_fixed +
-           SimpleRND(graphic_info[special_graphic].post_delay_random);
+           GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
 
          player->special_action_bored = special_action;
        }
@@ -9022,7 +8902,8 @@ static void CheckLevelTime()
   {
     if (level.native_em_level->lev->home == 0) /* all players at home */
     {
-      local_player->LevelSolved = TRUE;
+      PlayerWins(local_player);
+
       AllPlayersGone = TRUE;
 
       level.native_em_level->lev->home = -1;
@@ -9053,7 +8934,7 @@ static void CheckLevelTime()
       }
     }
 
-    if (!level.use_step_counter)
+    if (!local_player->LevelSolved && !level.use_step_counter)
     {
       TimePlayed++;
 
@@ -9062,7 +8943,7 @@ static void CheckLevelTime()
        TimeLeft--;
 
        if (TimeLeft <= 10 && setup.time_limit)
-         PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
+         PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
 
        DrawGameValue_Time(TimeLeft);
 
@@ -9174,7 +9055,8 @@ void GameActions()
   {
     if (level.native_em_level->lev->home == 0) /* all players at home */
     {
-      local_player->LevelSolved = TRUE;
+      PlayerWins(local_player);
+
       AllPlayersGone = TRUE;
 
       level.native_em_level->lev->home = -1;
@@ -10419,7 +10301,7 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
 
       if (local_player->friends_still_needed == 0 ||
          IS_SP_ELEMENT(Feld[jx][jy]))
-       player->LevelSolved = player->GameOver = TRUE;
+       PlayerWins(player);
     }
 
     /* this breaks one level: "machine", level 000 */
@@ -10467,7 +10349,7 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
        RemovePlayer(player);
     }
 
-    if (level.use_step_counter)
+    if (!local_player->LevelSolved && level.use_step_counter)
     {
       int i;
 
@@ -10478,7 +10360,7 @@ void ScrollPlayer(struct PlayerInfo *player, int mode)
        TimeLeft--;
 
        if (TimeLeft <= 10 && setup.time_limit)
-         PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
+         PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
 
        DrawGameValue_Time(TimeLeft);
 
@@ -11701,7 +11583,8 @@ int DigField(struct PlayerInfo *player,
       if (local_player->sokobanfields_still_needed == 0 &&
          game.emulation == EMU_SOKOBAN)
       {
-       player->LevelSolved = player->GameOver = TRUE;
+       PlayerWins(player);
+
        PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
       }
     }
@@ -12243,9 +12126,12 @@ static void PlayLevelMusic()
     PlayMusic(MAP_NOCONF_MUSIC(level_nr));     /* from music dir */
 }
 
-void PlayLevelSound_EM(int x, int y, int element_em, int sample)
+void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
 {
   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
+  int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
+  int x = xx - 1 - offset;
+  int y = yy - 1 - offset;
 
   switch (sample)
   {
@@ -12391,7 +12277,7 @@ void PlayLevelSound_EM(int x, int y, int element_em, int sample)
       break;
 
     case SAMPLE_time:
-      PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
+      PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
       break;
 
     default:
@@ -12400,6 +12286,31 @@ void PlayLevelSound_EM(int x, int y, int element_em, int sample)
   }
 }
 
+#if 0
+void ChangeTime(int value)
+{
+  int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
+
+  *time += value;
+
+  /* EMC game engine uses value from time counter of RND game engine */
+  level.native_em_level->lev->time = *time;
+
+  DrawGameValue_Time(*time);
+}
+
+void RaiseScore(int value)
+{
+  /* EMC game engine and RND game engine have separate score counters */
+  int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+               &level.native_em_level->lev->score : &local_player->score);
+
+  *score += value;
+
+  DrawGameValue_Score(*score);
+}
+#endif
+
 void RaiseScore(int value)
 {
   local_player->score += value;
@@ -12529,6 +12440,312 @@ void RequestQuitGame(boolean ask_if_really_quit)
 }
 
 
+/* ------------------------------------------------------------------------- */
+/* random generator functions                                                */
+/* ------------------------------------------------------------------------- */
+
+unsigned int InitEngineRandom_RND(long seed)
+{
+  game.num_random_calls = 0;
+
+#if 0
+  unsigned int rnd_seed = InitEngineRandom(seed);
+
+  printf("::: START RND: %d\n", rnd_seed);
+
+  return rnd_seed;
+#else
+
+  return InitEngineRandom(seed);
+
+#endif
+
+}
+
+unsigned int RND(int max)
+{
+  if (max > 0)
+  {
+    game.num_random_calls++;
+
+    return GetEngineRandom(max);
+  }
+
+  return 0;
+}
+
+
+/* ------------------------------------------------------------------------- */
+/* game engine snapshot handling functions                                   */
+/* ------------------------------------------------------------------------- */
+
+#define ARGS_ADDRESS_AND_SIZEOF(x)             (&(x)), (sizeof(x))
+
+struct EngineSnapshotInfo
+{
+  /* runtime values for custom element collect score */
+  int collect_score[NUM_CUSTOM_ELEMENTS];
+
+  /* runtime values for group element choice position */
+  int choice_pos[NUM_GROUP_ELEMENTS];
+
+  /* runtime values for belt position animations */
+  int belt_graphic[4 * NUM_BELT_PARTS];
+  int belt_anim_mode[4 * NUM_BELT_PARTS];
+};
+
+struct EngineSnapshotNodeInfo
+{
+  void *buffer_orig;
+  void *buffer_copy;
+  int size;
+};
+
+static struct EngineSnapshotInfo engine_snapshot_rnd;
+static ListNode *engine_snapshot_list = NULL;
+static char *snapshot_level_identifier = NULL;
+static int snapshot_level_nr = -1;
+
+void FreeEngineSnapshot()
+{
+  while (engine_snapshot_list != NULL)
+    deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
+                      checked_free);
+
+  setString(&snapshot_level_identifier, NULL);
+  snapshot_level_nr = -1;
+}
+
+static void SaveEngineSnapshotValues_RND()
+{
+  static int belt_base_active_element[4] =
+  {
+    EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
+    EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
+    EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
+    EL_CONVEYOR_BELT_4_LEFT_ACTIVE
+  };
+  int i, j;
+
+  for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+  {
+    int element = EL_CUSTOM_START + i;
+
+    engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
+  }
+
+  for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
+  {
+    int element = EL_GROUP_START + i;
+
+    engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
+  }
+
+  for (i = 0; i < 4; i++)
+  {
+    for (j = 0; j < NUM_BELT_PARTS; j++)
+    {
+      int element = belt_base_active_element[i] + j;
+      int graphic = el2img(element);
+      int anim_mode = graphic_info[graphic].anim_mode;
+
+      engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
+      engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
+    }
+  }
+}
+
+static void LoadEngineSnapshotValues_RND()
+{
+  unsigned long num_random_calls = game.num_random_calls;
+  int i, j;
+
+  for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+  {
+    int element = EL_CUSTOM_START + i;
+
+    element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
+  }
+
+  for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
+  {
+    int element = EL_GROUP_START + i;
+
+    element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
+  }
+
+  for (i = 0; i < 4; i++)
+  {
+    for (j = 0; j < NUM_BELT_PARTS; j++)
+    {
+      int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
+      int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
+
+      graphic_info[graphic].anim_mode = anim_mode;
+    }
+  }
+
+  if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
+  {
+    InitRND(tape.random_seed);
+    for (i = 0; i < num_random_calls; i++)
+      RND(1);
+  }
+
+  if (game.num_random_calls != num_random_calls)
+  {
+    Error(ERR_RETURN, "number of random calls out of sync");
+    Error(ERR_RETURN, "number of random calls should be %d", num_random_calls);
+    Error(ERR_RETURN, "number of random calls is %d", game.num_random_calls);
+    Error(ERR_EXIT, "this should not happen -- please debug");
+  }
+}
+
+static void SaveEngineSnapshotBuffer(void *buffer, int size)
+{
+  struct EngineSnapshotNodeInfo *bi =
+    checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
+
+  bi->buffer_orig = buffer;
+  bi->buffer_copy = checked_malloc(size);
+  bi->size = size;
+
+  memcpy(bi->buffer_copy, buffer, size);
+
+  addNodeToList(&engine_snapshot_list, NULL, bi);
+}
+
+void SaveEngineSnapshot()
+{
+  FreeEngineSnapshot();                /* free previous snapshot, if needed */
+
+  /* copy some special values to a structure better suited for the snapshot */
+
+  SaveEngineSnapshotValues_RND();
+  SaveEngineSnapshotValues_EM();
+
+  /* save values stored in special snapshot structure */
+
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
+
+  /* save further RND engine values */
+
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
+
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
+
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
+
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
+
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
+
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
+
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
+
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
+
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
+
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
+
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
+
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
+
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
+  SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
+
+  /* save level identification information */
+
+  setString(&snapshot_level_identifier, leveldir_current->identifier);
+  snapshot_level_nr = level_nr;
+
+#if 0
+  ListNode *node = engine_snapshot_list;
+  int num_bytes = 0;
+
+  while (node != NULL)
+  {
+    num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
+
+    node = node->next;
+  }
+
+  printf("::: size of engine snapshot: %d bytes\n", num_bytes);
+#endif
+}
+
+static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
+{
+  memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
+}
+
+void LoadEngineSnapshot()
+{
+  ListNode *node = engine_snapshot_list;
+
+  if (engine_snapshot_list == NULL)
+    return;
+
+  while (node != NULL)
+  {
+    LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
+
+    node = node->next;
+  }
+
+  /* restore special values from snapshot structure */
+
+  LoadEngineSnapshotValues_RND();
+  LoadEngineSnapshotValues_EM();
+}
+
+boolean CheckEngineSnapshot()
+{
+  return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
+         snapshot_level_nr == level_nr);
+}
+
+
 /* ---------- new game button stuff ---------------------------------------- */
 
 /* graphic position values for game buttons */