X-Git-Url: https://git.artsoft.org/?p=rocksndiamonds.git;a=blobdiff_plain;f=src%2Fgame.c;h=c7589cf005057b7a46c2fd1ec22ad7ba36d59a08;hp=479fa0aefda2d2279e9e5e08790a0f1eba85d36a;hb=39ad80f8d48b103622e36e40c951a66579444767;hpb=ae55cab94c43d759ef6d9772e32322df930a760d diff --git a/src/game.c b/src/game.c index 479fa0ae..c7589cf0 100644 --- a/src/game.c +++ b/src/game.c @@ -1683,6 +1683,30 @@ int GetElementFromGroupElement(int element) return element; } +static void IncrementSokobanFieldsNeeded(void) +{ + if (level.sb_fields_needed) + game.sokoban_fields_still_needed++; +} + +static void IncrementSokobanObjectsNeeded(void) +{ + if (level.sb_objects_needed) + game.sokoban_objects_still_needed++; +} + +static void DecrementSokobanFieldsNeeded(void) +{ + if (game.sokoban_fields_still_needed > 0) + game.sokoban_fields_still_needed--; +} + +static void DecrementSokobanObjectsNeeded(void) +{ + if (game.sokoban_objects_still_needed > 0) + game.sokoban_objects_still_needed--; +} + static void InitPlayerField(int x, int y, int element, boolean init_game) { if (element == EL_SP_MURPHY) @@ -1792,7 +1816,11 @@ static void InitField(int x, int y, boolean init_game) break; case EL_SOKOBAN_FIELD_EMPTY: - local_player->sokobanfields_still_needed++; + IncrementSokobanFieldsNeeded(); + break; + + case EL_SOKOBAN_OBJECT: + IncrementSokobanObjectsNeeded(); break; case EL_STONEBLOCK: @@ -1877,11 +1905,11 @@ static void InitField(int x, int y, boolean init_game) break; case EL_LAMP: - local_player->lights_still_needed++; + game.lights_still_needed++; break; case EL_PENGUIN: - local_player->friends_still_needed++; + game.friends_still_needed++; break; case EL_PIG: @@ -2179,8 +2207,8 @@ static void UpdatePlayfieldElementCount(void) static void UpdateGameControlValues(void) { int i, k; - int time = (local_player->LevelSolved ? - local_player->LevelSolved_CountingTime : + int time = (game.LevelSolved ? + game.LevelSolved_CountingTime : level.game_engine_type == GAME_ENGINE_TYPE_EM ? level.native_em_level->lev->time : level.game_engine_type == GAME_ENGINE_TYPE_SP ? @@ -2188,22 +2216,22 @@ static void UpdateGameControlValues(void) level.game_engine_type == GAME_ENGINE_TYPE_MM ? game_mm.energy_left : game.no_time_limit ? TimePlayed : TimeLeft); - int score = (local_player->LevelSolved ? - local_player->LevelSolved_CountingScore : + int score = (game.LevelSolved ? + game.LevelSolved_CountingScore : level.game_engine_type == GAME_ENGINE_TYPE_EM ? level.native_em_level->lev->score : level.game_engine_type == GAME_ENGINE_TYPE_SP ? level.native_sp_level->game_sp->score : level.game_engine_type == GAME_ENGINE_TYPE_MM ? game_mm.score : - local_player->score); + game.score); int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ? level.native_em_level->lev->required : level.game_engine_type == GAME_ENGINE_TYPE_SP ? level.native_sp_level->game_sp->infotrons_still_needed : level.game_engine_type == GAME_ENGINE_TYPE_MM ? game_mm.kettles_still_needed : - local_player->gems_still_needed); + game.gems_still_needed); int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ? level.native_em_level->lev->required > 0 : level.game_engine_type == GAME_ENGINE_TYPE_SP ? @@ -2211,14 +2239,15 @@ static void UpdateGameControlValues(void) level.game_engine_type == GAME_ENGINE_TYPE_MM ? game_mm.kettles_still_needed > 0 || game_mm.lights_still_needed > 0 : - local_player->gems_still_needed > 0 || - local_player->sokobanfields_still_needed > 0 || - local_player->lights_still_needed > 0); - int health = (local_player->LevelSolved ? - local_player->LevelSolved_CountingHealth : + game.gems_still_needed > 0 || + game.sokoban_fields_still_needed > 0 || + game.sokoban_objects_still_needed > 0 || + game.lights_still_needed > 0); + int health = (game.LevelSolved ? + game.LevelSolved_CountingHealth : level.game_engine_type == GAME_ENGINE_TYPE_MM ? MM_HEALTH(game_mm.laser_overload_value) : - local_player->health); + game.health); UpdatePlayfieldElementCount(); @@ -2378,12 +2407,12 @@ static void UpdateGameControlValues(void) (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY); game_panel_controls[GAME_PANEL_PENGUINS].value = - local_player->friends_still_needed; + game.friends_still_needed; game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value = - local_player->sokobanfields_still_needed; + game.sokoban_objects_still_needed; game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value = - local_player->sokobanfields_still_needed; + game.sokoban_fields_still_needed; game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value = (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL); @@ -3329,14 +3358,15 @@ void InitGame(void) else FadeSetEnterScreen(); - if (CheckIfGlobalBorderOrPlayfieldViewportHasChanged()) + if (CheckFadeAll()) fade_mask = REDRAW_ALL; FadeLevelSoundsAndMusic(); ExpireSoundLoops(TRUE); - FadeOut(fade_mask); + if (!level_editor_test_game) + FadeOut(fade_mask); // needed if different viewport properties defined for playing ChangeViewportPropertiesIfNeeded(); @@ -3367,10 +3397,12 @@ void InitGame(void) player->killed = FALSE; player->reanimated = FALSE; + player->buried = FALSE; player->action = 0; player->effective_action = 0; player->programmed_action = 0; + player->snap_action = 0; player->mouse_action.lx = 0; player->mouse_action.ly = 0; @@ -3382,18 +3414,6 @@ void InitGame(void) player->effective_mouse_action.button = 0; player->effective_mouse_action.button_hint = 0; - player->score = 0; - player->score_final = 0; - - player->health = MAX_HEALTH; - player->health_final = MAX_HEALTH; - - player->gems_still_needed = level.gems_needed; - player->sokobanfields_still_needed = 0; - player->lights_still_needed = 0; - player->players_still_needed = 0; - player->friends_still_needed = 0; - for (j = 0; j < MAX_NUM_KEYS; j++) player->key[j] = FALSE; @@ -3516,18 +3536,6 @@ void InitGame(void) DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH); SnapField(player, 0, 0); - player->LevelSolved = FALSE; - player->GameOver = FALSE; - - player->LevelSolved_GameWon = FALSE; - player->LevelSolved_GameEnd = FALSE; - player->LevelSolved_SaveTape = FALSE; - player->LevelSolved_SaveScore = FALSE; - - player->LevelSolved_CountingTime = 0; - player->LevelSolved_CountingScore = 0; - player->LevelSolved_CountingHealth = 0; - map_player_action[i] = i; } @@ -3537,9 +3545,6 @@ void InitGame(void) if (network_playing) SendToServer_MovePlayer(MV_NONE); - ZX = ZY = -1; - ExitX = ExitY = -1; - FrameCounter = 0; TimeFrames = 0; TimePlayed = 0; @@ -3552,7 +3557,27 @@ void InitGame(void) ScrollStepSize = 0; // will be correctly initialized by ScrollScreen() - AllPlayersGone = FALSE; + game.robot_wheel_x = -1; + game.robot_wheel_y = -1; + + game.exit_x = -1; + game.exit_y = -1; + + game.all_players_gone = FALSE; + + game.LevelSolved = FALSE; + game.GameOver = FALSE; + + game.GamePlayed = !tape.playing; + + game.LevelSolved_GameWon = FALSE; + game.LevelSolved_GameEnd = FALSE; + game.LevelSolved_SaveTape = FALSE; + game.LevelSolved_SaveScore = FALSE; + + game.LevelSolved_CountingTime = 0; + game.LevelSolved_CountingScore = 0; + game.LevelSolved_CountingHealth = 0; game.panel.active = TRUE; @@ -3567,6 +3592,19 @@ void InitGame(void) game.switchgate_pos = 0; game.wind_direction = level.wind_direction_initial; + game.score = 0; + game.score_final = 0; + + game.health = MAX_HEALTH; + game.health_final = MAX_HEALTH; + + game.gems_still_needed = level.gems_needed; + game.sokoban_fields_still_needed = 0; + game.sokoban_objects_still_needed = 0; + game.lights_still_needed = 0; + game.players_still_needed = 0; + game.friends_still_needed = 0; + game.lenses_time_left = 0; game.magnify_time_left = 0; @@ -3701,6 +3739,19 @@ void InitGame(void) game.belt_dir_nr[i] = 3; // not moving, next moving left #if USE_NEW_PLAYER_ASSIGNMENTS + // use preferred player also in local single-player mode + if (!network.enabled && !game.team_mode) + { + int old_index_nr = local_player->index_nr; + int new_index_nr = setup.network_player_nr; + + if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS) + { + stored_player[old_index_nr].connected_locally = FALSE; + stored_player[new_index_nr].connected_locally = TRUE; + } + } + for (i = 0; i < MAX_PLAYERS; i++) { stored_player[i].connected = FALSE; @@ -3954,10 +4005,10 @@ void InitGame(void) for (i = 0; i < MAX_PLAYERS; i++) if (stored_player[i].active) - local_player->players_still_needed++; + game.players_still_needed++; if (level.solved_by_one_player) - local_player->players_still_needed = 1; + game.players_still_needed = 1; // when recording the game, store which players take part in the game if (tape.recording) @@ -4426,28 +4477,27 @@ void InitAmoebaNr(int x, int y) AmoebaCnt2[group_nr]++; } -static void PlayerWins(struct PlayerInfo *player) +static void LevelSolved(void) { if (level.game_engine_type == GAME_ENGINE_TYPE_RND && - local_player->players_still_needed > 0) + game.players_still_needed > 0) return; - player->LevelSolved = TRUE; - player->GameOver = TRUE; + game.LevelSolved = TRUE; + game.GameOver = TRUE; - player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ? - level.native_em_level->lev->score : - level.game_engine_type == GAME_ENGINE_TYPE_MM ? - game_mm.score : - player->score); - player->health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ? - MM_HEALTH(game_mm.laser_overload_value) : - player->health); + game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ? + level.native_em_level->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); - player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : - TimeLeft); - player->LevelSolved_CountingScore = player->score_final; - player->LevelSolved_CountingHealth = player->health_final; + game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft); + game.LevelSolved_CountingScore = game.score_final; + game.LevelSolved_CountingHealth = game.health_final; } void GameWon(void) @@ -4463,17 +4513,17 @@ void GameWon(void) int game_over_delay_value_2 = 25; int game_over_delay_value_3 = 50; - if (!local_player->LevelSolved_GameWon) + if (!game.LevelSolved_GameWon) { int i; // do not start end game actions before the player stops moving (to exit) - if (local_player->MovPos) + if (local_player->active && local_player->MovPos) return; - local_player->LevelSolved_GameWon = TRUE; - local_player->LevelSolved_SaveTape = tape.recording; - local_player->LevelSolved_SaveScore = !tape.playing; + game.LevelSolved_GameWon = TRUE; + game.LevelSolved_SaveTape = tape.recording; + game.LevelSolved_SaveScore = !tape.playing; if (!tape.playing) { @@ -4492,8 +4542,8 @@ void GameWon(void) game_over_delay_3 = game_over_delay_value_3; time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft); - score = score_final = local_player->score_final; - health = health_final = local_player->health_final; + score = score_final = game.score_final; + health = health_final = game.health_final; if (level.score[SC_TIME_BONUS] > 0) { @@ -4520,8 +4570,8 @@ void GameWon(void) game_over_delay_2 = game_over_delay_value_2; } - local_player->score_final = score_final; - local_player->health_final = health_final; + game.score_final = score_final; + game.health_final = health_final; } if (level_editor_test_game) @@ -4529,8 +4579,8 @@ void GameWon(void) time = time_final; score = score_final; - local_player->LevelSolved_CountingTime = time; - local_player->LevelSolved_CountingScore = score; + game.LevelSolved_CountingTime = time; + game.LevelSolved_CountingScore = score; game_panel_controls[GAME_PANEL_TIME].value = time; game_panel_controls[GAME_PANEL_SCORE].value = score; @@ -4540,30 +4590,35 @@ void GameWon(void) if (level.game_engine_type == GAME_ENGINE_TYPE_RND) { - if (ExitX >= 0 && ExitY >= 0) // local player has left the level + // check if last player has left the level + if (game.exit_x >= 0 && + game.exit_y >= 0) { + int x = game.exit_x; + int y = game.exit_y; + int element = Feld[x][y]; + // close exit door after last player - if ((AllPlayersGone && - (Feld[ExitX][ExitY] == EL_EXIT_OPEN || - Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN || - Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) || - Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN || - Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN) + if ((game.all_players_gone && + (element == EL_EXIT_OPEN || + element == EL_SP_EXIT_OPEN || + element == EL_STEEL_EXIT_OPEN)) || + element == EL_EM_EXIT_OPEN || + element == EL_EM_STEEL_EXIT_OPEN) { - int element = Feld[ExitX][ExitY]; - Feld[ExitX][ExitY] = + Feld[x][y] = (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING : element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING : element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING: element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING: EL_EM_STEEL_EXIT_CLOSING); - PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING); + PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING); } // player disappears - DrawLevelField(ExitX, ExitY); + DrawLevelField(x, y); } for (i = 0; i < MAX_PLAYERS; i++) @@ -4601,8 +4656,8 @@ void GameWon(void) time += time_count_steps * time_count_dir; score += time_count_steps * level.score[SC_TIME_BONUS]; - local_player->LevelSolved_CountingTime = time; - local_player->LevelSolved_CountingScore = score; + game.LevelSolved_CountingTime = time; + game.LevelSolved_CountingScore = score; game_panel_controls[GAME_PANEL_TIME].value = time; game_panel_controls[GAME_PANEL_SCORE].value = score; @@ -4633,8 +4688,8 @@ void GameWon(void) health += health_count_dir; score += level.score[SC_TIME_BONUS]; - local_player->LevelSolved_CountingHealth = health; - local_player->LevelSolved_CountingScore = 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; @@ -4669,9 +4724,9 @@ void GameEnd(void) int last_level_nr = levelset.level_nr; int hi_pos; - local_player->LevelSolved_GameEnd = TRUE; + game.LevelSolved_GameEnd = TRUE; - if (local_player->LevelSolved_SaveTape) + if (game.LevelSolved_SaveTape) { // make sure that request dialog to save tape does not open door again if (!global.use_envelope_request) @@ -4692,7 +4747,7 @@ void GameEnd(void) return; } - if (!local_player->LevelSolved_SaveScore) + if (!game.LevelSolved_SaveScore) { SetGameStatus(GAME_MODE_MAIN); @@ -4754,12 +4809,12 @@ int NewHiScore(int level_nr) LoadScore(level_nr); if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) || - local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) + game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) return -1; - for (k = 0; k < MAX_SCORE_ENTRIES; k++) + for (k = 0; k < MAX_SCORE_ENTRIES; k++) { - if (local_player->score_final > highscore[k].Score) + if (game.score_final > highscore[k].Score) { // player has made it to the hall of fame @@ -4788,7 +4843,7 @@ int NewHiScore(int level_nr) strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN); highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0'; - highscore[k].Score = local_player->score_final; + highscore[k].Score = game.score_final; position = k; break; @@ -5218,11 +5273,8 @@ static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir, while (scroll_x != new_scroll_x || scroll_y != new_scroll_y) { - int dx = 0, dy = 0; - int fx = FX, fy = FY; - - dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0); - dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0); + int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0); + int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0); if (dx == 0 && dy == 0) // no scrolling needed at all break; @@ -5230,14 +5282,19 @@ static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir, scroll_x -= dx; scroll_y -= dy; - fx += dx * TILEX / 2; - fy += dy * TILEY / 2; + // set values for horizontal/vertical screen scrolling (half tile size) + int dir_x = (dx != 0 ? MV_HORIZONTAL : 0); + int dir_y = (dy != 0 ? MV_VERTICAL : 0); + int pos_x = dx * TILEX / 2; + int pos_y = dy * TILEY / 2; + int fx = getFieldbufferOffsetX_RND(dir_x, pos_x); + int fy = getFieldbufferOffsetY_RND(dir_y, pos_y); ScrollLevel(dx, dy); DrawAllPlayers(); // scroll in two steps of half tile size to make things smoother - BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY); + BlitScreenToBitmapExt_RND(window, fx, fy); // scroll second step to align at full tile size BlitScreenToBitmap(window); @@ -5273,7 +5330,7 @@ static void RelocatePlayer(int jx, int jy, int el_player_raw) int enter_side = enter_side_horiz | enter_side_vert; int leave_side = leave_side_horiz | leave_side_vert; - if (player->GameOver) // do not reanimate dead player + if (player->buried) // do not reanimate dead player return; if (!player_relocated) // no need to relocate the player @@ -6783,10 +6840,10 @@ static void TurnRoundExt(int x, int y) { int attr_x = -1, attr_y = -1; - if (AllPlayersGone) + if (game.all_players_gone) { - attr_x = ExitX; - attr_y = ExitY; + attr_x = game.exit_x; + attr_y = game.exit_y; } else { @@ -6809,12 +6866,14 @@ static void TurnRoundExt(int x, int y) } } - if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 && - (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE || + if (element == EL_ROBOT && + game.robot_wheel_x >= 0 && + game.robot_wheel_y >= 0 && + (Feld[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE || game.engine_version < VERSION_IDENT(3,1,0,0))) { - attr_x = ZX; - attr_y = ZY; + attr_x = game.robot_wheel_x; + attr_y = game.robot_wheel_y; } if (element == EL_PENGUIN) @@ -6847,13 +6906,13 @@ static void TurnRoundExt(int x, int y) MovDir[x][y] = MV_NONE; if (attr_x < x) - MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT); + MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT); else if (attr_x > x) - MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT); + MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT); if (attr_y < y) - MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP); + MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP); else if (attr_y > y) - MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN); + MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN); if (element == EL_ROBOT) { @@ -7152,10 +7211,10 @@ static void TurnRoundExt(int x, int y) int newx, newy; boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER); - if (AllPlayersGone) + if (game.all_players_gone) { - attr_x = ExitX; - attr_y = ExitY; + attr_x = game.exit_x; + attr_y = game.exit_y; } else { @@ -7918,10 +7977,11 @@ static void StartMoving(int x, int y) if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy))) DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0); - local_player->friends_still_needed--; - if (!local_player->friends_still_needed && - !local_player->GameOver && AllPlayersGone) - PlayerWins(local_player); + game.friends_still_needed--; + if (!game.friends_still_needed && + !game.GameOver && + game.all_players_gone) + LevelSolved(); return; } @@ -8995,10 +9055,11 @@ static void RunRobotWheel(int x, int y) static void StopRobotWheel(int x, int y) { - if (ZX == x && ZY == y) + if (game.robot_wheel_x == x && + game.robot_wheel_y == y) { - ZX = ZY = -1; - + game.robot_wheel_x = -1; + game.robot_wheel_y = -1; game.robot_wheel_active = FALSE; } } @@ -9052,9 +9113,10 @@ static void ActivateMagicBall(int bx, int by) static void CheckExit(int x, int y) { - if (local_player->gems_still_needed > 0 || - local_player->sokobanfields_still_needed > 0 || - local_player->lights_still_needed > 0) + if (game.gems_still_needed > 0 || + game.sokoban_fields_still_needed > 0 || + game.sokoban_objects_still_needed > 0 || + game.lights_still_needed > 0) { int element = Feld[x][y]; int graphic = el2img(element); @@ -9065,7 +9127,8 @@ static void CheckExit(int x, int y) return; } - if (AllPlayersGone) // do not re-open exit door closed after last player + // do not re-open exit door closed after last player + if (game.all_players_gone) return; Feld[x][y] = EL_EXIT_OPENING; @@ -9075,9 +9138,10 @@ static void CheckExit(int x, int y) static void CheckExitEM(int x, int y) { - if (local_player->gems_still_needed > 0 || - local_player->sokobanfields_still_needed > 0 || - local_player->lights_still_needed > 0) + if (game.gems_still_needed > 0 || + game.sokoban_fields_still_needed > 0 || + game.sokoban_objects_still_needed > 0 || + game.lights_still_needed > 0) { int element = Feld[x][y]; int graphic = el2img(element); @@ -9088,7 +9152,8 @@ static void CheckExitEM(int x, int y) return; } - if (AllPlayersGone) // do not re-open exit door closed after last player + // do not re-open exit door closed after last player + if (game.all_players_gone) return; Feld[x][y] = EL_EM_EXIT_OPENING; @@ -9098,9 +9163,10 @@ static void CheckExitEM(int x, int y) static void CheckExitSteel(int x, int y) { - if (local_player->gems_still_needed > 0 || - local_player->sokobanfields_still_needed > 0 || - local_player->lights_still_needed > 0) + if (game.gems_still_needed > 0 || + game.sokoban_fields_still_needed > 0 || + game.sokoban_objects_still_needed > 0 || + game.lights_still_needed > 0) { int element = Feld[x][y]; int graphic = el2img(element); @@ -9111,7 +9177,8 @@ static void CheckExitSteel(int x, int y) return; } - if (AllPlayersGone) // do not re-open exit door closed after last player + // do not re-open exit door closed after last player + if (game.all_players_gone) return; Feld[x][y] = EL_STEEL_EXIT_OPENING; @@ -9121,9 +9188,10 @@ static void CheckExitSteel(int x, int y) static void CheckExitSteelEM(int x, int y) { - if (local_player->gems_still_needed > 0 || - local_player->sokobanfields_still_needed > 0 || - local_player->lights_still_needed > 0) + if (game.gems_still_needed > 0 || + game.sokoban_fields_still_needed > 0 || + game.sokoban_objects_still_needed > 0 || + game.lights_still_needed > 0) { int element = Feld[x][y]; int graphic = el2img(element); @@ -9134,7 +9202,8 @@ static void CheckExitSteelEM(int x, int y) return; } - if (AllPlayersGone) // do not re-open exit door closed after last player + // do not re-open exit door closed after last player + if (game.all_players_gone) return; Feld[x][y] = EL_EM_STEEL_EXIT_OPENING; @@ -9144,7 +9213,7 @@ static void CheckExitSteelEM(int x, int y) static void CheckExitSP(int x, int y) { - if (local_player->gems_still_needed > 0) + if (game.gems_still_needed > 0) { int element = Feld[x][y]; int graphic = el2img(element); @@ -9155,7 +9224,8 @@ static void CheckExitSP(int x, int y) return; } - if (AllPlayersGone) // do not re-open exit door closed after last player + // do not re-open exit door closed after last player + if (game.all_players_gone) return; Feld[x][y] = EL_SP_EXIT_OPENING; @@ -9671,8 +9741,8 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score : action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) : action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value : - action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed : - action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score : + action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed : + action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score : action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element): action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value: action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element): @@ -9685,9 +9755,9 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) -1); int action_arg_number_old = - (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed : + (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed : action_type == CA_SET_LEVEL_TIME ? TimeLeft : - action_type == CA_SET_LEVEL_SCORE ? local_player->score : + action_type == CA_SET_LEVEL_SCORE ? game.score : action_type == CA_SET_CE_VALUE ? CustomValue[x][y] : action_type == CA_SET_CE_SCORE ? ei->collect_score : 0); @@ -9757,9 +9827,9 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) case CA_SET_LEVEL_SCORE: { - local_player->score = action_arg_number_new; + game.score = action_arg_number_new; - game_panel_controls[GAME_PANEL_SCORE].value = local_player->score; + game_panel_controls[GAME_PANEL_SCORE].value = game.score; DisplayGameControlValues(); @@ -9768,12 +9838,11 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) case CA_SET_LEVEL_GEMS: { - local_player->gems_still_needed = action_arg_number_new; + game.gems_still_needed = action_arg_number_new; game.snapshot.collected_item = TRUE; - game_panel_controls[GAME_PANEL_GEMS].value = - local_player->gems_still_needed; + game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed; DisplayGameControlValues(); @@ -9813,8 +9882,8 @@ static void ExecuteCustomElementAction(int x, int y, int element, int page) if (action_arg_player_bits & (1 << i)) ExitPlayer(&stored_player[i]); - if (AllPlayersGone) - PlayerWins(local_player); + if (game.players_still_needed == 0) + LevelSolved(); break; } @@ -10992,10 +11061,7 @@ static void CheckSingleStepMode(struct PlayerInfo *player) if (!player->is_moving && !player->is_pushing && !player->is_dropping_pressed) - { TapeTogglePause(TAPE_TOGGLE_AUTOMATIC); - SnapField(player, 0, 0); // stop snapping - } } CheckSaveEngineSnapshot(player); @@ -11083,50 +11149,48 @@ static void CheckLevelSolved(void) { if (level.game_engine_type == GAME_ENGINE_TYPE_EM) { - if (level.native_em_level->lev->home == 0) // all players at home + if (game_em.level_solved && + !game_em.game_over) // game won { - PlayerWins(local_player); + LevelSolved(); - AllPlayersGone = TRUE; + game_em.game_over = TRUE; - level.native_em_level->lev->home = -1; + game.all_players_gone = TRUE; } - if (level.native_em_level->ply[0]->alive == 0 && - level.native_em_level->ply[1]->alive == 0 && - level.native_em_level->ply[2]->alive == 0 && - level.native_em_level->ply[3]->alive == 0) // all dead - AllPlayersGone = TRUE; + if (game_em.game_over) // game lost + game.all_players_gone = TRUE; } else if (level.game_engine_type == GAME_ENGINE_TYPE_SP) { - if (game_sp.LevelSolved && - !game_sp.GameOver) // game won + if (game_sp.level_solved && + !game_sp.game_over) // game won { - PlayerWins(local_player); + LevelSolved(); - game_sp.GameOver = TRUE; + game_sp.game_over = TRUE; - AllPlayersGone = TRUE; + game.all_players_gone = TRUE; } - if (game_sp.GameOver) // game lost - AllPlayersGone = TRUE; + if (game_sp.game_over) // game lost + game.all_players_gone = TRUE; } else if (level.game_engine_type == GAME_ENGINE_TYPE_MM) { if (game_mm.level_solved && !game_mm.game_over) // game won { - PlayerWins(local_player); + LevelSolved(); game_mm.game_over = TRUE; - AllPlayersGone = TRUE; + game.all_players_gone = TRUE; } if (game_mm.game_over) // game lost - AllPlayersGone = TRUE; + game.all_players_gone = TRUE; } } @@ -11152,7 +11216,7 @@ static void CheckLevelTime(void) } } - if (!local_player->LevelSolved && !level.use_step_counter) + if (!game.LevelSolved && !level.use_step_counter) { TimePlayed++; @@ -11177,7 +11241,7 @@ static void CheckLevelTime(void) KillPlayer(&stored_player[i]); } } - else if (game.no_time_limit && !AllPlayersGone) // level w/o time limit + else if (game.no_time_limit && !game.all_players_gone) { game_panel_controls[GAME_PANEL_TIME].value = TimePlayed; } @@ -11299,10 +11363,10 @@ static void GameActionsExt(void) CheckLevelSolved(); - if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd) + if (game.LevelSolved && !game.LevelSolved_GameEnd) GameWon(); - if (AllPlayersGone && !TAPE_IS_STOPPED(tape)) + if (game.all_players_gone && !TAPE_IS_STOPPED(tape)) TapeStop(); if (game_status != GAME_MODE_PLAYING) // status might have changed @@ -11316,6 +11380,15 @@ static void GameActionsExt(void) SetVideoFrameDelay(game_frame_delay_value); + // (de)activate virtual buttons depending on current game status + if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS)) + { + if (game.all_players_gone) // if no players there to be controlled anymore + SetOverlayActive(FALSE); + else if (!tape.playing) // if game continues after tape stopped playing + SetOverlayActive(TRUE); + } + #if 0 #if 0 // ---------- main game synchronization point ---------- @@ -11392,13 +11465,14 @@ static void GameActionsExt(void) stored_player[map_player_action[local_player->index_nr]].effective_action = summarized_player_action; + // summarize all actions at centered player in local team mode if (tape.recording && - setup.team_mode && + setup.team_mode && !network.enabled && setup.input_on_focus && game.centered_player_nr != -1) { for (i = 0; i < MAX_PLAYERS; i++) - stored_player[i].effective_action = + stored_player[map_player_action[i]].effective_action = (i == game.centered_player_nr ? summarized_player_action : 0); } @@ -11426,6 +11500,10 @@ static void GameActionsExt(void) if (tape.recording) TapeRecordAction(tape_action); + // remember if game was played (especially after tape stopped playing) + if (!tape.playing && summarized_player_action) + game.GamePlayed = TRUE; + #if USE_NEW_PLAYER_ASSIGNMENTS // !!! also map player actions in single player mode !!! // if (game.team_mode) @@ -11919,7 +11997,8 @@ void GameActions_RND(void) element == EL_DC_MAGIC_WALL_FULL || element == EL_DC_MAGIC_WALL_ACTIVE || element == EL_DC_MAGIC_WALL_EMPTYING) && - ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy)) + ABS(x - jx) + ABS(y - jy) < + ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy)) { magic_wall_x = x; magic_wall_y = y; @@ -12101,11 +12180,17 @@ void GameActions_RND(void) DrawAllPlayers(); PlayAllPlayersSound(); - if (local_player->show_envelope != 0 && local_player->MovPos == 0) + for (i = 0; i < MAX_PLAYERS; i++) { - ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1); + struct PlayerInfo *player = &stored_player[i]; - local_player->show_envelope = 0; + if (player->show_envelope != 0 && (!player->active || + player->MovPos == 0)) + { + ShowEnvelope(player->show_envelope - EL_ENVELOPE_1); + + player->show_envelope = 0; + } } // use random number generator in every frame to make it less predictable @@ -12436,7 +12521,6 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) game.centered_player_nr == -1)) { int old_scroll_x = scroll_x, old_scroll_y = scroll_y; - int offset = game.scroll_delay_value; if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy))) { @@ -12448,15 +12532,19 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) } else { + int offset = game.scroll_delay_value; + if (jx != old_jx) // player has moved horizontally { - if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) || - (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset)) - scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset); + int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1); + int new_scroll_x = jx - MIDPOSX + offset_x; + + if ((player->MovDir == MV_LEFT && scroll_x > new_scroll_x) || + (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x)) + scroll_x = new_scroll_x; // don't scroll over playfield boundaries - if (scroll_x < SBX_Left || scroll_x > SBX_Right) - scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right); + scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right); // don't scroll more than one field at a time scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x); @@ -12468,13 +12556,15 @@ boolean MovePlayer(struct PlayerInfo *player, int dx, int dy) } else // player has moved vertically { - if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) || - (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset)) - scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset); + int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1); + int new_scroll_y = jy - MIDPOSY + offset_y; + + if ((player->MovDir == MV_UP && scroll_y > new_scroll_y) || + (player->MovDir == MV_DOWN && scroll_y < new_scroll_y)) + scroll_y = new_scroll_y; // don't scroll over playfield boundaries - if (scroll_y < SBY_Upper || scroll_y > SBY_Lower) - scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower); + scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower); // don't scroll more than one field at a time scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y); @@ -12642,10 +12732,10 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) { ExitPlayer(player); - if ((local_player->friends_still_needed == 0 || - IS_SP_ELEMENT(Feld[jx][jy])) && - AllPlayersGone) - PlayerWins(local_player); + if (game.players_still_needed == 0 && + (game.friends_still_needed == 0 || + IS_SP_ELEMENT(Feld[jx][jy]))) + LevelSolved(); } // this breaks one level: "machine", level 000 @@ -12693,7 +12783,7 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) RemovePlayer(player); } - if (!local_player->LevelSolved && level.use_step_counter) + if (!game.LevelSolved && level.use_step_counter) { int i; @@ -12714,7 +12804,7 @@ void ScrollPlayer(struct PlayerInfo *player, int mode) for (i = 0; i < MAX_PLAYERS; i++) KillPlayer(&stored_player[i]); } - else if (game.no_time_limit && !AllPlayersGone) // level w/o time limit + else if (game.no_time_limit && !game.all_players_gone) { game_panel_controls[GAME_PANEL_TIME].value = TimePlayed; @@ -13372,8 +13462,12 @@ void BuryPlayer(struct PlayerInfo *player) PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING); PlayLevelSound(jx, jy, SND_GAME_LOSING); - player->GameOver = TRUE; RemovePlayer(player); + + player->buried = TRUE; + + if (game.all_players_gone) + game.GameOver = TRUE; } void RemovePlayer(struct PlayerInfo *player) @@ -13384,6 +13478,9 @@ void RemovePlayer(struct PlayerInfo *player) player->present = FALSE; player->active = FALSE; + // required for some CE actions (even if the player is not active anymore) + player->MovPos = 0; + if (!ExplodeField[jx][jy]) StorePlayer[jx][jy] = 0; @@ -13395,10 +13492,13 @@ void RemovePlayer(struct PlayerInfo *player) found = TRUE; if (!found) - AllPlayersGone = TRUE; + { + game.all_players_gone = TRUE; + game.GameOver = TRUE; + } - ExitX = ZX = jx; - ExitY = ZY = jy; + game.exit_x = game.robot_wheel_x = jx; + game.exit_y = game.robot_wheel_y = jy; } void ExitPlayer(struct PlayerInfo *player) @@ -13406,12 +13506,8 @@ void ExitPlayer(struct PlayerInfo *player) DrawPlayer(player); // needed here only to cleanup last field RemovePlayer(player); - if (local_player->players_still_needed > 0) - local_player->players_still_needed--; - - // also set if some players not yet gone, but not needed to solve level - if (local_player->players_still_needed == 0) - AllPlayersGone = TRUE; + if (game.players_still_needed > 0) + game.players_still_needed--; } static void setFieldForSnapping(int x, int y, int element, int direction) @@ -13821,13 +13917,13 @@ static int DigField(struct PlayerInfo *player, } else if (collect_count > 0) { - local_player->gems_still_needed -= collect_count; - if (local_player->gems_still_needed < 0) - local_player->gems_still_needed = 0; + game.gems_still_needed -= collect_count; + if (game.gems_still_needed < 0) + game.gems_still_needed = 0; game.snapshot.collected_item = TRUE; - game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed; + game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed; DisplayGameControlValues(); } @@ -13933,16 +14029,26 @@ static int DigField(struct PlayerInfo *player, if (IS_SB_ELEMENT(element)) { + boolean sokoban_task_solved = FALSE; + if (element == EL_SOKOBAN_FIELD_FULL) { Back[x][y] = EL_SOKOBAN_FIELD_EMPTY; - local_player->sokobanfields_still_needed++; + + IncrementSokobanFieldsNeeded(); + IncrementSokobanObjectsNeeded(); } if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) { Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY; - local_player->sokobanfields_still_needed--; + + DecrementSokobanFieldsNeeded(); + DecrementSokobanObjectsNeeded(); + + // sokoban object was pushed from empty field to sokoban field + if (Back[x][y] == EL_EMPTY) + sokoban_task_solved = TRUE; } Feld[x][y] = EL_SOKOBAN_OBJECT; @@ -13956,12 +14062,14 @@ static int DigField(struct PlayerInfo *player, PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY, ACTION_FILLING); - if (local_player->sokobanfields_still_needed == 0 && + if (sokoban_task_solved && + game.sokoban_fields_still_needed == 0 && + game.sokoban_objects_still_needed == 0 && (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban)) { - local_player->players_still_needed = 0; + game.players_still_needed = 0; - PlayerWins(local_player); + LevelSolved(); PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING); } @@ -14013,9 +14121,9 @@ static int DigField(struct PlayerInfo *player, if (element == EL_ROBOT_WHEEL) { Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE; - ZX = x; - ZY = y; + game.robot_wheel_x = x; + game.robot_wheel_y = y; game.robot_wheel_active = TRUE; TEST_DrawLevelField(x, y); @@ -14077,7 +14185,7 @@ static int DigField(struct PlayerInfo *player, else if (element == EL_LAMP) { Feld[x][y] = EL_LAMP_ACTIVE; - local_player->lights_still_needed--; + game.lights_still_needed--; ResetGfxAnimation(x, y); TEST_DrawLevelField(x, y); @@ -14802,9 +14910,9 @@ void StopSound_MM(int sound_mm) void RaiseScore(int value) { - local_player->score += value; + game.score += value; - game_panel_controls[GAME_PANEL_SCORE].value = local_player->score; + game_panel_controls[GAME_PANEL_SCORE].value = game.score; DisplayGameControlValues(); } @@ -14895,7 +15003,12 @@ void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message) { // closing door required in case of envelope style request dialogs if (!skip_request) + { + // prevent short reactivation of overlay buttons while closing door + SetOverlayActive(FALSE); + CloseDoor(DOOR_CLOSE_1); + } if (network.enabled) SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER); @@ -14924,7 +15037,7 @@ void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message) void RequestQuitGame(boolean ask_if_really_quit) { boolean quick_quit = (!ask_if_really_quit || level_editor_test_game); - boolean skip_request = AllPlayersGone || quick_quit; + boolean skip_request = game.all_players_gone || quick_quit; RequestQuitGameExt(skip_request, quick_quit, "Do you really want to quit the game?"); @@ -14960,6 +15073,10 @@ void CheckGameOver(void) if (game.request_active) return; + // do not ask to play again if game was never actually played + if (!game.GamePlayed) + return; + if (!game_over) { last_game_over = FALSE; @@ -14986,22 +15103,19 @@ void CheckGameOver(void) boolean checkGameSolved(void) { // set for all game engines if level was solved - return local_player->LevelSolved_GameEnd; + return game.LevelSolved_GameEnd; } boolean checkGameFailed(void) { - if (!AllPlayersGone) - return FALSE; - if (level.game_engine_type == GAME_ENGINE_TYPE_EM) - return (level.native_em_level->lev->home > 0); + return (game_em.game_over && !game_em.level_solved); else if (level.game_engine_type == GAME_ENGINE_TYPE_SP) - return (game_sp.GameOver && !game_sp.LevelSolved); + return (game_sp.game_over && !game_sp.level_solved); else if (level.game_engine_type == GAME_ENGINE_TYPE_MM) return (game_mm.game_over && !game_mm.level_solved); else // GAME_ENGINE_TYPE_RND - return (local_player->GameOver && !local_player->LevelSolved); + return (game.GameOver && !game.LevelSolved); } boolean checkGameEnded(void) @@ -15185,11 +15299,6 @@ static ListNode *SaveEngineSnapshotBuffers(void) SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game)); SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape)); - SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX)); - SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY)); - SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX)); - SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY)); - SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter)); SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames)); SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed)); @@ -15202,8 +15311,6 @@ static ListNode *SaveEngineSnapshotBuffers(void) SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize)); - SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone)); - SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt)); SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));