From 94437c1c17bca3eb27deba579ce376de0aa49a4f Mon Sep 17 00:00:00 2001 From: Holger Schemel Date: Mon, 29 Oct 2018 00:26:22 +0100 Subject: [PATCH] fixed unexpected behaviour with solving levels with Sokoban elements This patch fixes problems with levels using a different number of Sokoban objects and Sokoban fields, causing unexpected behaviour. Before, levels with Sokoban objects, but without Sokoban fields, could be solved by just pushing any of the Sokoban objects (which triggers a check if there are any empty Sokoban fields left in the level, which is not the case in this scenario). In addition, pushing a Sokoban object to a Sokoban field would also solve a level with two Sokoban objects, but only one Sokoban field, but not a level with two Sokoban fields, but only one Sokoban object, which seems confusing or at least unexpected. (Things get even more complicated and confusing if Sokoban fields or objects are created during the game, for example as content of yam-yams.) Now, Sokoban fields and objects in a level are tracked independently and the level is only solved if there are no Sokoban fields or objects left on the playfield. Also see the following forum post for a description of the problem: https://www.artsoft.org/forum/viewtopic.php?f=7&t=2654 --- src/editor.c | 2 +- src/game.c | 44 ++++++++++++++++++++++++++++++++------------ src/game.h | 3 ++- 3 files changed, 35 insertions(+), 14 deletions(-) diff --git a/src/editor.c b/src/editor.c index 7757fe51..55e8104b 100644 --- a/src/editor.c +++ b/src/editor.c @@ -3071,7 +3071,7 @@ static struct GADGET_ID_AUTO_EXIT_SOKOBAN, GADGET_ID_NONE, &level.auto_exit_sokoban, NULL, NULL, - "exit level if all fields solved", "automatically finish Sokoban levels" + "exit level if all tasks solved", "automatically finish Sokoban levels" }, { ED_ELEMENT_SETTINGS_XPOS(0), ED_ELEMENT_SETTINGS_YPOS(14), diff --git a/src/game.c b/src/game.c index 479fa0ae..4a3d1eef 100644 --- a/src/game.c +++ b/src/game.c @@ -1792,7 +1792,11 @@ static void InitField(int x, int y, boolean init_game) break; case EL_SOKOBAN_FIELD_EMPTY: - local_player->sokobanfields_still_needed++; + local_player->sokoban_fields_still_needed++; + break; + + case EL_SOKOBAN_OBJECT: + local_player->sokoban_objects_still_needed++; break; case EL_STONEBLOCK: @@ -2212,7 +2216,8 @@ static void UpdateGameControlValues(void) 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->sokoban_fields_still_needed > 0 || + local_player->sokoban_objects_still_needed > 0 || local_player->lights_still_needed > 0); int health = (local_player->LevelSolved ? local_player->LevelSolved_CountingHealth : @@ -2381,9 +2386,9 @@ static void UpdateGameControlValues(void) local_player->friends_still_needed; game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value = - local_player->sokobanfields_still_needed; + local_player->sokoban_objects_still_needed; game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value = - local_player->sokobanfields_still_needed; + local_player->sokoban_fields_still_needed; game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value = (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL); @@ -3389,7 +3394,8 @@ void InitGame(void) player->health_final = MAX_HEALTH; player->gems_still_needed = level.gems_needed; - player->sokobanfields_still_needed = 0; + player->sokoban_fields_still_needed = 0; + player->sokoban_objects_still_needed = 0; player->lights_still_needed = 0; player->players_still_needed = 0; player->friends_still_needed = 0; @@ -9053,7 +9059,8 @@ 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->sokoban_fields_still_needed > 0 || + local_player->sokoban_objects_still_needed > 0 || local_player->lights_still_needed > 0) { int element = Feld[x][y]; @@ -9076,7 +9083,8 @@ 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->sokoban_fields_still_needed > 0 || + local_player->sokoban_objects_still_needed > 0 || local_player->lights_still_needed > 0) { int element = Feld[x][y]; @@ -9099,7 +9107,8 @@ 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->sokoban_fields_still_needed > 0 || + local_player->sokoban_objects_still_needed > 0 || local_player->lights_still_needed > 0) { int element = Feld[x][y]; @@ -9122,7 +9131,8 @@ 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->sokoban_fields_still_needed > 0 || + local_player->sokoban_objects_still_needed > 0 || local_player->lights_still_needed > 0) { int element = Feld[x][y]; @@ -13933,16 +13943,24 @@ 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++; + local_player->sokoban_fields_still_needed++; + local_player->sokoban_objects_still_needed++; } if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) { Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY; - local_player->sokobanfields_still_needed--; + local_player->sokoban_fields_still_needed--; + local_player->sokoban_objects_still_needed--; + + // 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,7 +13974,9 @@ 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 && + local_player->sokoban_fields_still_needed == 0 && + local_player->sokoban_objects_still_needed == 0 && (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban)) { local_player->players_still_needed = 0; diff --git a/src/game.h b/src/game.h index 553ec4b7..5ef27e78 100644 --- a/src/game.h +++ b/src/game.h @@ -347,7 +347,8 @@ struct PlayerInfo int health_final; int gems_still_needed; - int sokobanfields_still_needed; + int sokoban_fields_still_needed; + int sokoban_objects_still_needed; int lights_still_needed; int players_still_needed; int friends_still_needed; -- 2.34.1