(condition)))
#define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
- ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 1)
+ ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, 0)
#define ELEMENT_CAN_ENTER_FIELD_OR_ACID(e, x, y) \
ELEMENT_CAN_ENTER_FIELD_GENERIC(e, x, y, (Feld[x][y] == EL_ACID))
if (HAS_CHANGE_EVENT(i, CE_BY_OTHER))
trigger_events[element_info[i].change.trigger] |=
element_info[i].change.events;
+
+ /* set push delay value for all non-custom elements */
+ for (i=0; i<MAX_NUM_ELEMENTS; i++)
+ {
+ if (!IS_CUSTOM_ELEMENT(i))
+ {
+ if (i == EL_SPRING ||
+ i == EL_BALLOON)
+ {
+ element_info[i].push_delay_fixed = 0;
+ element_info[i].push_delay_random = 0;
+ }
+ else if (i == EL_SOKOBAN_OBJECT ||
+ i == EL_SOKOBAN_FIELD_FULL ||
+ i == EL_SATELLITE ||
+ i == EL_SP_DISK_YELLOW)
+ {
+ element_info[i].push_delay_fixed = 2;
+ element_info[i].push_delay_random = 0;
+ }
+ else
+ {
+ element_info[i].push_delay_fixed = 2;
+ element_info[i].push_delay_random = 8;
+ }
+ }
+ }
}
AmoebaNr[x][y] = 0;
JustStopped[x][y] = 0;
Stop[x][y] = FALSE;
+ Pushed[x][y] = FALSE;
ExplodePhase[x][y] = 0;
ExplodeField[x][y] = EX_NO_EXPLOSION;
{
if (element_info[element].move_direction_initial != MV_NO_MOVING)
MovDir[x][y] = element_info[element].move_direction_initial;
- else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS)
+ else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
+ element_info[element].move_pattern == MV_TURNING_LEFT ||
+ element_info[element].move_pattern == MV_TURNING_RIGHT)
MovDir[x][y] = 1 << RND(4);
else if (element_info[element].move_pattern == MV_HORIZONTAL)
MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
MovDir[x][y] = 0;
MovDelay[x][y] = 0;
ChangeDelay[x][y] = 0;
+ Pushed[x][y] = FALSE;
}
void RemoveMovingField(int x, int y)
}
}
}
- else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS)
+ else if (element_info[element].move_pattern == MV_ALL_DIRECTIONS ||
+ element_info[element].move_pattern == MV_TURNING_LEFT ||
+ element_info[element].move_pattern == MV_TURNING_RIGHT)
{
boolean can_turn_left = ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
boolean can_turn_right = ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
- if (can_turn_left && can_turn_right)
+ if (element_info[element].move_pattern == MV_TURNING_LEFT)
+ MovDir[x][y] = left_dir;
+ else if (element_info[element].move_pattern == MV_TURNING_RIGHT)
+ MovDir[x][y] = right_dir;
+ else if (can_turn_left && can_turn_right)
MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
else if (can_turn_left)
MovDir[x][y] = (RND(2) ? left_dir : back_dir);
#endif
}
#if 1
+#if 1
+ else if (game.engine_version < RELEASE_IDENT(2,2,0,7) &&
+ CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
+ JustStopped[x][y] && !Pushed[x][y + 1])
+#else
else if (CAN_SMASH(element) && Feld[x][y + 1] == EL_BLOCKED &&
JustStopped[x][y])
+#endif
{
/*
printf("::: %d\n", MovDir[x][y]);
if (element != EL_YAMYAM &&
element != EL_DARK_YAMYAM &&
element != EL_PACMAN &&
- !(element_info[element].move_pattern & MV_ANY_DIRECTION))
+ !(element_info[element].move_pattern & MV_ANY_DIRECTION) &&
+ element_info[element].move_pattern != MV_TURNING_LEFT &&
+ element_info[element].move_pattern != MV_TURNING_RIGHT)
{
TurnRound(x, y);
MovPos[x][y] += step;
#if 1
+#if 1
+ if (Pushed[x][y]) /* special case: moving object pushed by player */
+#else
if (pushing) /* special case: moving object pushed by player */
+#endif
#if 1
MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
#else
Feld[x][y] = get_next_element(element);
element = Feld[newx][newy] = Store[x][y];
}
+ else if (element == EL_SOKOBAN_OBJECT)
+ {
+ if (Back[x][y])
+ Feld[x][y] = Back[x][y];
+
+ if (Back[newx][newy])
+ Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
+
+ Back[x][y] = Back[newx][newy] = 0;
+ }
else if (Store[x][y] == EL_ACID)
{
element = Feld[newx][newy] = EL_ACID;
GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
+ Pushed[x][y] = Pushed[newx][newy] = FALSE;
+
ResetGfxAnimation(x, y); /* reset animation values for old field */
#if 1
if (!CAN_MOVE(element))
MovDir[newx][newy] = 0;
#else
- /*
+
+#if 0
if (CAN_FALL(element) && MovDir[newx][newy] == MV_DOWN)
MovDir[newx][newy] = 0;
- */
-
+#else
if (!CAN_MOVE(element) ||
(element == EL_SPRING && MovDir[newx][newy] == MV_DOWN))
MovDir[newx][newy] = 0;
#endif
+
+#endif
#endif
DrawLevelField(x, y);
if (!CAN_CHANGE(element) || !HAS_CHANGE_EVENT(element, trigger_event))
return;
+#if 1
+ ChangeDelay[x][y] = 1;
+ ChangeElement(x, y);
+#else
ChangeElementDoIt(x, y, element_info[element].change.successor);
+#endif
}
static void PlayerActions(struct PlayerInfo *player, byte player_action)
int DigField(struct PlayerInfo *player,
int x, int y, int real_dx, int real_dy, int mode)
{
+ boolean use_spring_bug = (game.engine_version < VERSION_IDENT(2,2,0));
int jx = player->jx, jy = player->jy;
int dx = x - jx, dy = y - jy;
int move_direction = (dx == -1 ? MV_LEFT :
return MF_ACTION;
break;
+#if 0
+
/* the following elements cannot be pushed by "snapping" */
case EL_ROCK:
case EL_BOMB:
if (dy)
return MF_NO_ACTION;
+ if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
+ !(element == EL_SPRING && use_spring_bug))
+ return MF_NO_ACTION;
+
player->Pushing = TRUE;
#if 0
break;
+#endif
+
case EL_GATE_1:
case EL_GATE_2:
case EL_GATE_3:
return MF_ACTION;
break;
+#if 0
+
#if 0
case EL_SOKOBAN_FIELD_EMPTY:
break;
if (IS_SB_ELEMENT(element))
{
+#if 1
+ if (element == EL_SOKOBAN_FIELD_FULL)
+ {
+ Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
+ local_player->sokobanfields_still_needed++;
+ }
+
+ if (Feld[x + dx][y + dy] == EL_SOKOBAN_FIELD_EMPTY)
+ {
+ Back[x + dx][y + dy] = EL_SOKOBAN_FIELD_EMPTY;
+ local_player->sokobanfields_still_needed--;
+ }
+
+ Feld[x][y] = EL_SOKOBAN_OBJECT;
+
+ if (Back[x][y] == Back[x + dx][y + dy])
+ PlaySoundLevelAction(x, y, ACTION_PUSHING);
+ else if (Back[x][y] != 0)
+ PlaySoundLevelElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
+ ACTION_EMPTYING);
+ else
+ PlaySoundLevelElementAction(x + dx, y + dy, EL_SOKOBAN_FIELD_EMPTY,
+ ACTION_FILLING);
+
+ InitMovingField(x, y, (dx < 0 ? MV_LEFT :
+ dx > 0 ? MV_RIGHT :
+ dy < 0 ? MV_UP : MV_DOWN));
+ MovPos[x][y] = (dx != 0 ? dx : dy);
+
+#if 0
+ printf("::: %s -> %s [%s -> %s]\n",
+ element_info[Feld[x][y]].token_name,
+ element_info[Feld[x + dx][y + dy]].token_name,
+ element_info[Back[x][y]].token_name,
+ element_info[Back[x + dx][y + dy]].token_name);
+#endif
+
+#else
if (element == EL_SOKOBAN_FIELD_FULL)
{
Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
PlaySoundLevel(x, y, SND_SOKOBAN_OBJECT_PUSHING);
#endif
}
+#endif
}
else
{
+#if 1
+ InitMovingField(x, y, (dx < 0 ? MV_LEFT :
+ dx > 0 ? MV_RIGHT :
+ dy < 0 ? MV_UP : MV_DOWN));
+ MovPos[x][y] = (dx != 0 ? dx : dy);
+#else
RemoveField(x, y);
- Feld[x+dx][y+dy] = element;
+ Feld[x + dx][y + dy] = element;
+#endif
PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
}
break;
+#endif
+
case EL_PENGUIN:
case EL_PIG:
case EL_DRAGON:
}
else if (IS_PUSHABLE(element))
{
- if (mode == DF_SNAP)
+ if (mode == DF_SNAP && element != EL_BD_ROCK)
return MF_NO_ACTION;
if (CAN_FALL(element) && dy)
return MF_NO_ACTION;
+ if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
+ !(element == EL_SPRING && use_spring_bug))
+ return MF_NO_ACTION;
+
+ if (element == EL_SPRING && MovDir[x][y] != MV_NO_MOVING)
+ return MF_NO_ACTION;
+
if (!player->Pushing &&
game.engine_version >= RELEASE_IDENT(2,2,0,7))
player->push_delay_value = GET_NEW_PUSH_DELAY(element);
player->Pushing = TRUE;
- if (!IN_LEV_FIELD(x + dx, y + dy) || !IS_FREE(x + dx, y + dy))
+ if (!(IN_LEV_FIELD(x + dx, y + dy) &&
+ (IS_FREE(x + dx, y + dy) ||
+ (Feld[x + dx][y + dy] == EL_SOKOBAN_FIELD_EMPTY &&
+ IS_SB_ELEMENT(element)))))
return MF_NO_ACTION;
if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
player->push_delay = FrameCounter;
if (!FrameReached(&player->push_delay, player->push_delay_value) &&
- !(tape.playing && tape.file_version < FILE_VERSION_2_0))
+ !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
+ element != EL_SPRING && element != EL_BALLOON)
return MF_NO_ACTION;
-#if 1
- InitMovingField(x, y, (dx < 0 ? MV_LEFT :
- dx > 0 ? MV_RIGHT :
- dy < 0 ? MV_UP : MV_DOWN));
- MovPos[x][y] = (dx != 0 ? dx : dy);
-#else
- RemoveField(x, y);
- Feld[x + dx][y + dy] = element;
-#endif
+ if (IS_SB_ELEMENT(element))
+ {
+ if (element == EL_SOKOBAN_FIELD_FULL)
+ {
+ Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
+ local_player->sokobanfields_still_needed++;
+ }
+
+ if (Feld[x + dx][y + dy] == EL_SOKOBAN_FIELD_EMPTY)
+ {
+ Back[x + dx][y + dy] = EL_SOKOBAN_FIELD_EMPTY;
+ local_player->sokobanfields_still_needed--;
+ }
+
+ Feld[x][y] = EL_SOKOBAN_OBJECT;
+
+ if (Back[x][y] == Back[x + dx][y + dy])
+ PlaySoundLevelAction(x, y, ACTION_PUSHING);
+ else if (Back[x][y] != 0)
+ PlaySoundLevelElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
+ ACTION_EMPTYING);
+ else
+ PlaySoundLevelElementAction(x + dx, y + dy, EL_SOKOBAN_FIELD_EMPTY,
+ ACTION_FILLING);
+
+ if (local_player->sokobanfields_still_needed == 0 &&
+ game.emulation == EMU_SOKOBAN)
+ {
+ player->LevelSolved = player->GameOver = TRUE;
+ PlaySoundLevel(x, y, SND_GAME_SOKOBAN_SOLVING);
+ }
+ }
+ else
+ PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
+
+ InitMovingField(x, y, move_direction);
+
+ if (mode == DF_SNAP)
+ ContinueMoving(x, y);
+ else
+ MovPos[x][y] = (dx != 0 ? dx : dy);
+
+ Pushed[x][y] = TRUE;
+ Pushed[x + dx][y + dy] = TRUE;
-#if 1
if (game.engine_version < RELEASE_IDENT(2,2,0,7))
player->push_delay_value = GET_NEW_PUSH_DELAY(element);
-#else
- player->push_delay_value = 2 + RND(8);
-#endif
-
- DrawLevelField(x + dx, y + dy);
- PlaySoundLevelElementAction(x, y, element, ACTION_PUSHING);
CheckTriggeredElementChange(element, CE_OTHER_PUSHING);
- CheckPlayerElementChange(x + dx, y + dy, element, CE_PUSHED_BY_PLAYER);
+ CheckPlayerElementChange(x, y, element, CE_PUSHED_BY_PLAYER);
break;
}