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
GADGET_ID_AUTO_EXIT_SOKOBAN, GADGET_ID_NONE,
&level.auto_exit_sokoban,
NULL, NULL,
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),
},
{
ED_ELEMENT_SETTINGS_XPOS(0), ED_ELEMENT_SETTINGS_YPOS(14),
break;
case EL_SOKOBAN_FIELD_EMPTY:
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:
break;
case EL_STONEBLOCK:
game_mm.kettles_still_needed > 0 ||
game_mm.lights_still_needed > 0 :
local_player->gems_still_needed > 0 ||
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 :
local_player->lights_still_needed > 0);
int health = (local_player->LevelSolved ?
local_player->LevelSolved_CountingHealth :
local_player->friends_still_needed;
game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
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 =
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);
game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
(game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
player->health_final = MAX_HEALTH;
player->gems_still_needed = level.gems_needed;
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;
player->lights_still_needed = 0;
player->players_still_needed = 0;
player->friends_still_needed = 0;
static void CheckExit(int x, int y)
{
if (local_player->gems_still_needed > 0 ||
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];
local_player->lights_still_needed > 0)
{
int element = Feld[x][y];
static void CheckExitEM(int x, int y)
{
if (local_player->gems_still_needed > 0 ||
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];
local_player->lights_still_needed > 0)
{
int element = Feld[x][y];
static void CheckExitSteel(int x, int y)
{
if (local_player->gems_still_needed > 0 ||
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];
local_player->lights_still_needed > 0)
{
int element = Feld[x][y];
static void CheckExitSteelEM(int x, int y)
{
if (local_player->gems_still_needed > 0 ||
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];
local_player->lights_still_needed > 0)
{
int element = Feld[x][y];
if (IS_SB_ELEMENT(element))
{
if (IS_SB_ELEMENT(element))
{
+ boolean sokoban_task_solved = FALSE;
+
if (element == EL_SOKOBAN_FIELD_FULL)
{
Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
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;
}
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;
}
Feld[x][y] = EL_SOKOBAN_OBJECT;
PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
ACTION_FILLING);
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;
(game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
{
local_player->players_still_needed = 0;
int health_final;
int gems_still_needed;
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;
int lights_still_needed;
int players_still_needed;
int friends_still_needed;