X-Git-Url: https://git.artsoft.org/?a=blobdiff_plain;f=src%2Fgame.c;h=9127a1b6a5be48bc22c593b6ff728b8a02269192;hb=4ed68b54ed9bed72d87eac4dd7b117fc4b8022d6;hp=e6e78cbf92b0c713ed52641422fb08e6c7c1254d;hpb=97102b3b1d4db840689412618e9e787e76e3cb62;p=rocksndiamonds.git diff --git a/src/game.c b/src/game.c index e6e78cbf..9127a1b6 100644 --- a/src/game.c +++ b/src/game.c @@ -89,7 +89,7 @@ // game panel display and control definitions #define GAME_PANEL_LEVEL_NUMBER 0 #define GAME_PANEL_GEMS 1 -#define GAME_PANEL_GEMS_TOTAL 2 +#define GAME_PANEL_GEMS_NEEDED 2 #define GAME_PANEL_GEMS_COLLECTED 3 #define GAME_PANEL_GEMS_SCORE 4 #define GAME_PANEL_INVENTORY_COUNT 5 @@ -246,8 +246,8 @@ static struct GamePanelControlInfo game_panel_controls[] = TYPE_INTEGER, }, { - GAME_PANEL_GEMS_TOTAL, - &game.panel.gems_total, + GAME_PANEL_GEMS_NEEDED, + &game.panel.gems_needed, TYPE_INTEGER, }, { @@ -1835,6 +1835,29 @@ static void InitPlayerField(int x, int y, int element, boolean init_game) } } +static void InitFieldForEngine_RND(int x, int y) +{ + int element = Tile[x][y]; + + // convert BD engine elements to corresponding R'n'D engine elements + element = (element == EL_BD_EMPTY ? EL_EMPTY : + element == EL_BD_PLAYER ? EL_PLAYER_1 : + element == EL_BD_INBOX ? EL_PLAYER_1 : + element == EL_BD_SAND ? EL_SAND : + element == EL_BD_STEELWALL ? EL_STEELWALL : + element == EL_BD_EXIT_CLOSED ? EL_EXIT_CLOSED : + element == EL_BD_EXIT_OPEN ? EL_EXIT_OPEN : + element); + + Tile[x][y] = element; +} + +static void InitFieldForEngine(int x, int y) +{ + if (level.game_engine_type == GAME_ENGINE_TYPE_RND) + InitFieldForEngine_RND(x, y); +} + static void InitField(int x, int y, boolean init_game) { int element = Tile[x][y]; @@ -2262,6 +2285,8 @@ static void UpdateGameControlValues(void) int i, k; int time = (game.LevelSolved ? game.LevelSolved_CountingTime : + level.game_engine_type == GAME_ENGINE_TYPE_BD ? + game_bd.time_played : level.game_engine_type == GAME_ENGINE_TYPE_EM ? game_em.lev->time : level.game_engine_type == GAME_ENGINE_TYPE_SP ? @@ -2271,6 +2296,8 @@ static void UpdateGameControlValues(void) game.no_level_time_limit ? TimePlayed : TimeLeft); int score = (game.LevelSolved ? game.LevelSolved_CountingScore : + level.game_engine_type == GAME_ENGINE_TYPE_BD ? + game_bd.score : level.game_engine_type == GAME_ENGINE_TYPE_EM ? game_em.lev->score : level.game_engine_type == GAME_ENGINE_TYPE_SP ? @@ -2278,17 +2305,25 @@ static void UpdateGameControlValues(void) level.game_engine_type == GAME_ENGINE_TYPE_MM ? game_mm.score : game.score); - int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ? + int gems = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? + game_bd.gems_still_needed : + level.game_engine_type == GAME_ENGINE_TYPE_EM ? game_em.lev->gems_needed : level.game_engine_type == GAME_ENGINE_TYPE_SP ? game_sp.infotrons_still_needed : level.game_engine_type == GAME_ENGINE_TYPE_MM ? game_mm.kettles_still_needed : game.gems_still_needed); - int gems_total = level.gems_needed; - int gems_collected = gems_total - gems; - int gems_score = level.score[SC_EMERALD]; - int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ? + int gems_needed = level.gems_needed; + int gems_collected = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? + game_bd.game->cave->diamonds_collected : + gems_needed - gems); + int gems_score = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? + game_bd.game->cave->diamond_value : + level.score[SC_EMERALD]); + int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? + game_bd.gems_still_needed > 0 : + level.game_engine_type == GAME_ENGINE_TYPE_EM ? game_em.lev->gems_needed > 0 : level.game_engine_type == GAME_ENGINE_TYPE_SP ? game_sp.infotrons_still_needed > 0 : @@ -2313,7 +2348,7 @@ static void UpdateGameControlValues(void) // used instead of "level_nr" (for network games) game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr; game_panel_controls[GAME_PANEL_GEMS].value = gems; - game_panel_controls[GAME_PANEL_GEMS_TOTAL].value = gems_total; + game_panel_controls[GAME_PANEL_GEMS_NEEDED].value = gems_needed; game_panel_controls[GAME_PANEL_GEMS_COLLECTED].value = gems_collected; game_panel_controls[GAME_PANEL_GEMS_SCORE].value = gems_score; @@ -2710,7 +2745,7 @@ static void DisplayGameControlValues(void) if (PANEL_DEACTIVATED(pos)) continue; - if (pos->class == get_hash_from_key("extra_panel_items") && + if (pos->class == get_hash_from_string("extra_panel_items") && !setup.prefer_extra_panel_items) continue; @@ -2802,7 +2837,7 @@ static void DisplayGameControlValues(void) int width, height; int dst_x = PANEL_XPOS(pos); int dst_y = PANEL_YPOS(pos); - boolean skip = (pos->class == get_hash_from_key("mm_engine_only") && + boolean skip = (pos->class == get_hash_from_string("mm_engine_only") && level.game_engine_type != GAME_ENGINE_TYPE_MM); if (graphic != IMG_UNDEFINED && !skip) @@ -3995,6 +4030,8 @@ void InitGame(void) SCAN_PLAYFIELD(x, y) { + InitFieldForEngine(x, y); + if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y])) emulate_bd = FALSE; if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y])) @@ -4491,7 +4528,11 @@ void InitGame(void) scroll_y = game.forced_scroll_y; // !!! FIX THIS (START) !!! - if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + if (level.game_engine_type == GAME_ENGINE_TYPE_BD) + { + InitGameEngine_BD(); + } + else if (level.game_engine_type == GAME_ENGINE_TYPE_EM) { InitGameEngine_EM(); } @@ -4818,14 +4859,14 @@ void InitAmoebaNr(int x, int y) static void LevelSolved_SetFinalGameValues(void) { - game.time_final = (game.no_level_time_limit ? TimePlayed : TimeLeft); + game.time_final = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? game_bd.time_played : + game.no_level_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_final = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? game_bd.score : + 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 ? @@ -4865,6 +4906,31 @@ static void LevelSolved(void) LevelSolved_SetFinalGameValues(); } +static boolean AdvanceToNextLevel(void) +{ + 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(); + } + + return TRUE; + } + + return FALSE; +} + void GameWon(void) { static int time_count_steps; @@ -4939,7 +5005,13 @@ void GameWon(void) time_count_steps = MAX(1, ABS(time_final - time) / 100); - if (level.game_engine_type == GAME_ENGINE_TYPE_MM) + if (level.game_engine_type == GAME_ENGINE_TYPE_BD) + { + // keep previous values (final values already processed here) + time_final = time; + score_final = score; + } + else if (level.game_engine_type == GAME_ENGINE_TYPE_MM) { health_final = 0; score_final += health * time_score; @@ -4950,7 +5022,8 @@ void GameWon(void) } // if not counting score after game, immediately update game panel values - if (level_editor_test_game || !setup.count_score_after_game) + if (level_editor_test_game || !setup.count_score_after_game || + level.game_engine_type == GAME_ENGINE_TYPE_BD) { time = time_final; score = score_final; @@ -5089,7 +5162,11 @@ void GameEnd(void) int last_level_nr = levelset.level_nr; boolean tape_saved = FALSE; - game.LevelSolved_GameEnd = TRUE; + // Important note: This function is not only called after "GameWon()", but also after + // "game over" (if automatically asking for restarting the game is disabled in setup) + + if (game.LevelSolved) + game.LevelSolved_GameEnd = TRUE; if (game.LevelSolved_SaveTape && !score_info_tape_play) { @@ -5116,7 +5193,7 @@ void GameEnd(void) return; } - if (!game.LevelSolved_SaveScore) + if (!game.GamePlayed || (!game.LevelSolved_SaveScore && !level.bd_intermission)) { SetGameStatus(GAME_MODE_MAIN); @@ -5133,27 +5210,13 @@ void GameEnd(void) } // 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 (game.LevelSolved_SaveScore) + NewHighScore(last_level_nr, tape_saved); - if (setup.auto_play_next_level) - { - scores.continue_playing = TRUE; - scores.next_level_nr = level_nr; + // increment and load next level (if possible and not configured otherwise) + AdvanceToNextLevel(); - LoadLevel(level_nr); - - SaveLevelSetup_SeriesInfo(); - } - } - - if (scores.last_added >= 0 && setup.show_scores_after_game) + if (game.LevelSolved_SaveScore && scores.last_added >= 0 && setup.show_scores_after_game) { SetGameStatus(GAME_MODE_SCORES); @@ -11622,7 +11685,22 @@ static void SetTapeActionFromMouseAction(byte *tape_action, static void CheckLevelSolved(void) { - if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + if (level.game_engine_type == GAME_ENGINE_TYPE_BD) + { + if (game_bd.level_solved && + !game_bd.game_over) // game won + { + LevelSolved(); + + game_bd.game_over = TRUE; + + game.all_players_gone = TRUE; + } + + if (game_bd.game_over) // game lost + game.all_players_gone = TRUE; + } + else if (level.game_engine_type == GAME_ENGINE_TYPE_EM) { if (game_em.level_solved && !game_em.game_over) // game won @@ -11669,6 +11747,27 @@ static void CheckLevelSolved(void) } } +static void PlayTimeoutSound(int seconds_left) +{ + // will be played directly by BD engine (for classic bonus time sounds) + if (level.game_engine_type == GAME_ENGINE_TYPE_BD && checkBonusTime_BD()) + return; + + // try to use individual "running out of time" sound for each second left + int sound = SND_GAME_RUNNING_OUT_OF_TIME_0 - seconds_left; + + // if special sound per second not defined, use default sound + if (getSoundInfoEntryFilename(sound) == NULL) + sound = SND_GAME_RUNNING_OUT_OF_TIME; + + // if out of time, but player still alive, play special "timeout" sound, if defined + if (seconds_left == 0 && !checkGameFailed()) + if (getSoundInfoEntryFilename(SND_GAME_TIMEOUT) != NULL) + sound = SND_GAME_TIMEOUT; + + PlaySound(sound); +} + static void CheckLevelTime_StepCounter(void) { int i; @@ -11680,7 +11779,7 @@ static void CheckLevelTime_StepCounter(void) TimeLeft--; if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved) - PlaySound(SND_GAME_RUNNING_OUT_OF_TIME); + PlayTimeoutSound(TimeLeft); game_panel_controls[GAME_PANEL_TIME].value = TimeLeft; @@ -11700,9 +11799,24 @@ static void CheckLevelTime_StepCounter(void) static void CheckLevelTime(void) { + int frames_per_second = FRAMES_PER_SECOND; int i; - if (TimeFrames >= FRAMES_PER_SECOND) + if (level.game_engine_type == GAME_ENGINE_TYPE_BD) + { + // level time may be running slower in native BD engine + frames_per_second = getFramesPerSecond_BD(); + + // if native engine time changed, force main engine time change + if (getTimeLeft_BD() < TimeLeft) + TimeFrames = frames_per_second; + + // if last second running, wait for native engine time to exactly reach zero + if (getTimeLeft_BD() == 1 && TimeLeft == 1) + TimeFrames = frames_per_second - 1; + } + + if (TimeFrames >= frames_per_second) { TimeFrames = 0; @@ -11728,7 +11842,7 @@ static void CheckLevelTime(void) TimeLeft--; if (TimeLeft <= 10 && game.time_limit) - PlaySound(SND_GAME_RUNNING_OUT_OF_TIME); + PlayTimeoutSound(TimeLeft); /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value is reset from other values in UpdateGameDoorValues() -- FIX THIS */ @@ -11737,11 +11851,20 @@ static void CheckLevelTime(void) if (!TimeLeft && game.time_limit) { - if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + if (level.game_engine_type == GAME_ENGINE_TYPE_BD) + { + if (game_bd.game->cave->player_state == GD_PL_LIVING) + game_bd.game->cave->player_state = GD_PL_TIMEOUT; + } + else if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + { game_em.lev->killed_out_of_time = TRUE; + } else + { for (i = 0; i < MAX_PLAYERS; i++) KillPlayer(&stored_player[i]); + } } } else if (game.no_level_time_limit && !game.all_players_gone) @@ -11772,10 +11895,20 @@ void AdvanceFrameAndPlayerCounters(int player_nr) { int i; + // handle game and tape time differently for native BD game engine + + // tape time is running in native BD engine even if player is not hatched yet + if (!checkGameRunning()) + return; + // advance frame counters (global frame counter and tape time frame counter) FrameCounter++; TapeTimeFrames++; + // level time is running in native BD engine after player is being hatched + if (!checkGamePlaying()) + return; + // advance time frame counter (used to control available time to solve level) TimeFrames++; @@ -12126,7 +12259,11 @@ static void GameActionsExt(void) game.snapshot.last_action[i] = stored_player[i].effective_action; } - if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + if (level.game_engine_type == GAME_ENGINE_TYPE_BD) + { + GameActions_BD_Main(); + } + else if (level.game_engine_type == GAME_ENGINE_TYPE_EM) { GameActions_EM_Main(); } @@ -12193,6 +12330,17 @@ void GameActions(void) GameActions_CheckSaveEngineSnapshot(); } +void GameActions_BD_Main(void) +{ + byte effective_action[MAX_PLAYERS]; + int i; + + for (i = 0; i < MAX_PLAYERS; i++) + effective_action[i] = stored_player[i].effective_action; + + GameActions_BD(effective_action); +} + void GameActions_EM_Main(void) { byte effective_action[MAX_PLAYERS]; @@ -15287,15 +15435,15 @@ void InitPlayLevelSound(void) loop_sound_volume = checked_calloc(num_sounds * sizeof(int)); } -static void PlayLevelSound(int x, int y, int nr) +static void PlayLevelSoundExt(int x, int y, int nr, boolean is_loop_sound) { int sx = SCREENX(x), sy = SCREENY(y); int volume, stereo_position; int max_distance = 8; - int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND); + int type = (is_loop_sound ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND); - if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) || - (!setup.sound_loops && IS_LOOP_SOUND(nr))) + if ((!setup.sound_simple && !is_loop_sound) || + (!setup.sound_loops && is_loop_sound)) return; if (!IN_LEV_FIELD(x, y) || @@ -15317,7 +15465,7 @@ static void PlayLevelSound(int x, int y, int nr) (sx + max_distance) * SOUND_MAX_LEFT2RIGHT / (SCR_FIELDX + 2 * max_distance)); - if (IS_LOOP_SOUND(nr)) + if (is_loop_sound) { /* This assures that quieter loop sounds do not overwrite louder ones, while restarting sound volume comparison with each new game frame. */ @@ -15332,6 +15480,11 @@ static void PlayLevelSound(int x, int y, int nr) PlaySoundExt(nr, volume, stereo_position, type); } +static void PlayLevelSound(int x, int y, int nr) +{ + PlayLevelSoundExt(x, y, nr, IS_LOOP_SOUND(nr)); +} + static void PlayLevelSoundNearest(int x, int y, int sound_action) { PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) : @@ -15420,6 +15573,338 @@ static void PlayLevelMusic(void) PlayMusicLoop(music_nr); } +static int getSoundAction_BD(int sample) +{ + switch (sample) + { + case GD_S_STONE_PUSHING: + case GD_S_MEGA_STONE_PUSHING: + case GD_S_FLYING_STONE_PUSHING: + case GD_S_WAITING_STONE_PUSHING: + case GD_S_CHASING_STONE_PUSHING: + case GD_S_NUT_PUSHING: + case GD_S_NITRO_PACK_PUSHING: + case GD_S_BLADDER_PUSHING: + case GD_S_BOX_PUSHING: + return ACTION_PUSHING; + + case GD_S_STONE_FALLING: + case GD_S_MEGA_STONE_FALLING: + case GD_S_FLYING_STONE_FALLING: + case GD_S_NUT_FALLING: + case GD_S_DIRT_BALL_FALLING: + case GD_S_DIRT_LOOSE_FALLING: + case GD_S_NITRO_PACK_FALLING: + case GD_S_FALLING_WALL_FALLING: + return ACTION_FALLING; + + case GD_S_STONE_IMPACT: + case GD_S_MEGA_STONE_IMPACT: + case GD_S_FLYING_STONE_IMPACT: + case GD_S_NUT_IMPACT: + case GD_S_DIRT_BALL_IMPACT: + case GD_S_DIRT_LOOSE_IMPACT: + case GD_S_NITRO_PACK_IMPACT: + case GD_S_FALLING_WALL_IMPACT: + return ACTION_IMPACT; + + case GD_S_NUT_CRACKING: + return ACTION_BREAKING; + + case GD_S_EXPANDING_WALL: + case GD_S_WALL_REAPPEARING: + case GD_S_SLIME: + case GD_S_LAVA: + case GD_S_ACID_SPREADING: + return ACTION_GROWING; + + case GD_S_DIAMOND_COLLECTING: + case GD_S_FLYING_DIAMOND_COLLECTING: + case GD_S_SKELETON_COLLECTING: + case GD_S_PNEUMATIC_COLLECTING: + case GD_S_BOMB_COLLECTING: + case GD_S_CLOCK_COLLECTING: + case GD_S_SWEET_COLLECTING: + case GD_S_KEY_COLLECTING: + case GD_S_DIAMOND_KEY_COLLECTING: + return ACTION_COLLECTING; + + case GD_S_BOMB_PLACING: + case GD_S_REPLICATOR: + return ACTION_DROPPING; + + case GD_S_BLADDER_MOVING: + return ACTION_MOVING; + + case GD_S_BLADDER_SPENDER: + case GD_S_BLADDER_CONVERTING: + case GD_S_GRAVITY_CHANGING: + return ACTION_CHANGING; + + case GD_S_BITER_EATING: + return ACTION_EATING; + + case GD_S_DOOR_OPENING: + case GD_S_CRACKING: + return ACTION_OPENING; + + case GD_S_DIRT_WALKING: + return ACTION_DIGGING; + + case GD_S_EMPTY_WALKING: + return ACTION_WALKING; + + case GD_S_SWITCH_BITER: + case GD_S_SWITCH_CREATURES: + case GD_S_SWITCH_GRAVITY: + case GD_S_SWITCH_EXPANDING: + case GD_S_SWITCH_CONVEYOR: + case GD_S_SWITCH_REPLICATOR: + case GD_S_STIRRING: + return ACTION_ACTIVATING; + + case GD_S_TELEPORTER: + return ACTION_PASSING; + + case GD_S_EXPLODING: + case GD_S_BOMB_EXPLODING: + case GD_S_GHOST_EXPLODING: + case GD_S_VOODOO_EXPLODING: + case GD_S_NITRO_PACK_EXPLODING: + return ACTION_EXPLODING; + + case GD_S_COVERING: + case GD_S_AMOEBA: + case GD_S_MAGIC_WALL: + case GD_S_PNEUMATIC_HAMMER: + case GD_S_WATER: + return ACTION_ACTIVE; + + case GD_S_DIAMOND_FALLING_RANDOM: + case GD_S_DIAMOND_FALLING_1: + case GD_S_DIAMOND_FALLING_2: + case GD_S_DIAMOND_FALLING_3: + case GD_S_DIAMOND_FALLING_4: + case GD_S_DIAMOND_FALLING_5: + case GD_S_DIAMOND_FALLING_6: + case GD_S_DIAMOND_FALLING_7: + case GD_S_DIAMOND_FALLING_8: + case GD_S_DIAMOND_IMPACT_RANDOM: + case GD_S_DIAMOND_IMPACT_1: + case GD_S_DIAMOND_IMPACT_2: + case GD_S_DIAMOND_IMPACT_3: + case GD_S_DIAMOND_IMPACT_4: + case GD_S_DIAMOND_IMPACT_5: + case GD_S_DIAMOND_IMPACT_6: + case GD_S_DIAMOND_IMPACT_7: + case GD_S_DIAMOND_IMPACT_8: + case GD_S_FLYING_DIAMOND_FALLING_RANDOM: + case GD_S_FLYING_DIAMOND_FALLING_1: + case GD_S_FLYING_DIAMOND_FALLING_2: + case GD_S_FLYING_DIAMOND_FALLING_3: + case GD_S_FLYING_DIAMOND_FALLING_4: + case GD_S_FLYING_DIAMOND_FALLING_5: + case GD_S_FLYING_DIAMOND_FALLING_6: + case GD_S_FLYING_DIAMOND_FALLING_7: + case GD_S_FLYING_DIAMOND_FALLING_8: + case GD_S_FLYING_DIAMOND_IMPACT_RANDOM: + case GD_S_FLYING_DIAMOND_IMPACT_1: + case GD_S_FLYING_DIAMOND_IMPACT_2: + case GD_S_FLYING_DIAMOND_IMPACT_3: + case GD_S_FLYING_DIAMOND_IMPACT_4: + case GD_S_FLYING_DIAMOND_IMPACT_5: + case GD_S_FLYING_DIAMOND_IMPACT_6: + case GD_S_FLYING_DIAMOND_IMPACT_7: + case GD_S_FLYING_DIAMOND_IMPACT_8: + case GD_S_TIMEOUT_0: + case GD_S_TIMEOUT_1: + case GD_S_TIMEOUT_2: + case GD_S_TIMEOUT_3: + case GD_S_TIMEOUT_4: + case GD_S_TIMEOUT_5: + case GD_S_TIMEOUT_6: + case GD_S_TIMEOUT_7: + case GD_S_TIMEOUT_8: + case GD_S_TIMEOUT_9: + case GD_S_TIMEOUT_10: + case GD_S_BONUS_LIFE: + // trigger special post-processing (and force sound to be non-looping) + return ACTION_OTHER; + + case GD_S_AMOEBA_MAGIC: + case GD_S_FINISHED: + // trigger special post-processing (and force sound to be looping) + return ACTION_DEFAULT; + + default: + return ACTION_DEFAULT; + } +} + +static int getSoundEffect_BD(int element_bd, int sample) +{ + int sound_action = getSoundAction_BD(sample); + int sound_effect = element_info[SND_ELEMENT(element_bd)].sound[sound_action]; + int nr; + + // standard sounds + if (sound_action != ACTION_OTHER && + sound_action != ACTION_DEFAULT) + return sound_effect; + + // special post-processing for some sounds + switch (sample) + { + case GD_S_DIAMOND_FALLING_RANDOM: + case GD_S_DIAMOND_FALLING_1: + case GD_S_DIAMOND_FALLING_2: + case GD_S_DIAMOND_FALLING_3: + case GD_S_DIAMOND_FALLING_4: + case GD_S_DIAMOND_FALLING_5: + case GD_S_DIAMOND_FALLING_6: + case GD_S_DIAMOND_FALLING_7: + case GD_S_DIAMOND_FALLING_8: + nr = (sample == GD_S_DIAMOND_FALLING_RANDOM ? GetSimpleRandom(8) : + sample - GD_S_DIAMOND_FALLING_1); + sound_effect = SND_BD_DIAMOND_FALLING_RANDOM_1 + nr; + + if (getSoundInfoEntryFilename(sound_effect) == NULL) + sound_effect = SND_BD_DIAMOND_FALLING; + break; + + case GD_S_DIAMOND_IMPACT_RANDOM: + case GD_S_DIAMOND_IMPACT_1: + case GD_S_DIAMOND_IMPACT_2: + case GD_S_DIAMOND_IMPACT_3: + case GD_S_DIAMOND_IMPACT_4: + case GD_S_DIAMOND_IMPACT_5: + case GD_S_DIAMOND_IMPACT_6: + case GD_S_DIAMOND_IMPACT_7: + case GD_S_DIAMOND_IMPACT_8: + nr = (sample == GD_S_DIAMOND_IMPACT_RANDOM ? GetSimpleRandom(8) : + sample - GD_S_DIAMOND_IMPACT_1); + sound_effect = SND_BD_DIAMOND_IMPACT_RANDOM_1 + nr; + + if (getSoundInfoEntryFilename(sound_effect) == NULL) + sound_effect = SND_BD_DIAMOND_IMPACT; + break; + + case GD_S_FLYING_DIAMOND_FALLING_RANDOM: + case GD_S_FLYING_DIAMOND_FALLING_1: + case GD_S_FLYING_DIAMOND_FALLING_2: + case GD_S_FLYING_DIAMOND_FALLING_3: + case GD_S_FLYING_DIAMOND_FALLING_4: + case GD_S_FLYING_DIAMOND_FALLING_5: + case GD_S_FLYING_DIAMOND_FALLING_6: + case GD_S_FLYING_DIAMOND_FALLING_7: + case GD_S_FLYING_DIAMOND_FALLING_8: + nr = (sample == GD_S_FLYING_DIAMOND_FALLING_RANDOM ? GetSimpleRandom(8) : + sample - GD_S_FLYING_DIAMOND_FALLING_1); + sound_effect = SND_BD_FLYING_DIAMOND_FALLING_RANDOM_1 + nr; + + if (getSoundInfoEntryFilename(sound_effect) == NULL) + sound_effect = SND_BD_FLYING_DIAMOND_FALLING; + break; + + case GD_S_FLYING_DIAMOND_IMPACT_RANDOM: + case GD_S_FLYING_DIAMOND_IMPACT_1: + case GD_S_FLYING_DIAMOND_IMPACT_2: + case GD_S_FLYING_DIAMOND_IMPACT_3: + case GD_S_FLYING_DIAMOND_IMPACT_4: + case GD_S_FLYING_DIAMOND_IMPACT_5: + case GD_S_FLYING_DIAMOND_IMPACT_6: + case GD_S_FLYING_DIAMOND_IMPACT_7: + case GD_S_FLYING_DIAMOND_IMPACT_8: + nr = (sample == GD_S_FLYING_DIAMOND_IMPACT_RANDOM ? GetSimpleRandom(8) : + sample - GD_S_FLYING_DIAMOND_IMPACT_1); + sound_effect = SND_BD_FLYING_DIAMOND_IMPACT_RANDOM_1 + nr; + + if (getSoundInfoEntryFilename(sound_effect) == NULL) + sound_effect = SND_BD_FLYING_DIAMOND_IMPACT; + break; + + case GD_S_TIMEOUT_0: + case GD_S_TIMEOUT_1: + case GD_S_TIMEOUT_2: + case GD_S_TIMEOUT_3: + case GD_S_TIMEOUT_4: + case GD_S_TIMEOUT_5: + case GD_S_TIMEOUT_6: + case GD_S_TIMEOUT_7: + case GD_S_TIMEOUT_8: + case GD_S_TIMEOUT_9: + case GD_S_TIMEOUT_10: + nr = sample - GD_S_TIMEOUT_0; + sound_effect = SND_GAME_RUNNING_OUT_OF_TIME_0 + nr; + + if (getSoundInfoEntryFilename(sound_effect) == NULL && sample != GD_S_TIMEOUT_0) + sound_effect = SND_GAME_RUNNING_OUT_OF_TIME; + break; + + case GD_S_BONUS_LIFE: + sound_effect = SND_GAME_HEALTH_BONUS; + break; + + case GD_S_AMOEBA_MAGIC: + sound_effect = SND_BD_AMOEBA_OTHER; + break; + + case GD_S_FINISHED: + sound_effect = SND_GAME_LEVELTIME_BONUS; + break; + + default: + sound_effect = SND_UNDEFINED; + break; + } + + return sound_effect; +} + +void PlayLevelSound_BD(int xx, int yy, int element_bd, int sample) +{ + int element = (element_bd > -1 ? map_element_BD_to_RND(element_bd) : 0); + int sound_effect = getSoundEffect_BD(element, sample); + int sound_action = getSoundAction_BD(sample); + boolean is_loop_sound = IS_LOOP_SOUND(sound_effect); + int offset = 0; + int x = xx - offset; + int y = yy - offset; + + // some sound actions are always looping in native BD game engine + if (sound_action == ACTION_DEFAULT) + is_loop_sound = TRUE; + + // some sound actions are always non-looping in native BD game engine + if (sound_action == ACTION_FALLING || + sound_action == ACTION_MOVING || + sound_action == ACTION_OTHER) + is_loop_sound = FALSE; + + if (sound_effect != SND_UNDEFINED) + PlayLevelSoundExt(x, y, sound_effect, is_loop_sound); +} + +void StopSound_BD(int element_bd, int sample) +{ + int element = (element_bd > -1 ? map_element_BD_to_RND(element_bd) : 0); + int sound_effect = getSoundEffect_BD(element, sample); + + if (sound_effect != SND_UNDEFINED) + StopSound(sound_effect); +} + +boolean isSoundPlaying_BD(int element_bd, int sample) +{ + int element = (element_bd > -1 ? map_element_BD_to_RND(element_bd) : 0); + int sound_effect = getSoundEffect_BD(element, sample); + + if (sound_effect != SND_UNDEFINED) + return isSoundPlaying(sound_effect); + + return FALSE; +} + void PlayLevelSound_EM(int xx, int yy, int element_em, int sample) { int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0); @@ -15744,6 +16229,10 @@ void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message) } else { + // when using BD game engine, cover screen before fading out + if (!quick_quit && level.game_engine_type == GAME_ENGINE_TYPE_BD) + game_bd.cover_screen = TRUE; + if (quick_quit) FadeSkipNextFadeIn(); @@ -15800,7 +16289,17 @@ static void RequestRestartGame(void) int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM); int door_state = DOOR_CLOSE_1; - if (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game) + boolean restart_wanted = (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game); + + // if no restart wanted, continue with next level for BD style intermission levels + if (!restart_wanted && !level_editor_test_game && level.bd_intermission) + { + boolean success = AdvanceToNextLevel(); + + restart_wanted = (success && setup.auto_play_next_level); + } + + if (restart_wanted) { CloseDoor(door_state); @@ -15866,6 +16365,28 @@ boolean CheckRestartGame(void) return TRUE; } +boolean checkGameRunning(void) +{ + if (game_status != GAME_MODE_PLAYING) + return FALSE; + + if (level.game_engine_type == GAME_ENGINE_TYPE_BD && !checkGameRunning_BD()) + return FALSE; + + return TRUE; +} + +boolean checkGamePlaying(void) +{ + if (game_status != GAME_MODE_PLAYING) + return FALSE; + + if (level.game_engine_type == GAME_ENGINE_TYPE_BD && !checkGamePlaying_BD()) + return FALSE; + + return TRUE; +} + boolean checkGameSolved(void) { // set for all game engines if level was solved @@ -15874,7 +16395,9 @@ boolean checkGameSolved(void) boolean checkGameFailed(void) { - if (level.game_engine_type == GAME_ENGINE_TYPE_EM) + if (level.game_engine_type == GAME_ENGINE_TYPE_BD) + return (game_bd.game_over && !game_bd.level_solved); + else if (level.game_engine_type == GAME_ENGINE_TYPE_EM) return (game_em.game_over && !game_em.level_solved); else if (level.game_engine_type == GAME_ENGINE_TYPE_SP) return (game_sp.game_over && !game_sp.level_solved);