void Bang(int, int);
void InitMovDir(int, int);
void InitAmoebaNr(int, int);
-int NewHighScore(int);
+void NewHighScore(int);
void TestIfGoodThingHitsBadThing(int, int, int);
void TestIfBadThingHitsGoodThing(int, int, int);
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;
{
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;
}
game.emulation = (emulate_bd ? EMU_BOULDERDASH :
- emulate_sb ? EMU_SOKOBAN :
emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
// initialize type of slippery elements
{
// 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)))
{
// used instead of "level_nr" (needed for network games)
int last_level_nr = levelset.level_nr;
- int highlight_position;
game.LevelSolved_GameEnd = TRUE;
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)
}
}
- highlight_position = NewHighScore(last_level_nr);
-
- if (highlight_position >= 0 && setup.show_scores_after_game)
+ if (scores.last_added >= 0 && setup.show_scores_after_game)
{
SetGameStatus(GAME_MODE_SCORES);
- DrawHallOfFame(last_level_nr, highlight_position);
+ DrawHallOfFame(last_level_nr);
}
else if (setup.auto_play_next_level && setup.increment_levels &&
last_level_nr < leveldir_current->last_level &&
}
}
-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++)
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 (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
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
return -1;
}
-int NewHighScore(int level_nr)
+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);
LoadScore(level_nr);
- int position = addScoreEntry(&scores, &new_entry);
+ scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
- if (position >= 0)
+ if (scores.last_added >= 0)
{
SaveScore(level_nr);
- if (game.LevelSolved_SaveTape)
- SaveScoreTape(level_nr);
+ // store last added local score entry (before merging server scores)
+ scores.last_added_local = scores.last_added;
}
- return position;
+ 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;
}
}
}
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)
{
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;