X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=blobdiff_plain;f=src%2Fgame.c;h=4d1d9a858244e4283d7bbbdf7f085ec5c4fc3b95;hp=de8384e003562d4a2faea5c49122f701a9a67e2a;hb=42706644f944aae5213d9f605010537748aecf6b;hpb=59274d67d96fc0b59f572fb8e67879dfd3c2949f diff --git a/src/game.c b/src/game.c index de8384e0..4d1d9a85 100644 --- a/src/game.c +++ b/src/game.c @@ -1106,7 +1106,7 @@ void ContinueMoving(int, int); void Bang(int, int); void InitMovDir(int, int); void InitAmoebaNr(int, int); -int NewHiScore(int); +void NewHighScore(int, boolean); void TestIfGoodThingHitsBadThing(int, int, int); void TestIfBadThingHitsGoodThing(int, int, int); @@ -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))) @@ -4700,6 +4696,40 @@ void InitAmoebaNr(int x, int y) AmoebaCnt2[group_nr]++; } +static void LevelSolved_SetFinalGameValues(void) +{ + 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; +} + +static void LevelSolved_DisplayFinalGameValues(int time, int score, int health) +{ + game.LevelSolved_CountingTime = time; + game.LevelSolved_CountingScore = score; + game.LevelSolved_CountingHealth = health; + + game_panel_controls[GAME_PANEL_TIME].value = time; + game_panel_controls[GAME_PANEL_SCORE].value = score; + game_panel_controls[GAME_PANEL_HEALTH].value = health; + + DisplayGameControlValues(); +} + static void LevelSolved(void) { if (level.game_engine_type == GAME_ENGINE_TYPE_RND && @@ -4708,6 +4738,9 @@ static void LevelSolved(void) game.LevelSolved = TRUE; game.GameOver = TRUE; + + // needed here to display correct panel values while player walks into exit + LevelSolved_SetFinalGameValues(); } void GameWon(void) @@ -4730,23 +4763,8 @@ 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; + // calculate final game values after player finished walking into exit + LevelSolved_SetFinalGameValues(); game.LevelSolved_GameWon = TRUE; game.LevelSolved_SaveTape = tape.recording; @@ -4772,6 +4790,10 @@ void GameWon(void) score = score_final = game.score_final; health = health_final = game.health_final; + // update game panel values before (delayed) counting of score (if any) + LevelSolved_DisplayFinalGameValues(time, score, health); + + // if level has time score defined, calculate new final game values if (time_score > 0) { int time_final_max = 999; @@ -4805,18 +4827,13 @@ void GameWon(void) game.health_final = health_final; } + // if not counting score after game, immediately update game panel values if (level_editor_test_game || !setup.count_score_after_game) { time = time_final; score = score_final; - game.LevelSolved_CountingTime = time; - game.LevelSolved_CountingScore = score; - - game_panel_controls[GAME_PANEL_TIME].value = time; - game_panel_controls[GAME_PANEL_SCORE].value = score; - - DisplayGameControlValues(); + LevelSolved_DisplayFinalGameValues(time, score, health); } if (level.game_engine_type == GAME_ENGINE_TYPE_RND) @@ -4893,13 +4910,7 @@ void GameWon(void) if (time == time_final) score = score_final; - game.LevelSolved_CountingTime = time; - game.LevelSolved_CountingScore = score; - - game_panel_controls[GAME_PANEL_TIME].value = time; - game_panel_controls[GAME_PANEL_SCORE].value = score; - - DisplayGameControlValues(); + LevelSolved_DisplayFinalGameValues(time, score, health); if (time == time_final) StopSound(SND_GAME_LEVELTIME_BONUS); @@ -4925,13 +4936,7 @@ void GameWon(void) health += health_count_dir; score += time_score; - game.LevelSolved_CountingHealth = health; - game.LevelSolved_CountingScore = score; - - game_panel_controls[GAME_PANEL_HEALTH].value = health; - game_panel_controls[GAME_PANEL_SCORE].value = score; - - DisplayGameControlValues(); + LevelSolved_DisplayFinalGameValues(time, score, health); if (health == health_final) StopSound(SND_GAME_LEVELTIME_BONUS); @@ -4960,7 +4965,7 @@ void GameEnd(void) { // used instead of "level_nr" (needed for network games) int last_level_nr = levelset.level_nr; - int hi_pos; + boolean tape_saved = FALSE; game.LevelSolved_GameEnd = TRUE; @@ -4970,7 +4975,8 @@ void GameEnd(void) if (!global.use_envelope_request) CloseDoor(DOOR_CLOSE_1); - SaveTapeChecked_LevelSolved(tape.level_nr); // ask to save tape + // ask to save tape + tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr); // set unique basename for score tape (also saved in high score table) strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name)); @@ -5004,6 +5010,9 @@ void GameEnd(void) 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) @@ -5019,13 +5028,11 @@ void GameEnd(void) } } - 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 && @@ -5041,25 +5048,21 @@ void GameEnd(void) } } -int NewHiScore(int level_nr) +static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry, + boolean one_score_entry_per_name) { - int i, 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 (i = 0; i < MAX_SCORE_ENTRIES; i++) { - struct ScoreEntry *entry = &scores.entry[i]; - boolean score_is_better = (game.score_final > entry->score); - boolean score_is_equal = (game.score_final == entry->score); - boolean time_is_better = (game.score_time_final < entry->time); - boolean time_is_equal = (game.score_time_final == entry->time); + 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 || @@ -5069,6 +5072,12 @@ int NewHiScore(int level_nr) 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 @@ -5076,11 +5085,12 @@ int NewHiScore(int level_nr) 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(setup.player_name, scores.entry[l].name)) + if (strEqual(list->entry[l].name, new_entry->name)) m = l; if (m == i) // player's new highscore overwrites his old one @@ -5088,36 +5098,81 @@ int NewHiScore(int level_nr) } for (l = m; l > i; 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; - } + list->entry[l] = list->entry[l - 1]; } put_into_list: - strcpy(entry->tape_basename, tape.score_tape_basename); - strncpy(entry->name, setup.player_name, MAX_PLAYER_NAME_LEN); - entry->name[MAX_PLAYER_NAME_LEN] = '\0'; - entry->score = game.score_final; - entry->time = game.score_time_final; - position = i; + *entry = *new_entry; - break; + return i; } else if (one_score_entry_per_name && - !strncmp(setup.player_name, entry->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, 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); + + scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name); + + if (scores.last_added >= 0) { - SaveScoreTape(level_nr); SaveScore(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, tape_saved); + } } +} + +void MergeServerScore(void) +{ + struct ScoreEntry last_added_entry; + boolean one_per_name = FALSE; + int i; - return position; + 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) @@ -5640,7 +5695,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 } @@ -5810,7 +5865,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]; @@ -5950,7 +6005,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))) @@ -8736,7 +8791,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); } @@ -10546,7 +10601,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)); @@ -10722,7 +10777,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; @@ -10784,6 +10839,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); @@ -11654,7 +11713,7 @@ static void GameActionsExt(void) Warn("element '%s' caused endless loop in game engine", EL_NAME(recursion_loop_element)); - RequestQuitGameExt(FALSE, level_editor_test_game, message); + RequestQuitGameExt(program.headless, level_editor_test_game, message); recursion_loop_detected = FALSE; // if game should be continued @@ -12125,6 +12184,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; } } @@ -13908,7 +13970,11 @@ static void TestFieldAfterSnapping(int x, int y, int element, int direction, if (level.finish_dig_collect) { int dig_side = MV_DIR_OPPOSITE(direction); + int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X : + CE_PLAYER_COLLECTS_X); + CheckTriggeredElementChangeByPlayer(x, y, element, change_event, + player_index_bit, dig_side); CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X, player_index_bit, dig_side); } @@ -14282,7 +14348,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) { @@ -14469,7 +14538,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; @@ -16099,12 +16168,18 @@ static void UnmapGameButtonsAtSamePosition(int id) static void UnmapGameButtonsAtSamePosition_All(void) { - if (setup.show_snapshot_buttons) + if (setup.show_load_save_buttons) { UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE); UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2); UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD); } + else if (setup.show_undo_redo_buttons) + { + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO); + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2); + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO); + } else { UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP); @@ -16117,17 +16192,13 @@ static void UnmapGameButtonsAtSamePosition_All(void) } } -static void MapGameButtonsAtSamePosition(int id) +void MapLoadSaveButtons(void) { - int i; - - for (i = 0; i < NUM_GAME_BUTTONS; i++) - if (i != id && - gamebutton_info[i].pos->x == gamebutton_info[id].pos->x && - gamebutton_info[i].pos->y == gamebutton_info[id].pos->y) - MapGadget(game_gadget[i]); + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD); + UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE); - UnmapGameButtonsAtSamePosition_All(); + MapGadget(game_gadget[GAME_CTRL_ID_LOAD]); + MapGadget(game_gadget[GAME_CTRL_ID_SAVE]); } void MapUndoRedoButtons(void) @@ -16139,15 +16210,6 @@ void MapUndoRedoButtons(void) MapGadget(game_gadget[GAME_CTRL_ID_REDO]); } -void UnmapUndoRedoButtons(void) -{ - UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]); - UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]); - - MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO); - MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO); -} - void ModifyPauseButtons(void) { static int ids[] = @@ -16169,9 +16231,7 @@ static void MapGameButtonsExt(boolean on_tape) int i; for (i = 0; i < NUM_GAME_BUTTONS; i++) - if ((!on_tape || gamebutton_info[i].allowed_on_tape) && - i != GAME_CTRL_ID_UNDO && - i != GAME_CTRL_ID_REDO) + if (!on_tape || gamebutton_info[i].allowed_on_tape) MapGadget(game_gadget[i]); UnmapGameButtonsAtSamePosition_All(); @@ -16263,6 +16323,8 @@ static void GameUndoRedoExt(void) DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter); DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0); + ModifyPauseButtons(); + BackToFront(); } @@ -16271,8 +16333,12 @@ static void GameUndo(int steps) if (!CheckEngineSnapshotList()) return; + int tape_property_bits = tape.property_bits; + LoadEngineSnapshot_Undo(steps); + tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT; + GameUndoRedoExt(); } @@ -16281,8 +16347,12 @@ static void GameRedo(int steps) if (!CheckEngineSnapshotList()) return; + int tape_property_bits = tape.property_bits; + LoadEngineSnapshot_Redo(steps); + tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT; + GameUndoRedoExt(); }