moved setting auto-exit for Sokoban-style levels from runtime to level
[rocksndiamonds.git] / src / game.c
index 7ce53c5b077581d92118638e15ff81da1a0dfc2f..0dc37771206428ec53ab7a72d6bccf0ea9cee016 100644 (file)
@@ -3542,7 +3542,6 @@ void InitGame(void)
   int fade_mask = REDRAW_FIELD;
 
   boolean emulate_bd = TRUE;   // unless non-BOULDERDASH elements found
-  boolean emulate_sb = TRUE;   // unless non-SOKOBAN     elements found
   boolean emulate_sp = TRUE;   // unless non-SUPAPLEX    elements found
   int initial_move_dir = MV_DOWN;
   int i, j, x, y;
@@ -3886,8 +3885,6 @@ void InitGame(void)
   {
     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
       emulate_bd = FALSE;
-    if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
-      emulate_sb = FALSE;
     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
       emulate_sp = FALSE;
 
@@ -3912,7 +3909,6 @@ void InitGame(void)
   }
 
   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
-                   emulate_sb ? EMU_SOKOBAN :
                    emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
 
   // initialize type of slippery elements
@@ -4310,7 +4306,7 @@ void InitGame(void)
        {
          // check for player created from custom element as single target
          content = element_info[element].change_page[i].target_element;
-         is_player = ELEM_IS_PLAYER(content);
+         is_player = IS_PLAYER_ELEMENT(content);
 
          if (is_player && (found_rating < 3 ||
                            (found_rating == 3 && element < found_element)))
@@ -4328,7 +4324,7 @@ void InitGame(void)
       {
        // check for player created from custom element as explosion content
        content = element_info[element].content.e[xx][yy];
-       is_player = ELEM_IS_PLAYER(content);
+       is_player = IS_PLAYER_ELEMENT(content);
 
        if (is_player && (found_rating < 2 ||
                          (found_rating == 2 && element < found_element)))
@@ -4349,7 +4345,7 @@ void InitGame(void)
          content =
            element_info[element].change_page[i].target_content.e[xx][yy];
 
-         is_player = ELEM_IS_PLAYER(content);
+         is_player = IS_PLAYER_ELEMENT(content);
 
          if (is_player && (found_rating < 1 ||
                            (found_rating == 1 && element < found_element)))
@@ -5025,7 +5021,7 @@ void GameEnd(void)
   {
     SetGameStatus(GAME_MODE_SCORES);
 
-    DrawHallOfFame(last_level_nr, scores.last_added);
+    DrawHallOfFame(last_level_nr);
   }
   else if (setup.auto_play_next_level && setup.increment_levels &&
           last_level_nr < leveldir_current->last_level &&
@@ -5041,12 +5037,12 @@ void GameEnd(void)
   }
 }
 
-static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry)
+static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
+                        boolean one_score_entry_per_name)
 {
-  boolean one_score_entry_per_name = !program.many_scores_per_name;
   int i;
 
-  if (strEqual(setup.player_name, EMPTY_PLAYER_NAME))
+  if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
     return -1;
 
   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
@@ -5066,7 +5062,9 @@ static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry)
                              entry->time == 0);
 
     // prevent adding server score entries if also existing in local score file
-    if (strEqual(new_entry->tape_basename, entry->tape_basename))
+    // (special case: historic score entries have an empty tape basename entry)
+    if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
+       !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
       return -1;
 
     if (is_better || entry_is_empty)
@@ -5081,7 +5079,7 @@ static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry)
        if (one_score_entry_per_name)
        {
          for (l = i; l < MAX_SCORE_ENTRIES; l++)
-           if (strEqual(list->entry[l].name, setup.player_name))
+           if (strEqual(list->entry[l].name, new_entry->name))
              m = l;
 
          if (m == i)   // player's new highscore overwrites his old one
@@ -5099,7 +5097,7 @@ static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry)
       return i;
     }
     else if (one_score_entry_per_name &&
-            strEqual(entry->name, setup.player_name))
+            strEqual(entry->name, new_entry->name))
     {
       // player already in high score list with better score or time
 
@@ -5113,6 +5111,7 @@ static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry)
 void NewHighScore(int level_nr)
 {
   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
+  boolean one_per_name = FALSE;
 
   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
@@ -5122,34 +5121,47 @@ void NewHighScore(int level_nr)
 
   LoadScore(level_nr);
 
-  scores.last_added = addScoreEntry(&scores, &new_entry);
+  scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
 
   if (scores.last_added >= 0)
   {
     SaveScore(level_nr);
 
-    if (game.LevelSolved_SaveTape)
-    {
-      SaveScoreTape(level_nr);
-      SaveServerScore(level_nr);
-    }
+    // store last added local score entry (before merging server scores)
+    scores.last_added_local = scores.last_added;
+  }
+
+  if (game.LevelSolved_SaveTape)
+  {
+    SaveScoreTape(level_nr);
+    SaveServerScore(level_nr);
   }
 }
 
 void MergeServerScore(void)
 {
+  struct ScoreEntry last_added_entry;
+  boolean one_per_name = FALSE;
   int i;
 
+  if (scores.last_added >= 0)
+    last_added_entry = scores.entry[scores.last_added];
+
   for (i = 0; i < server_scores.num_entries; i++)
   {
-    int pos = addScoreEntry(&scores, &server_scores.entry[i]);
+    int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
 
     if (pos >= 0 && pos <= scores.last_added)
       scores.last_added++;
   }
 
   if (scores.last_added >= MAX_SCORE_ENTRIES)
-    scores.last_added = -1;
+  {
+    scores.last_added = MAX_SCORE_ENTRIES - 1;
+    scores.force_last_added = TRUE;
+
+    scores.entry[scores.last_added] = last_added_entry;
+  }
 }
 
 static int getElementMoveStepsizeExt(int x, int y, int direction)
