#define USE_ELEMENT_TOUCHING_BUGFIX (USE_NEW_STUFF * 1)
#define USE_NEW_CONTINUOUS_SNAPPING (USE_NEW_STUFF * 1)
#define USE_GFX_RESET_GFX_ANIMATION (USE_NEW_STUFF * 1)
+#define USE_BOTH_SWITCHGATE_SWITCHES (USE_NEW_STUFF * 1)
+#define USE_PLAYER_GRAVITY (USE_NEW_STUFF * 1)
+#define USE_FIXED_BORDER_RUNNING_GFX (USE_NEW_STUFF * 1)
+#define USE_QUICKSAND_BD_ROCK_BUGFIX (USE_NEW_STUFF * 0)
#define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
boolean init_game)
{
+ int player_nr = player->index_nr;
int move_delay = get_move_delay_from_stepsize(move_stepsize);
boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
if (init_game)
{
- player->move_delay = game.initial_move_delay;
- player->move_delay_value = game.initial_move_delay_value;
+ player->move_delay = game.initial_move_delay[player_nr];
+ player->move_delay_value = game.initial_move_delay_value[player_nr];
player->move_delay_value_next = -1;
}
break;
+#if !USE_BOTH_SWITCHGATE_SWITCHES
case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
if (init_game)
Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
break;
+#endif
case EL_LIGHT_SWITCH_ACTIVE:
if (init_game)
#if 1
/* dynamically adjust player properties according to level information */
- game.initial_move_delay_value =
- get_move_delay_from_stepsize(level.initial_player_stepsize);
+ for (i = 0; i < MAX_PLAYERS; i++)
+ game.initial_move_delay_value[i] =
+ get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
#else
/* dynamically adjust player properties according to level information */
game.initial_move_delay_value =
#endif
/* dynamically adjust player properties according to game engine version */
- game.initial_move_delay = (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
- game.initial_move_delay_value : 0);
+ for (i = 0; i < MAX_PLAYERS; i++)
+ game.initial_move_delay[i] =
+ (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
+ game.initial_move_delay_value[i] : 0);
/* ---------- initialize player's initial push delay --------------------- */
{
if (!IS_CUSTOM_ELEMENT(i))
{
+#if 1
+ /* set default push delay values (corrected since version 3.0.7-1) */
+ if (game.engine_version < VERSION_IDENT(3,0,7,1))
+ {
+ element_info[i].push_delay_fixed = 2;
+ element_info[i].push_delay_random = 8;
+ }
+ else
+ {
+ element_info[i].push_delay_fixed = 8;
+ element_info[i].push_delay_random = 8;
+ }
+#else
element_info[i].push_delay_fixed = game.default_push_delay_fixed;
element_info[i].push_delay_random = game.default_push_delay_random;
+#endif
}
}
player->block_last_field = FALSE; /* initialized in InitPlayerField() */
player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
+ player->gravity = level.initial_player_gravity[i];
+
player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
player->actual_frame_counter = 0;
player->last_move_dir = MV_NONE;
+ player->is_active = FALSE;
+
player->is_waiting = FALSE;
player->is_moving = FALSE;
player->is_auto_moving = FALSE;
player->show_envelope = 0;
#if 1
- SetPlayerMoveSpeed(player, level.initial_player_stepsize, TRUE);
+ SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
#else
player->move_delay = game.initial_move_delay;
player->move_delay_value = game.initial_move_delay_value;
game.timegate_time_left = 0;
game.switchgate_pos = 0;
game.wind_direction = level.wind_direction_initial;
+
+#if !USE_PLAYER_GRAVITY
+#if 1
+ game.gravity = FALSE;
+#else
game.gravity = level.initial_gravity;
+#endif
game.explosions_delayed = TRUE;
+#endif
game.lenses_time_left = 0;
game.magnify_time_left = 0;
GfxFrame[x][y] = CustomValue[x][y];
else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
GfxFrame[x][y] = element_info[element].collect_score;
+ else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
+ GfxFrame[x][y] = ChangeDelay[x][y];
if (redraw && GfxFrame[x][y] != last_gfx_frame)
DrawLevelGraphicAnimation(x, y, graphic);
GfxFrame[x][y] = CustomValue[x][y];
else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
GfxFrame[x][y] = element_info[element].collect_score;
+ else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
+ GfxFrame[x][y] = ChangeDelay[x][y];
#endif
#if USE_GFX_RESET_GFX_ANIMATION
GfxFrame[x][y] = CustomValue[x][y];
else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
GfxFrame[x][y] = element_info[element].collect_score;
+ else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
+ GfxFrame[x][y] = ChangeDelay[x][y];
#endif
/* this is needed for CEs with property "can move" / "not moving" */
{
int element = Feld[xx][yy];
+#if !USE_BOTH_SWITCHGATE_SWITCHES
if (element == EL_SWITCHGATE_SWITCH_UP ||
element == EL_SWITCHGATE_SWITCH_DOWN)
{
Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
DrawLevelField(xx, yy);
}
+#else
+ if (element == EL_SWITCHGATE_SWITCH_UP)
+ {
+ Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
+ DrawLevelField(xx, yy);
+ }
+ else if (element == EL_SWITCHGATE_SWITCH_DOWN)
+ {
+ Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
+ DrawLevelField(xx, yy);
+ }
+#endif
else if (element == EL_SWITCHGATE_OPEN ||
element == EL_SWITCHGATE_OPENING)
{
GfxFrame[x][y] = CustomValue[x][y];
else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
GfxFrame[x][y] = element_info[element].collect_score;
+ else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
+ GfxFrame[x][y] = ChangeDelay[x][y];
#endif
}
started_moving = TRUE;
Feld[x][y] = EL_QUICKSAND_EMPTYING;
+#if USE_QUICKSAND_BD_ROCK_BUGFIX
+ if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
+ Store[x][y] = EL_ROCK;
+#else
Store[x][y] = EL_ROCK;
+#endif
PlayLevelSoundAction(x, y, ACTION_EMPTYING);
}
if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
element == EL_EXPANDABLE_WALL_ANY ||
- element == EL_EXPANDABLE_WALL)
+ element == EL_EXPANDABLE_WALL ||
+ element == EL_BD_EXPANDABLE_WALL)
{
if (links_frei)
{
CA_ARG_MAX);
int action_arg_number_reset =
- (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize :
+ (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
action_type == CA_SET_LEVEL_TIME ? level.time :
action_type == CA_SET_LEVEL_SCORE ? 0 :
break;
}
+#if !USE_PLAYER_GRAVITY
case CA_SET_LEVEL_GRAVITY:
{
game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
game.gravity);
break;
}
+#endif
case CA_SET_LEVEL_WIND:
{
action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
CA_MODE_MULTIPLY);
}
+ else if (action_arg == CA_ARG_NUMBER_RESET)
+ {
+ action_arg_number = level.initial_player_stepsize[i];
+ }
move_stepsize =
getModifiedActionNumber(move_stepsize,
break;
}
+#if USE_PLAYER_GRAVITY
+ case CA_SET_PLAYER_GRAVITY:
+ {
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ if (trigger_player_bits & (1 << i))
+ {
+ stored_player[i].gravity =
+ (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
+ action_arg == CA_ARG_GRAVITY_ON ? TRUE :
+ action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
+ stored_player[i].gravity);
+ }
+ }
+
+ break;
+ }
+#endif
+
case CA_SET_PLAYER_ARTWORK:
{
for (i = 0; i < MAX_PLAYERS; i++)
case CA_SET_CE_VALUE:
{
#if USE_NEW_CUSTOM_VALUE
- int last_custom_value = CustomValue[x][y];
+ int last_ce_value = CustomValue[x][y];
CustomValue[x][y] = action_arg_number_new;
#if 0
- printf("::: Count == %d\n", CustomValue[x][y]);
+ printf("::: CE value == %d\n", CustomValue[x][y]);
#endif
- if (CustomValue[x][y] == 0 && last_custom_value > 0)
+ if (CustomValue[x][y] != last_ce_value)
{
+ CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
+ CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
+
+ if (CustomValue[x][y] == 0)
+ {
#if 0
- printf("::: CE_VALUE_GETS_ZERO\n");
+ printf("::: CE_VALUE_GETS_ZERO\n");
#endif
- CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
- CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
+ CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
+ CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
#if 0
- printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
+ printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
#endif
+ }
}
+
#endif
break;
case CA_SET_CE_SCORE:
{
+#if USE_NEW_CUSTOM_VALUE
+ int last_ce_score = ei->collect_score;
+
ei->collect_score = action_arg_number_new;
+#if 0
+ printf("::: CE score == %d\n", ei->collect_score);
+#endif
+
+ if (ei->collect_score != last_ce_score)
+ {
+ CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
+ CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
+
+ if (ei->collect_score == 0)
+ {
+ int xx, yy;
+
+#if 0
+ printf("::: CE_SCORE_GETS_ZERO\n");
+#endif
+
+ CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
+ CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
+
+#if 0
+ printf("::: RESULT: %d, %d\n", Feld[x][y], ChangePage[x][y]);
+#endif
+
+#if 1
+ /*
+ This is a very special case that seems to be a mixture between
+ CheckElementChange() and CheckTriggeredElementChange(): while
+ the first one only affects single elements that are triggered
+ directly, the second one affects multiple elements in the playfield
+ that are triggered indirectly by another element. This is a third
+ case: Changing the CE score always affects multiple identical CEs,
+ so every affected CE must be checked, not only the single CE for
+ which the CE score was changed in the first place (as every instance
+ of that CE shares the same CE score, and therefore also can change)!
+ */
+ SCAN_PLAYFIELD(xx, yy)
+ {
+ if (Feld[xx][yy] == element)
+ CheckElementChange(xx, yy, element, EL_UNDEFINED,
+ CE_SCORE_GETS_ZERO);
+ }
+#endif
+ }
+ }
+
+#endif
+
break;
}
#if USE_NEW_CUSTOM_VALUE
int last_ce_value = CustomValue[x][y];
#endif
- boolean add_player = (ELEM_IS_PLAYER(new_element) &&
- IS_WALKABLE(old_element));
+ boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
+ boolean add_player_onto_element = (new_element_is_player &&
+ new_element != EL_SOKOBAN_FIELD_PLAYER &&
+ IS_WALKABLE(old_element));
#if 0
- /* check if element under player changes from accessible to unaccessible
+ /* check if element under the player changes from accessible to unaccessible
(needed for special case of dropping element which then changes) */
if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
}
#endif
- if (!add_player)
+ if (!add_player_onto_element)
{
if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
RemoveMovingField(x, y);
}
#if 1
- /* check if element under player changes from accessible to unaccessible
+ /* check if element under the player changes from accessible to unaccessible
(needed for special case of dropping element which then changes) */
+ /* (must be checked after creating new element for walkable group elements) */
if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
{
#endif
/* "ChangeCount" not set yet to allow "entered by player" change one time */
- if (ELEM_IS_PLAYER(new_element))
+ if (new_element_is_player)
RelocatePlayer(x, y, new_element);
if (is_change)
GfxFrame[x][y] = element_info[element].collect_score;
+#if 1
+ if (GfxFrame[x][y] != old_gfx_frame)
+#endif
+ DrawLevelGraphicAnimation(x, y, graphic);
+ }
+ else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
+ {
+ int old_gfx_frame = GfxFrame[x][y];
+
+ GfxFrame[x][y] = ChangeDelay[x][y];
+
#if 1
if (GfxFrame[x][y] != old_gfx_frame)
#endif
else if (element == EL_EXPANDABLE_WALL ||
element == EL_EXPANDABLE_WALL_HORIZONTAL ||
element == EL_EXPANDABLE_WALL_VERTICAL ||
- element == EL_EXPANDABLE_WALL_ANY)
+ element == EL_EXPANDABLE_WALL_ANY ||
+ element == EL_BD_EXPANDABLE_WALL)
MauerAbleger(x, y);
else if (element == EL_FLAMES)
CheckForDragon(x, y);
static void CheckGravityMovement(struct PlayerInfo *player)
{
+#if USE_PLAYER_GRAVITY
+ if (player->gravity && !player->programmed_action)
+#else
if (game.gravity && !player->programmed_action)
+#endif
{
int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
int move_dir_vertical = player->effective_action & MV_VERTICAL;
{
return CheckGravityMovement(player);
+#if USE_PLAYER_GRAVITY
+ if (player->gravity && !player->programmed_action)
+#else
if (game.gravity && !player->programmed_action)
+#endif
{
int jx = player->jx, jy = player->jy;
boolean field_under_player_is_free =
player->move_delay_value = original_move_delay_value;
}
+ player->is_active = FALSE;
+
if (player->last_move_dir & MV_HORIZONTAL)
{
if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
}
+#if USE_FIXED_BORDER_RUNNING_GFX
+ if (!moved && !player->is_active)
+ {
+ player->is_moving = FALSE;
+ player->is_digging = FALSE;
+ player->is_collecting = FALSE;
+ player->is_snapping = FALSE;
+ player->is_pushing = FALSE;
+ }
+#endif
+
jx = player->jx;
jy = player->jy;
last_field_block_delay += player->move_delay_value;
/* when blocking enabled, prevent moving up despite gravity */
+#if USE_PLAYER_GRAVITY
+ if (player->gravity && player->MovDir == MV_UP)
+ block_delay_adjustment = -1;
+#else
if (game.gravity && player->MovDir == MV_UP)
block_delay_adjustment = -1;
+#endif
}
/* add block delay adjustment (also possible when not blocking) */
game.engine_version >= VERSION_IDENT(2,2,0,0))
old_element = Back[jx][jy];
+ /* checking here causes player to move into acid even if the current field
+ cannot be left to that direction */
#if 0
#if USE_FIXED_DONT_RUN_INTO
if (player_can_move && DONT_RUN_INTO(element))
#endif
#endif
+#if 1 /* ------------------------------ NEW ------------------------------ */
+
+ if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
+ return MP_NO_ACTION; /* field has no opening in this direction */
+
+ if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
+ return MP_NO_ACTION; /* field has no opening in this direction */
+
+#if USE_FIXED_DONT_RUN_INTO
+ if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
+ {
+ SplashAcid(x, y);
+#if 1
+ Feld[jx][jy] = player->artwork_element;
+#else
+ Feld[jx][jy] = EL_PLAYER_1;
+#endif
+ InitMovingField(jx, jy, MV_DOWN);
+ Store[jx][jy] = EL_ACID;
+ ContinueMoving(jx, jy);
+ BuryPlayer(player);
+
+ return MP_DONT_RUN_INTO;
+ }
+#endif
+
+#if USE_FIXED_DONT_RUN_INTO
+ if (player_can_move && DONT_RUN_INTO(element))
+ {
+ TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
+
+ return MP_DONT_RUN_INTO;
+ }
+#endif
+
+#else /* ------------------------------ OLD ------------------------------ */
+
#if 1
#if USE_FIXED_DONT_RUN_INTO
if (player_can_move && DONT_RUN_INTO(element))
if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
return MP_NO_ACTION; /* field has no opening in this direction */
+ /* checking here causes player to explode when moving into acid */
#if 1
#if USE_FIXED_DONT_RUN_INTO
if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
#endif
#endif
+#endif /* ------------------------------ END ------------------------------ */
+
#if 0
#if USE_FIXED_DONT_RUN_INTO
if (player_can_move && DONT_RUN_INTO(element))
return MP_NO_ACTION;
}
+#if USE_PLAYER_GRAVITY
+ if (player->gravity && is_player && !player->is_auto_moving &&
+ canFallDown(player) && move_direction != MV_DOWN &&
+ !canMoveToValidFieldWithGravity(jx, jy, move_direction))
+ return MP_NO_ACTION; /* player cannot walk here due to gravity */
+#else
if (game.gravity && is_player && !player->is_auto_moving &&
canFallDown(player) && move_direction != MV_DOWN &&
!canMoveToValidFieldWithGravity(jx, jy, move_direction))
return MP_NO_ACTION; /* player cannot walk here due to gravity */
+#endif
if (player_can_move &&
IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
return MP_NO_ACTION;
}
+ else if (IS_EMC_GATE(element))
+ {
+ if (!player->key[EMC_GATE_NR(element)])
+ return MP_NO_ACTION;
+ }
+ else if (IS_EMC_GATE_GRAY(element))
+ {
+ if (!player->key[EMC_GATE_GRAY_NR(element)])
+ return MP_NO_ACTION;
+ }
+ else if (IS_EMC_GATE_GRAY_ACTIVE(element))
+ {
+ if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
+ return MP_NO_ACTION;
+ }
else if (IS_SP_PORT(element))
{
if (element == EL_SP_GRAVITY_PORT_LEFT ||
element == EL_SP_GRAVITY_PORT_RIGHT ||
element == EL_SP_GRAVITY_PORT_UP ||
element == EL_SP_GRAVITY_PORT_DOWN)
+#if USE_PLAYER_GRAVITY
+ player->gravity = !player->gravity;
+#else
game.gravity = !game.gravity;
+#endif
else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
element == EL_SP_GRAVITY_ON_PORT_UP ||
element == EL_SP_GRAVITY_ON_PORT_DOWN)
+#if USE_PLAYER_GRAVITY
+ player->gravity = TRUE;
+#else
game.gravity = TRUE;
+#endif
else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
element == EL_SP_GRAVITY_OFF_PORT_UP ||
element == EL_SP_GRAVITY_OFF_PORT_DOWN)
+#if USE_PLAYER_GRAVITY
+ player->gravity = FALSE;
+#else
game.gravity = FALSE;
+#endif
}
/* automatically move to the next field with double speed */
}
player->is_pushing = TRUE;
+ player->is_active = TRUE;
if (!(IN_LEV_FIELD(nextx, nexty) &&
(IS_FREE(nextx, nexty) ||
if (is_player) /* function can also be called by EL_PENGUIN */
{
if (Feld[x][y] != element) /* really digged/collected something */
+ {
player->is_collecting = !player->is_digging;
+ player->is_active = TRUE;
+ }
}
return MP_MOVING;
return FALSE;
player->is_snapping = TRUE;
+ player->is_active = TRUE;
if (player->MovPos == 0)
{