X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=blobdiff_plain;f=src%2Fgame.c;h=c1af86314e0a12c979d32a8efac9f0488d844026;hp=edf7f6ab5066ef7af0900bcdcac720f386b7e4af;hb=b55729ffeba9da45b5ae83abfd3460a5c0609e94;hpb=0dcc9cbb74fb123dcb9ef18b1e00aca24604007b diff --git a/src/game.c b/src/game.c index edf7f6ab..c1af8631 100644 --- a/src/game.c +++ b/src/game.c @@ -1119,6 +1119,8 @@ void ExitPlayer(struct PlayerInfo *); static int getInvisibleActiveFromInvisibleElement(int); static int getInvisibleFromInvisibleActiveElement(int); +static void TestFieldAfterSnapping(int, int, int, int, int); + static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS]; // for detection of endless loops, caused by custom element programming @@ -2867,12 +2869,10 @@ void UpdateAndDisplayGameControlValues(void) DisplayGameControlValues(); } -#if 0 -static void UpdateGameDoorValues(void) +void UpdateGameDoorValues(void) { UpdateGameControlValues(); } -#endif void DrawGameDoorValues(void) { @@ -3576,11 +3576,15 @@ void InitGame(void) InitGameEngine(); InitGameControlValues(); - // initialize tape actions from game when recording tape if (tape.recording) { + // initialize tape actions from game when recording tape tape.use_key_actions = game.use_key_actions; tape.use_mouse_actions = game.use_mouse_actions; + + // initialize visible playfield size when recording tape (for team mode) + tape.scr_fieldx = SCR_FIELDX; + tape.scr_fieldy = SCR_FIELDY; } // don't play tapes over network @@ -3713,6 +3717,8 @@ void InitGame(void) player->shield_normal_time_left = 0; player->shield_deadly_time_left = 0; + player->last_removed_element = EL_UNDEFINED; + player->inventory_infinite_element = EL_UNDEFINED; player->inventory_size = 0; @@ -4447,7 +4453,9 @@ void InitGame(void) game.restart_level = FALSE; game.restart_game_message = NULL; + game.request_active = FALSE; + game.request_active_or_moving = FALSE; if (level.game_engine_type == GAME_ENGINE_TYPE_MM) InitGameActions_MM(); @@ -4708,7 +4716,7 @@ void GameWon(void) { static int time_count_steps; static int time, time_final; - static int score, score_final; + static float score, score_final; // needed for time score < 10 for 10 seconds static int health, health_final; static int game_over_delay_1 = 0; static int game_over_delay_2 = 0; @@ -4716,6 +4724,8 @@ void GameWon(void) int game_over_delay_value_1 = 50; int game_over_delay_value_2 = 25; int game_over_delay_value_3 = 50; + int time_score_base = MIN(MAX(1, level.time_score_base), 10); + float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base; if (!game.LevelSolved_GameWon) { @@ -4749,19 +4759,23 @@ void GameWon(void) score = score_final = game.score_final; health = health_final = game.health_final; - if (level.score[SC_TIME_BONUS] > 0) + if (time_score > 0) { + int time_frames = 0; + if (TimeLeft > 0) { time_final = 0; - score_final += TimeLeft * level.score[SC_TIME_BONUS]; + time_frames = TimeLeft * FRAMES_PER_SECOND - TimeFrames; } else if (game.no_time_limit && TimePlayed < 999) { time_final = 999; - score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS]; + time_frames = (999 - TimePlayed) * FRAMES_PER_SECOND - TimeFrames; } + score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5; + time_count_steps = MAX(1, ABS(time_final - time) / 100); game_over_delay_1 = game_over_delay_value_1; @@ -4769,7 +4783,7 @@ void GameWon(void) if (level.game_engine_type == GAME_ENGINE_TYPE_MM) { health_final = 0; - score_final += health * level.score[SC_TIME_BONUS]; + score_final += health * time_score; game_over_delay_2 = game_over_delay_value_2; } @@ -4778,7 +4792,7 @@ void GameWon(void) game.health_final = health_final; } - if (level_editor_test_game) + if (level_editor_test_game || !setup.count_score_after_game) { time = time_final; score = score_final; @@ -4842,72 +4856,79 @@ void GameWon(void) PlaySound(SND_GAME_WINNING); } - if (game_over_delay_1 > 0) + if (setup.count_score_after_game) { - game_over_delay_1--; + if (game_over_delay_1 > 0) + { + game_over_delay_1--; - return; - } + return; + } - if (time != time_final) - { - int time_to_go = ABS(time_final - time); - int time_count_dir = (time < time_final ? +1 : -1); + if (time != time_final) + { + int time_to_go = ABS(time_final - time); + int time_count_dir = (time < time_final ? +1 : -1); - if (time_to_go < time_count_steps) - time_count_steps = 1; + if (time_to_go < time_count_steps) + time_count_steps = 1; - time += time_count_steps * time_count_dir; - score += time_count_steps * level.score[SC_TIME_BONUS]; + time += time_count_steps * time_count_dir; + score += time_count_steps * time_score; - game.LevelSolved_CountingTime = time; - game.LevelSolved_CountingScore = score; + // set final score to correct rounding differences after counting score + if (time == time_final) + score = score_final; - game_panel_controls[GAME_PANEL_TIME].value = time; - game_panel_controls[GAME_PANEL_SCORE].value = score; + game.LevelSolved_CountingTime = time; + game.LevelSolved_CountingScore = score; - DisplayGameControlValues(); + game_panel_controls[GAME_PANEL_TIME].value = time; + game_panel_controls[GAME_PANEL_SCORE].value = score; - if (time == time_final) - StopSound(SND_GAME_LEVELTIME_BONUS); - else if (setup.sound_loops) - PlaySoundLoop(SND_GAME_LEVELTIME_BONUS); - else - PlaySound(SND_GAME_LEVELTIME_BONUS); + DisplayGameControlValues(); - return; - } + if (time == time_final) + StopSound(SND_GAME_LEVELTIME_BONUS); + else if (setup.sound_loops) + PlaySoundLoop(SND_GAME_LEVELTIME_BONUS); + else + PlaySound(SND_GAME_LEVELTIME_BONUS); - if (game_over_delay_2 > 0) - { - game_over_delay_2--; + return; + } - return; - } + if (game_over_delay_2 > 0) + { + game_over_delay_2--; - if (health != health_final) - { - int health_count_dir = (health < health_final ? +1 : -1); + return; + } - health += health_count_dir; - score += level.score[SC_TIME_BONUS]; + if (health != health_final) + { + int health_count_dir = (health < health_final ? +1 : -1); - game.LevelSolved_CountingHealth = health; - game.LevelSolved_CountingScore = score; + health += health_count_dir; + score += time_score; - game_panel_controls[GAME_PANEL_HEALTH].value = health; - game_panel_controls[GAME_PANEL_SCORE].value = score; + game.LevelSolved_CountingHealth = health; + game.LevelSolved_CountingScore = score; - DisplayGameControlValues(); + game_panel_controls[GAME_PANEL_HEALTH].value = health; + game_panel_controls[GAME_PANEL_SCORE].value = score; - if (health == health_final) - StopSound(SND_GAME_LEVELTIME_BONUS); - else if (setup.sound_loops) - PlaySoundLoop(SND_GAME_LEVELTIME_BONUS); - else - PlaySound(SND_GAME_LEVELTIME_BONUS); + DisplayGameControlValues(); - return; + if (health == health_final) + StopSound(SND_GAME_LEVELTIME_BONUS); + else if (setup.sound_loops) + PlaySoundLoop(SND_GAME_LEVELTIME_BONUS); + else + PlaySound(SND_GAME_LEVELTIME_BONUS); + + return; + } } game.panel.active = FALSE; @@ -4984,7 +5005,7 @@ void GameEnd(void) hi_pos = NewHiScore(last_level_nr); - if (hi_pos >= 0 && !setup.skip_scores_after_game) + if (hi_pos >= 0 && setup.show_scores_after_game) { SetGameStatus(GAME_MODE_SCORES); @@ -11280,13 +11301,14 @@ static void CheckSingleStepMode(struct PlayerInfo *player) { if (tape.single_step && tape.recording && !tape.pausing) { - /* as it is called "single step mode", just return to pause mode when the - player stopped moving after one tile (or never starts moving at all) */ - if (!player->is_moving && - !player->is_pushing && - !player->is_dropping_pressed && - !player->effective_mouse_action.button) - TapeTogglePause(TAPE_TOGGLE_AUTOMATIC); + // as it is called "single step mode", just return to pause mode when the + // player stopped moving after one tile (or never starts moving at all) + // (reverse logic needed here in case single step mode used in team mode) + if (player->is_moving || + player->is_pushing || + player->is_dropping_pressed || + player->effective_mouse_action.button) + game.enter_single_step_mode = FALSE; } CheckSaveEngineSnapshot(player); @@ -11949,6 +11971,10 @@ void GameActions_RND(void) DrawGameDoorValues(); } + // check single step mode (set flag and clear again if any player is active) + game.enter_single_step_mode = + (tape.single_step && tape.recording && !tape.pausing); + for (i = 0; i < MAX_PLAYERS; i++) { int actual_player_action = stored_player[i].effective_action; @@ -11973,6 +11999,10 @@ void GameActions_RND(void) ScrollPlayer(&stored_player[i], SCROLL_GO_ON); } + // single step pause mode may already have been toggled by "ScrollPlayer()" + if (game.enter_single_step_mode && !tape.pausing) + TapeTogglePause(TAPE_TOGGLE_AUTOMATIC); + ScrollScreen(NULL, SCROLL_GO_ON); /* for backwards compatibility, the following code emulates a fixed bug that @@ -12025,10 +12055,17 @@ void GameActions_RND(void) MovDelay[x][y]--; if (MovDelay[x][y] <= 0) { + int element = Store[x][y]; + int move_direction = MovDir[x][y]; + int player_index_bit = Store2[x][y]; + + Store[x][y] = 0; + Store2[x][y] = 0; + RemoveField(x, y); TEST_DrawLevelField(x, y); - TestIfElementTouchesCustomElement(x, y); // for empty space + TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit); } } @@ -12446,6 +12483,8 @@ void GameActions_RND(void) static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y) { int min_x = x, min_y = y, max_x = x, max_y = y; + int scr_fieldx = getScreenFieldSizeX(); + int scr_fieldy = getScreenFieldSizeY(); int i; for (i = 0; i < MAX_PLAYERS; i++) @@ -12461,7 +12500,7 @@ static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y) max_y = MAX(max_y, jy); } - return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY); + return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy); } static boolean AllPlayersInVisibleScreen(void) @@ -13027,6 +13066,21 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) if (!player->is_pushing) TestIfElementTouchesCustomElement(jx, jy); // for empty space + if (level.finish_dig_collect && + (player->is_digging || player->is_collecting)) + { + int last_element = player->last_removed_element; + int move_direction = player->MovDir; + int enter_side = MV_DIR_OPPOSITE(move_direction); + int change_event = (player->is_digging ? CE_PLAYER_DIGS_X : + CE_PLAYER_COLLECTS_X); + + CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event, + player->index_bit, enter_side); + + player->last_removed_element = EL_UNDEFINED; + } + if (!player->active) RemovePlayer(player); } @@ -13761,7 +13815,8 @@ void ExitPlayer(struct PlayerInfo *player) game.players_still_needed--; } -static void setFieldForSnapping(int x, int y, int element, int direction) +static void SetFieldForSnapping(int x, int y, int element, int direction, + int player_index_bit) { struct ElementInfo *ei = &element_info[element]; int direction_bit = MV_DIR_TO_BIT(direction); @@ -13771,6 +13826,9 @@ static void setFieldForSnapping(int x, int y, int element, int direction) Tile[x][y] = EL_ELEMENT_SNAPPING; MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1; + MovDir[x][y] = direction; + Store[x][y] = element; + Store2[x][y] = player_index_bit; ResetGfxAnimation(x, y); @@ -13780,6 +13838,20 @@ static void setFieldForSnapping(int x, int y, int element, int direction) GfxFrame[x][y] = -1; } +static void TestFieldAfterSnapping(int x, int y, int element, int direction, + int player_index_bit) +{ + TestIfElementTouchesCustomElement(x, y); // for empty space + + if (level.finish_dig_collect) + { + int dig_side = MV_DIR_OPPOSITE(direction); + + CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X, + player_index_bit, dig_side); + } +} + /* ============================================================================= checkDiagonalPushing() @@ -14059,22 +14131,28 @@ static int DigField(struct PlayerInfo *player, PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING); - CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X, - player->index_bit, dig_side); + // use old behaviour for old levels (digging) + if (!level.finish_dig_collect) + { + CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X, + player->index_bit, dig_side); - // if digging triggered player relocation, finish digging tile - if (mode == DF_DIG && (player->jx != jx || player->jy != jy)) - setFieldForSnapping(x, y, element, move_direction); + // if digging triggered player relocation, finish digging tile + if (mode == DF_DIG && (player->jx != jx || player->jy != jy)) + SetFieldForSnapping(x, y, element, move_direction, player->index_bit); + } if (mode == DF_SNAP) { if (level.block_snap_field) - setFieldForSnapping(x, y, element, move_direction); + SetFieldForSnapping(x, y, element, move_direction, player->index_bit); else - TestIfElementTouchesCustomElement(x, y); // for empty space + TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit); - CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X, - player->index_bit, dig_side); + // use old behaviour for old levels (snapping) + if (!level.finish_dig_collect) + CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X, + player->index_bit, dig_side); } } else if (player_can_move_or_snap && IS_COLLECTIBLE(element)) @@ -14186,25 +14264,28 @@ static int DigField(struct PlayerInfo *player, RaiseScoreElement(element); PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING); - if (is_player) + // use old behaviour for old levels (collecting) + if (!level.finish_dig_collect && is_player) { CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X, player->index_bit, dig_side); // if collecting triggered player relocation, finish collecting tile if (mode == DF_DIG && (player->jx != jx || player->jy != jy)) - setFieldForSnapping(x, y, element, move_direction); + SetFieldForSnapping(x, y, element, move_direction, player->index_bit); } if (mode == DF_SNAP) { if (level.block_snap_field) - setFieldForSnapping(x, y, element, move_direction); + SetFieldForSnapping(x, y, element, move_direction, player->index_bit); else - TestIfElementTouchesCustomElement(x, y); // for empty space + TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit); - CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X, - player->index_bit, dig_side); + // use old behaviour for old levels (snapping) + if (!level.finish_dig_collect) + CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X, + player->index_bit, dig_side); } } else if (player_can_move_or_snap && IS_PUSHABLE(element)) @@ -14542,6 +14623,8 @@ static int DigField(struct PlayerInfo *player, { player->is_collecting = !player->is_digging; player->is_active = TRUE; + + player->last_removed_element = element; } }