@@ -5672,7 +5684,7 @@ static void RelocatePlayer(int jx, int jy, int el_player_raw)
      possible that the relocation target field did not contain a player element,
      but a walkable element, to which the new player was relocated -- in this
      case, restore that (already initialized!) element on the player field */
-  if (!ELEM_IS_PLAYER(element))        // player may be set on walkable element
+  if (!IS_PLAYER_ELEMENT(element))     // player may be set on walkable element
   {
     Tile[jx][jy] = element;    // restore previously existing element
   }
@@ -5842,7 +5854,7 @@ static void Explode(int ex, int ey, int phase, int mode)
 
       // !!! check this case -- currently needed for rnd_rado_negundo_v,
       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
-      else if (ELEM_IS_PLAYER(center_element))
+      else if (IS_PLAYER_ELEMENT(center_element))
        Store[x][y] = EL_EMPTY;
       else if (center_element == EL_YAMYAM)
        Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
@@ -5982,7 +5994,7 @@ static void Explode(int ex, int ey, int phase, int mode)
     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
       StorePlayer[x][y] = 0;
 
-    if (ELEM_IS_PLAYER(element))
+    if (IS_PLAYER_ELEMENT(element))
       RelocatePlayer(x, y, element);
   }
   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
@@ -8768,7 +8780,7 @@ void ContinueMoving(int x, int y)
     if (GFX_CRUMBLED(Tile[x][y]))
       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
 
-    if (ELEM_IS_PLAYER(move_leave_element))
+    if (IS_PLAYER_ELEMENT(move_leave_element))
       RelocatePlayer(x, y, move_leave_element);
   }
 
@@ -10578,7 +10590,7 @@ static void CreateFieldExt(int x, int y, int element, boolean is_change)
   int previous_move_direction = MovDir[x][y];
   int last_ce_value = CustomValue[x][y];
   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
-  boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
+  boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
   boolean add_player_onto_element = (new_element_is_player &&
                                     new_element != EL_SOKOBAN_FIELD_PLAYER &&
                                     IS_WALKABLE(old_element));
@@ -10754,7 +10766,7 @@ static boolean ChangeElement(int x, int y, int element, int page)
          (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
          (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
          (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
-        !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
+        !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
 
       if (!can_replace[xx][yy])
        complete_replace = FALSE;
@@ -10816,6 +10828,10 @@ static boolean ChangeElement(int x, int y, int element, int page)
       Store[x][y] = EL_EMPTY;
     }
 
+    // special case: element changes to player (and may be kept if walkable)
+    if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
+      CreateElementFromChange(x, y, EL_EMPTY);
+
     CreateElementFromChange(x, y, target_element);
 
     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
@@ -12157,6 +12173,9 @@ void GameActions_RND(void)
        TEST_DrawLevelField(x, y);
 
        TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
+
+       if (IS_ENVELOPE(element))
+         local_player->show_envelope = element;
       }
     }
 
@@ -14314,7 +14333,10 @@ static int DigField(struct PlayerInfo *player,
     }
     else if (IS_ENVELOPE(element))
     {
-      player->show_envelope = element;
+      boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
+
+      if (!wait_for_snapping)
+       player->show_envelope = element;
     }
     else if (element == EL_EMC_LENSES)
     {
@@ -14501,7 +14523,7 @@ static int DigField(struct PlayerInfo *player,
       if (sokoban_task_solved &&
          game.sokoban_fields_still_needed == 0 &&
          game.sokoban_objects_still_needed == 0 &&
-         (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
+         level.auto_exit_sokoban)
       {
        game.players_still_needed = 0;