#define USE_NEW_PLAYER_SPEED (USE_NEW_STUFF * 1)
#define USE_NEW_DELAYED_ACTION (USE_NEW_STUFF * 1)
#define USE_NEW_SNAP_DELAY (USE_NEW_STUFF * 1)
+#define USE_ONLY_ONE_CHANGE_PER_FRAME (USE_NEW_STUFF * 0)
/* for DigField() */
#define DF_NO_PUSH 0
InitMovDir(x, y);
#if USE_NEW_CUSTOM_VALUE
- if (!element_info[element].use_last_ce_value)
+ if (!element_info[element].use_last_ce_value || init_game)
CustomValue[x][y] = GET_NEW_CUSTOM_VALUE(Feld[x][y]);
#endif
}
game.use_block_last_field_bug =
(game.engine_version < VERSION_IDENT(3,1,1,0));
+ /*
+ Summary of bugfix/change:
+ Changed behaviour of CE changes with multiple changes per single frame.
+
+ Fixed/changed in version:
+ 3.2.0-6
+
+ Description:
+ Before 3.2.0-6, only one single CE change was allowed in each engine frame.
+ This resulted in race conditions where CEs seem to behave strange in some
+ situations (where triggered CE changes were just skipped because there was
+ already a CE change on that tile in the playfield in that engine frame).
+ Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
+ (The number of changes per frame must be limited in any case, because else
+ it is easily possible to define CE changes that would result in an infinite
+ loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
+ should be set large enough so that it would only be reached in cases where
+ the corresponding CE change conditions run into a loop. Therefore, it seems
+ to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
+ maximal number of change pages for custom elements.)
+
+ Affected levels/tapes:
+ Probably many.
+ */
+
+#if USE_ONLY_ONE_CHANGE_PER_FRAME
+ game.max_num_changes_per_frame = 1;
+#else
+ game.max_num_changes_per_frame =
+ (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
+#endif
+
/* ---------------------------------------------------------------------- */
/* dynamically adjust element properties according to game engine version */
game.gravity = level.initial_gravity;
game.explosions_delayed = TRUE;
+ game.lenses_time_left = 0;
+ game.magnify_time_left = 0;
+
game.envelope_active = FALSE;
for (i = 0; i < NUM_BELTS; i++)
Stop[x][y] = FALSE;
Pushed[x][y] = FALSE;
- Changed[x][y] = FALSE;
+ Changed[x][y] = 0;
ChangeEvent[x][y] = -1;
ExplodePhase[x][y] = 0;
{
int x, y;
- for (y = 0; y < lev_fieldy; y++)
+ for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
{
- for (x = 0; x < lev_fieldx; x++)
+ int element = Feld[x][y];
+
+ if (element == EL_LIGHT_SWITCH &&
+ game.light_time_left > 0)
{
- int element = Feld[x][y];
+ Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
+ DrawLevelField(x, y);
+ }
+ else if (element == EL_LIGHT_SWITCH_ACTIVE &&
+ game.light_time_left == 0)
+ {
+ Feld[x][y] = EL_LIGHT_SWITCH;
+ DrawLevelField(x, y);
+ }
+ else if (element == EL_EMC_DRIPPER &&
+ game.light_time_left > 0)
+ {
+ Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
+ DrawLevelField(x, y);
+ }
+ else if (element == EL_EMC_DRIPPER_ACTIVE &&
+ game.light_time_left == 0)
+ {
+ Feld[x][y] = EL_EMC_DRIPPER;
+ DrawLevelField(x, y);
+ }
+ else if (element == EL_INVISIBLE_STEELWALL ||
+ element == EL_INVISIBLE_WALL ||
+ element == EL_INVISIBLE_SAND)
+ {
+ if (game.light_time_left > 0)
+ Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
- if (element == EL_LIGHT_SWITCH &&
- game.light_time_left > 0)
- {
- Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
- DrawLevelField(x, y);
- }
- else if (element == EL_LIGHT_SWITCH_ACTIVE &&
- game.light_time_left == 0)
- {
- Feld[x][y] = EL_LIGHT_SWITCH;
- DrawLevelField(x, y);
- }
- else if (element == EL_INVISIBLE_STEELWALL ||
- element == EL_INVISIBLE_WALL ||
- element == EL_INVISIBLE_SAND)
- {
- if (game.light_time_left > 0)
- Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
+ DrawLevelField(x, y);
- DrawLevelField(x, y);
+ /* uncrumble neighbour fields, if needed */
+ if (element == EL_INVISIBLE_SAND)
+ DrawLevelFieldCrumbledSandNeighbours(x, y);
+ }
+ else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
+ element == EL_INVISIBLE_WALL_ACTIVE ||
+ element == EL_INVISIBLE_SAND_ACTIVE)
+ {
+ if (game.light_time_left == 0)
+ Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
- /* uncrumble neighbour fields, if needed */
- if (element == EL_INVISIBLE_SAND)
- DrawLevelFieldCrumbledSandNeighbours(x, y);
- }
- else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
- element == EL_INVISIBLE_WALL_ACTIVE ||
- element == EL_INVISIBLE_SAND_ACTIVE)
- {
- if (game.light_time_left == 0)
- Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
+ DrawLevelField(x, y);
- DrawLevelField(x, y);
+ /* re-crumble neighbour fields, if needed */
+ if (element == EL_INVISIBLE_SAND)
+ DrawLevelFieldCrumbledSandNeighbours(x, y);
+ }
+ }
+}
- /* re-crumble neighbour fields, if needed */
- if (element == EL_INVISIBLE_SAND)
- DrawLevelFieldCrumbledSandNeighbours(x, y);
- }
+static void RedrawAllInvisibleElementsForLenses()
+{
+ int x, y;
+
+ for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
+ {
+ int element = Feld[x][y];
+
+ if (element == EL_EMC_DRIPPER &&
+ game.lenses_time_left > 0)
+ {
+ Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
+ DrawLevelField(x, y);
+ }
+ else if (element == EL_EMC_DRIPPER_ACTIVE &&
+ game.lenses_time_left == 0)
+ {
+ Feld[x][y] = EL_EMC_DRIPPER;
+ DrawLevelField(x, y);
+ }
+ else if (element == EL_INVISIBLE_STEELWALL ||
+ element == EL_INVISIBLE_WALL ||
+ element == EL_INVISIBLE_SAND)
+ {
+ if (game.lenses_time_left > 0)
+ Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
+
+ DrawLevelField(x, y);
+
+ /* uncrumble neighbour fields, if needed */
+ if (element == EL_INVISIBLE_SAND)
+ DrawLevelFieldCrumbledSandNeighbours(x, y);
+ }
+ else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
+ element == EL_INVISIBLE_WALL_ACTIVE ||
+ element == EL_INVISIBLE_SAND_ACTIVE)
+ {
+ if (game.lenses_time_left == 0)
+ Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
+
+ DrawLevelField(x, y);
+
+ /* re-crumble neighbour fields, if needed */
+ if (element == EL_INVISIBLE_SAND)
+ DrawLevelFieldCrumbledSandNeighbours(x, y);
+ }
+ }
+}
+
+static void RedrawAllInvisibleElementsForMagnifier()
+{
+ int x, y;
+
+ for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
+ {
+ int element = Feld[x][y];
+
+ if (element == EL_EMC_FAKE_GRASS &&
+ game.magnify_time_left > 0)
+ {
+ Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
+ DrawLevelField(x, y);
+ }
+ else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
+ game.magnify_time_left == 0)
+ {
+ Feld[x][y] = EL_EMC_FAKE_GRASS;
+ DrawLevelField(x, y);
+ }
+ else if (IS_GATE_GRAY(element) &&
+ game.magnify_time_left > 0)
+ {
+ Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
+ element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
+ IS_EM_GATE_GRAY(element) ?
+ element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
+ IS_EMC_GATE_GRAY(element) ?
+ element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
+ element);
+ DrawLevelField(x, y);
+ }
+ else if (IS_GATE_GRAY_ACTIVE(element) &&
+ game.magnify_time_left == 0)
+ {
+ Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
+ element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
+ IS_EM_GATE_GRAY_ACTIVE(element) ?
+ element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
+ IS_EMC_GATE_GRAY_ACTIVE(element) ?
+ element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
+ element);
+ DrawLevelField(x, y);
}
}
}
ChangeDelay[x][y] = 0;
ChangePage[x][y] = -1;
- Changed[x][y] = FALSE;
+ Changed[x][y] = 0;
ChangeEvent[x][y] = -1;
#if USE_NEW_CUSTOM_VALUE
if (ELEM_IS_PLAYER(target_element))
RelocatePlayer(x, y, target_element);
-#if 1
- Changed[x][y] = TRUE; /* ignore all further changes in this frame */
-#else
- Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
-#endif
+ Changed[x][y]++; /* count number of changes in the same frame */
TestIfBadThingTouchesPlayer(x, y);
TestIfPlayerTouchesCustomElement(x, y);
change->actual_trigger_ce_value = 0;
}
-#if 1
- /* do not change any elements that have already changed in this frame */
- if (Changed[x][y])
- return FALSE;
-#else
- /* do not change already changed elements with same change event */
- if (Changed[x][y] & ChangeEvent[x][y])
+ /* do not change elements more than a specified maximum number of changes */
+ if (Changed[x][y] >= game.max_num_changes_per_frame)
return FALSE;
-#endif
-#if 1
- Changed[x][y] = TRUE; /* ignore all further changes in this frame */
-#else
- Changed[x][y] |= ChangeEvent[x][y]; /* ignore same changes in this frame */
-#endif
+ Changed[x][y]++; /* count number of changes in the same frame */
if (change->explode)
{
for (y = 0; y < lev_fieldy; y++) for (x = 0; x < lev_fieldx; x++)
{
- Changed[x][y] = FALSE;
+ Changed[x][y] = 0;
ChangeEvent[x][y] = -1;
/* this must be handled before main playfield loop */
CloseAllOpenTimegates();
}
+ if (game.lenses_time_left > 0)
+ {
+ game.lenses_time_left--;
+
+ if (game.lenses_time_left == 0)
+ RedrawAllInvisibleElementsForLenses();
+ }
+
+ if (game.magnify_time_left > 0)
+ {
+ game.magnify_time_left--;
+
+ if (game.magnify_time_left == 0)
+ RedrawAllInvisibleElementsForMagnifier();
+ }
+
for (i = 0; i < MAX_PLAYERS; i++)
{
struct PlayerInfo *player = &stored_player[i];
if (player->cannot_move)
{
+#if 1
+ if (player->MovPos == 0)
+ {
+ player->is_moving = FALSE;
+ player->is_digging = FALSE;
+ player->is_collecting = FALSE;
+ player->is_snapping = FALSE;
+ player->is_pushing = FALSE;
+ }
+#else
DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
SnapField(player, 0, 0);
+#endif
return MF_NO_ACTION;
}
if (!player->key[RND_GATE_GRAY_NR(element)])
return MF_NO_ACTION;
}
+ else if (IS_RND_GATE_GRAY_ACTIVE(element))
+ {
+ if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
+ return MF_NO_ACTION;
+ }
else if (element == EL_EXIT_OPEN ||
element == EL_SP_EXIT_OPEN ||
element == EL_SP_EXIT_OPENING)
if (!player->key[EM_GATE_GRAY_NR(element)])
return MF_NO_ACTION;
}
+ else if (IS_EM_GATE_GRAY_ACTIVE(element))
+ {
+ if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
+ return MF_NO_ACTION;
+ }
else if (IS_SP_PORT(element))
{
if (element == EL_SP_GRAVITY_PORT_LEFT ||
{
player->show_envelope = element;
}
+ else if (element == EL_EMC_LENSES)
+ {
+ game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
+
+ RedrawAllInvisibleElementsForLenses();
+ }
+ else if (element == EL_EMC_MAGNIFIER)
+ {
+ game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
+
+ RedrawAllInvisibleElementsForMagnifier();
+ }
else if (IS_DROPPABLE(element) ||
IS_THROWABLE(element)) /* can be collected and dropped */
{
PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
/* needed if previous element just changed to "empty" in the last frame */
- Changed[dropx][dropy] = FALSE; /* allow another change */
+ Changed[dropx][dropy] = 0; /* allow at least one more change */
CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
player->index_bit, drop_side);
nextx = dropx + GET_DX_FROM_DIR(move_direction);
nexty = dropy + GET_DY_FROM_DIR(move_direction);
- Changed[dropx][dropy] = FALSE; /* allow another change */
+ Changed[dropx][dropy] = 0; /* allow at least one more change */
CheckCollision[dropx][dropy] = 2;
}