void Bang(int, int);
void InitMovDir(int, int);
void InitAmoebaNr(int, int);
-int NewHiScore(int);
+void NewHighScore(int);
void TestIfGoodThingHitsBadThing(int, int, int);
void TestIfBadThingHitsGoodThing(int, int, int);
{
// 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)))
{
// 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)))
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)))
game.LevelSolved = TRUE;
game.GameOver = TRUE;
-
- game.time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
- game.score_time_final = (level.use_step_counter ? TimePlayed :
- TimePlayed * FRAMES_PER_SECOND + TimeFrames);
-
- game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
- game_em.lev->score :
- level.game_engine_type == GAME_ENGINE_TYPE_MM ?
- game_mm.score :
- game.score);
-
- game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
- MM_HEALTH(game_mm.laser_overload_value) :
- game.health);
-
- game.LevelSolved_CountingTime = game.time_final;
- game.LevelSolved_CountingScore = game.score_final;
- game.LevelSolved_CountingHealth = game.health_final;
}
void GameWon(void)
if (local_player->active && local_player->MovPos)
return;
+ game.time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
+ game.score_time_final = (level.use_step_counter ? TimePlayed :
+ TimePlayed * FRAMES_PER_SECOND + TimeFrames);
+
+ game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
+ game_em.lev->score :
+ level.game_engine_type == GAME_ENGINE_TYPE_MM ?
+ game_mm.score :
+ game.score);
+
+ game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
+ MM_HEALTH(game_mm.laser_overload_value) :
+ game.health);
+
+ game.LevelSolved_CountingTime = game.time_final;
+ game.LevelSolved_CountingScore = game.score_final;
+ game.LevelSolved_CountingHealth = game.health_final;
+
game.LevelSolved_GameWon = TRUE;
game.LevelSolved_SaveTape = tape.recording;
game.LevelSolved_SaveScore = !tape.playing;
{
// used instead of "level_nr" (needed for network games)
int last_level_nr = levelset.level_nr;
- int hi_pos;
game.LevelSolved_GameEnd = TRUE;
CloseDoor(DOOR_CLOSE_1);
SaveTapeChecked_LevelSolved(tape.level_nr); // ask to save tape
+
+ // set unique basename for score tape (also saved in high score table)
+ strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
}
// if no tape is to be saved, close both doors simultaneously
SaveLevelSetup_SeriesInfo();
}
+ // save score and score tape before potentially erasing tape below
+ NewHighScore(last_level_nr);
+
if (setup.increment_levels &&
level_nr < leveldir_current->last_level &&
!network_playing)
}
}
- hi_pos = NewHiScore(last_level_nr);
-
- if (hi_pos >= 0 && setup.show_scores_after_game)
+ if (scores.last_added >= 0 && setup.show_scores_after_game)
{
SetGameStatus(GAME_MODE_SCORES);
- DrawHallOfFame(last_level_nr, hi_pos);
+ DrawHallOfFame(last_level_nr);
}
else if (setup.auto_play_next_level && setup.increment_levels &&
last_level_nr < leveldir_current->last_level &&
}
}
-int NewHiScore(int level_nr)
+static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
+ boolean one_score_entry_per_name)
{
- int k, l;
- int position = -1;
- boolean one_score_entry_per_name = !program.many_scores_per_name;
-
- LoadScore(level_nr);
+ int i;
- if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
- game.score_final < scores.entry[MAX_SCORE_ENTRIES - 1].score)
+ if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
return -1;
- for (k = 0; k < MAX_SCORE_ENTRIES; k++)
- {
- boolean score_is_better = (game.score_final > scores.entry[k].score);
- boolean score_is_equal = (game.score_final == scores.entry[k].score);
- boolean time_is_better = (game.score_time_final < scores.entry[k].time);
-
- if (score_is_better || (score_is_equal && time_is_better))
+ 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))
+ return -1;
+
+ if (is_better || entry_is_empty)
{
// player has made it to the hall of fame
- if (k < MAX_SCORE_ENTRIES - 1)
+ if (i < MAX_SCORE_ENTRIES - 1)
{
int m = MAX_SCORE_ENTRIES - 1;
+ int l;
if (one_score_entry_per_name)
{
- for (l = k; l < MAX_SCORE_ENTRIES; l++)
- if (strEqual(setup.player_name, scores.entry[l].name))
+ for (l = i; l < MAX_SCORE_ENTRIES; l++)
+ if (strEqual(list->entry[l].name, new_entry->name))
m = l;
- if (m == k) // player's new highscore overwrites his old one
+ if (m == i) // player's new highscore overwrites his old one
goto put_into_list;
}
- for (l = m; l > k; l--)
- {
- strcpy(scores.entry[l].name, scores.entry[l - 1].name);
- scores.entry[l].score = scores.entry[l - 1].score;
- scores.entry[l].time = scores.entry[l - 1].time;
- }
+ for (l = m; l > i; l--)
+ list->entry[l] = list->entry[l - 1];
}
put_into_list:
- strncpy(scores.entry[k].name, setup.player_name, MAX_PLAYER_NAME_LEN);
- scores.entry[k].name[MAX_PLAYER_NAME_LEN] = '\0';
- scores.entry[k].score = game.score_final;
- scores.entry[k].time = game.score_time_final;
- position = k;
+ *entry = *new_entry;
- break;
+ return i;
}
else if (one_score_entry_per_name &&
- !strncmp(setup.player_name, scores.entry[k].name,
- MAX_PLAYER_NAME_LEN))
- break; // player already there with a higher score
+ strEqual(entry->name, new_entry->name))
+ {
+ // player already in high score list with better score or time
+
+ return -1;
+ }
}
- if (position >= 0)
+ return -1;
+}
+
+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);
+
+ new_entry.score = game.score_final;
+ new_entry.time = game.score_time_final;
+
+ LoadScore(level_nr);
+
+ scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
+
+ if (scores.last_added >= 0)
+ {
SaveScore(level_nr);
- return position;
+ // 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], one_per_name);
+
+ if (pos >= 0 && pos <= scores.last_added)
+ scores.last_added++;
+ }
+
+ if (scores.last_added >= MAX_SCORE_ENTRIES)
+ {
+ 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)
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
}
// !!! 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];
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)))
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);
}
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));
(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;
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);
TEST_DrawLevelField(x, y);
TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
+
+ if (IS_ENVELOPE(element))
+ local_player->show_envelope = element;
}
}
RemovePlayer(player);
}
- if (!game.LevelSolved && level.use_step_counter)
+ if (level.use_step_counter)
{
int i;
{
TimeLeft--;
- if (TimeLeft <= 10 && setup.time_limit)
+ if (TimeLeft <= 10 && setup.time_limit && !game.LevelSolved)
PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
DisplayGameControlValues();
- if (!TimeLeft && setup.time_limit)
+ if (!TimeLeft && setup.time_limit && !game.LevelSolved)
for (i = 0; i < MAX_PLAYERS; i++)
KillPlayer(&stored_player[i]);
}
}
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)
{