+ SaveLevelSetup_SeriesInfo();
+ }
+
+ // save score and score tape before potentially erasing tape below
+ NewHighScore(last_level_nr, tape_saved);
+
+ if (setup.increment_levels &&
+ level_nr < leveldir_current->last_level &&
+ !network_playing)
+ {
+ level_nr++; // advance to next level
+ TapeErase(); // start with empty tape
+
+ if (setup.auto_play_next_level)
+ {
+ scores.continue_playing = TRUE;
+ scores.next_level_nr = level_nr;
+
+ LoadLevel(level_nr);
+
+ SaveLevelSetup_SeriesInfo();
+ }
+ }
+
+ if (scores.last_added >= 0 && setup.show_scores_after_game)
+ {
+ SetGameStatus(GAME_MODE_SCORES);
+
+ DrawHallOfFame(last_level_nr);
+ }
+ else if (scores.continue_playing)
+ {
+ StartGameActions(network.enabled, setup.autorecord, level.random_seed);
+ }
+ else
+ {
+ SetGameStatus(GAME_MODE_MAIN);
+
+ DrawMainMenu();
+ }
+}
+
+static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
+ boolean one_score_entry_per_name)
+{
+ int i;
+
+ if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
+ return -1;
+
+ for (i = 0; i < MAX_SCORE_ENTRIES; i++)
+ {
+ struct ScoreEntry *entry = &list->entry[i];
+ boolean score_is_better = (new_entry->score > entry->score);
+ boolean score_is_equal = (new_entry->score == entry->score);
+ boolean time_is_better = (new_entry->time < entry->time);
+ boolean time_is_equal = (new_entry->time == entry->time);
+ boolean better_by_score = (score_is_better ||
+ (score_is_equal && time_is_better));
+ boolean better_by_time = (time_is_better ||
+ (time_is_equal && score_is_better));
+ boolean is_better = (level.rate_time_over_score ? better_by_time :
+ better_by_score);
+ boolean entry_is_empty = (entry->score == 0 &&
+ entry->time == 0);
+
+ // prevent adding server score entries if also existing in local score file
+ // (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))
+ {
+ // add fields from server score entry not stored in local score entry
+ // (currently, this means setting platform, version and country fields;
+ // in rare cases, this may also correct an invalid score value, as
+ // historic scores might have been truncated to 16-bit values locally)
+ *entry = *new_entry;
+
+ return -1;
+ }
+
+ if (is_better || entry_is_empty)
+ {
+ // player has made it to the hall of fame
+
+ if (i < MAX_SCORE_ENTRIES - 1)
+ {
+ int m = MAX_SCORE_ENTRIES - 1;
+ int l;
+
+ if (one_score_entry_per_name)
+ {
+ for (l = i; l < MAX_SCORE_ENTRIES; l++)
+ if (strEqual(list->entry[l].name, new_entry->name))
+ m = l;
+
+ if (m == i) // player's new highscore overwrites his old one
+ goto put_into_list;
+ }
+
+ for (l = m; l > i; l--)
+ list->entry[l] = list->entry[l - 1];
+ }
+
+ put_into_list:
+
+ *entry = *new_entry;
+
+ return i;
+ }
+ else if (one_score_entry_per_name &&
+ strEqual(entry->name, new_entry->name))
+ {
+ // player already in high score list with better score or time
+
+ return -1;
+ }
+ }
+
+ // special case: new score is beyond the last high score list position
+ return MAX_SCORE_ENTRIES;
+}
+
+void NewHighScore(int level_nr, boolean tape_saved)
+{
+ 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);
+
+ new_entry.score = game.score_final;
+ new_entry.time = game.score_time_final;
+
+ LoadScore(level_nr);