+#if 1
+ InitPlayerGfxAnimation(player, ACTION_DEFAULT);
+#else
+ if (!(moved & MF_MOVING) && !player->is_pushing)
+ player->Frame = 0;
+#endif
+#endif
+
+ player->StepFrame = 0;
+
+ if (moved & MF_MOVING)
+ {
+ if (old_jx != jx && old_jy == jy)
+ player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
+ else if (old_jx == jx && old_jy != jy)
+ player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
+
+ DrawLevelField(jx, jy); /* for "crumbled sand" */
+
+ player->last_move_dir = player->MovDir;
+ player->is_moving = TRUE;
+#if 1
+ player->is_snapping = FALSE;
+#endif
+
+#if 1
+ player->is_switching = FALSE;
+#endif
+
+ player->is_dropping = FALSE;
+
+
+#if 1
+ {
+ static int trigger_sides[4][2] =
+ {
+ /* enter side leave side */
+ { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
+ { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
+ { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
+ { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
+ };
+ int move_direction = player->MovDir;
+ int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
+ int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
+
+#if 1
+ if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
+ {
+ CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
+ CE_OTHER_GETS_LEFT,
+ player->index_bit, leave_side);
+ CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
+ CE_LEFT_BY_PLAYER,
+ player->index_bit, leave_side);
+ }
+
+ if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
+ {
+ CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
+ CE_OTHER_GETS_ENTERED,
+ player->index_bit, enter_side);
+ CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
+ player->index_bit, enter_side);
+ }
+#endif
+
+ }
+#endif
+
+
+ }
+ else
+ {
+ CheckGravityMovementWhenNotMoving(player);
+
+ /*
+ player->last_move_dir = MV_NO_MOVING;
+ */
+ player->is_moving = FALSE;
+ }
+
+ if (game.engine_version < VERSION_IDENT(3,0,7,0))
+ {
+ TestIfHeroTouchesBadThing(jx, jy);
+ TestIfPlayerTouchesCustomElement(jx, jy);
+ }
+
+ if (!player->active)
+ RemoveHero(player);
+
+ return moved;
+}
+
+void ScrollPlayer(struct PlayerInfo *player, int mode)
+{
+ int jx = player->jx, jy = player->jy;
+ int last_jx = player->last_jx, last_jy = player->last_jy;
+ int move_stepsize = TILEX / player->move_delay_value;
+
+ if (!player->active || !player->MovPos)
+ return;
+
+ if (mode == SCROLL_INIT)
+ {
+ player->actual_frame_counter = FrameCounter;
+ player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
+
+ if (Feld[last_jx][last_jy] == EL_EMPTY)
+ Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
+
+#if 0
+ DrawPlayer(player);
+#endif
+
+ return;
+ }
+ else if (!FrameReached(&player->actual_frame_counter, 1))
+ return;
+
+ player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
+ player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
+
+ if (!player->block_last_field &&
+ Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
+ Feld[last_jx][last_jy] = EL_EMPTY;
+
+ /* before DrawPlayer() to draw correct player graphic for this case */
+ if (player->MovPos == 0)
+ CheckGravityMovement(player);
+
+#if 0
+ DrawPlayer(player); /* needed here only to cleanup last field */
+#endif
+
+ if (player->MovPos == 0) /* player reached destination field */
+ {
+#if 1
+ if (player->move_delay_reset_counter > 0)
+ {
+ player->move_delay_reset_counter--;
+
+ if (player->move_delay_reset_counter == 0)
+ {
+ /* continue with normal speed after quickly moving through gate */
+ HALVE_PLAYER_SPEED(player);
+
+ /* be able to make the next move without delay */
+ player->move_delay = 0;
+ }
+ }
+#else
+ if (IS_PASSABLE(Feld[last_jx][last_jy]))
+ {
+ /* continue with normal speed after quickly moving through gate */
+ HALVE_PLAYER_SPEED(player);
+
+ /* be able to make the next move without delay */
+ player->move_delay = 0;
+ }
+#endif
+
+ if (player->block_last_field &&
+ Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
+ Feld[last_jx][last_jy] = EL_EMPTY;
+
+ player->last_jx = jx;
+ player->last_jy = jy;
+
+ if (Feld[jx][jy] == EL_EXIT_OPEN ||
+ Feld[jx][jy] == EL_SP_EXIT_OPEN ||
+ Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
+ {
+ DrawPlayer(player); /* needed here only to cleanup last field */
+ RemoveHero(player);
+
+ if (local_player->friends_still_needed == 0 ||
+ IS_SP_ELEMENT(Feld[jx][jy]))
+ player->LevelSolved = player->GameOver = TRUE;
+ }
+
+#if 0
+ /* !!! ENABLE THIS FOR NEW VERSIONS !!! */
+ {
+ static int trigger_sides[4][2] =
+ {
+ /* enter side leave side */
+ { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* moving left */
+ { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* moving right */
+ { CH_SIDE_BOTTOM, CH_SIDE_TOP }, /* moving up */
+ { CH_SIDE_TOP, CH_SIDE_BOTTOM } /* moving down */
+ };
+ int move_direction = player->MovDir;
+ int enter_side = trigger_sides[MV_DIR_BIT(move_direction)][0];
+ int leave_side = trigger_sides[MV_DIR_BIT(move_direction)][1];
+ int old_jx = last_jx;
+ int old_jy = last_jy;
+
+#if 1
+ if (IS_CUSTOM_ELEMENT(Feld[old_jx][old_jy]))
+ {
+ CheckTriggeredElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
+ CE_OTHER_GETS_LEFT,
+ player->index_bit, leave_side);
+ CheckElementChangePlayer(old_jx, old_jy, Feld[old_jx][old_jy],
+ CE_LEFT_BY_PLAYER,
+ player->index_bit, leave_side);
+ }
+
+ if (IS_CUSTOM_ELEMENT(Feld[jx][jy]))
+ {
+ CheckTriggeredElementChangePlayer(jx, jy, Feld[jx][jy],
+ CE_OTHER_GETS_ENTERED,
+ player->index_bit, enter_side);
+ CheckElementChangePlayer(jx, jy, Feld[jx][jy], CE_ENTERED_BY_PLAYER,
+ player->index_bit, enter_side);
+ }
+#endif
+
+ }
+#endif
+
+ if (game.engine_version >= VERSION_IDENT(3,0,7,0))
+ {
+ TestIfHeroTouchesBadThing(jx, jy);
+ TestIfPlayerTouchesCustomElement(jx, jy);
+#if 1
+ TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
+#endif
+
+ if (!player->active)
+ RemoveHero(player);
+ }
+
+ if (level.use_step_counter)
+ {
+ int i;
+
+ TimePlayed++;
+
+ for (i = 0; i < MAX_PLAYERS; i++)
+ {
+ struct PlayerInfo *player = &stored_player[i];
+
+ if (SHIELD_ON(player))
+ {
+ player->shield_normal_time_left--;
+
+ if (player->shield_deadly_time_left > 0)
+ player->shield_deadly_time_left--;
+ }
+ }
+
+ if (TimeLeft > 0)
+ {
+ TimeLeft--;
+
+ if (TimeLeft <= 10 && setup.time_limit)
+ PlaySoundStereo(SND_GAME_RUNNING_OUT_OF_TIME, SOUND_MIDDLE);
+
+ DrawGameValue_Time(TimeLeft);
+
+ if (!TimeLeft && setup.time_limit)
+ for (i = 0; i < MAX_PLAYERS; i++)
+ KillHero(&stored_player[i]);
+ }
+ else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
+ DrawGameValue_Time(TimePlayed);
+ }
+
+ if (tape.single_step && tape.recording && !tape.pausing &&
+ !player->programmed_action)
+ TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+ }
+}
+
+void ScrollScreen(struct PlayerInfo *player, int mode)
+{
+ static unsigned long screen_frame_counter = 0;
+
+ if (mode == SCROLL_INIT)
+ {
+ /* set scrolling step size according to actual player's moving speed */
+ ScrollStepSize = TILEX / player->move_delay_value;
+
+ screen_frame_counter = FrameCounter;
+ ScreenMovDir = player->MovDir;
+ ScreenMovPos = player->MovPos;
+ ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
+ return;
+ }
+ else if (!FrameReached(&screen_frame_counter, 1))
+ return;
+
+ if (ScreenMovPos)
+ {
+ ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
+ ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
+ redraw_mask |= REDRAW_FIELD;
+ }
+ else
+ ScreenMovDir = MV_NO_MOVING;
+}
+
+void TestIfPlayerTouchesCustomElement(int x, int y)
+{
+ static int xy[4][2] =
+ {
+ { 0, -1 },
+ { -1, 0 },
+ { +1, 0 },
+ { 0, +1 }
+ };
+ static int trigger_sides[4][2] =
+ {
+ /* center side border side */
+ { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
+ { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
+ { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
+ { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
+ };
+ static int touch_dir[4] =
+ {
+ MV_LEFT | MV_RIGHT,
+ MV_UP | MV_DOWN,
+ MV_UP | MV_DOWN,
+ MV_LEFT | MV_RIGHT
+ };
+ int center_element = Feld[x][y]; /* should always be non-moving! */
+ int i;
+
+ for (i = 0; i < NUM_DIRECTIONS; i++)
+ {
+ int xx = x + xy[i][0];
+ int yy = y + xy[i][1];
+ int center_side = trigger_sides[i][0];
+ int border_side = trigger_sides[i][1];
+ int border_element;
+
+ if (!IN_LEV_FIELD(xx, yy))
+ continue;
+
+ if (IS_PLAYER(x, y))
+ {
+ struct PlayerInfo *player = PLAYERINFO(x, y);
+
+ if (game.engine_version < VERSION_IDENT(3,0,7,0))
+ border_element = Feld[xx][yy]; /* may be moving! */
+ else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
+ border_element = Feld[xx][yy];
+ else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
+ border_element = MovingOrBlocked2Element(xx, yy);
+ else
+ continue; /* center and border element do not touch */
+
+ CheckTriggeredElementChangePlayer(xx, yy, border_element,
+ CE_OTHER_GETS_TOUCHED,
+ player->index_bit, border_side);
+ CheckElementChangePlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
+ player->index_bit, border_side);
+ }
+ else if (IS_PLAYER(xx, yy))
+ {
+ struct PlayerInfo *player = PLAYERINFO(xx, yy);
+
+ if (game.engine_version >= VERSION_IDENT(3,0,7,0))
+ {
+ if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
+ continue; /* center and border element do not touch */
+ }
+
+ CheckTriggeredElementChangePlayer(x, y, center_element,
+ CE_OTHER_GETS_TOUCHED,
+ player->index_bit, center_side);
+ CheckElementChangePlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
+ player->index_bit, center_side);
+
+ break;
+ }
+ }
+}
+
+void TestIfElementTouchesCustomElement(int x, int y)
+{
+ static int xy[4][2] =
+ {
+ { 0, -1 },
+ { -1, 0 },
+ { +1, 0 },
+ { 0, +1 }
+ };
+ static int trigger_sides[4][2] =
+ {
+ /* center side border side */
+ { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
+ { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
+ { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
+ { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
+ };
+ static int touch_dir[4] =
+ {
+ MV_LEFT | MV_RIGHT,
+ MV_UP | MV_DOWN,
+ MV_UP | MV_DOWN,
+ MV_LEFT | MV_RIGHT
+ };
+ boolean change_center_element = FALSE;
+ int center_element_change_page = 0;
+ int center_element = Feld[x][y]; /* should always be non-moving! */
+ int i, j;
+
+ for (i = 0; i < NUM_DIRECTIONS; i++)
+ {
+ int xx = x + xy[i][0];
+ int yy = y + xy[i][1];
+ int center_side = trigger_sides[i][0];
+ int border_side = trigger_sides[i][1];
+ int border_element;
+
+ if (!IN_LEV_FIELD(xx, yy))
+ continue;
+
+ if (game.engine_version < VERSION_IDENT(3,0,7,0))
+ border_element = Feld[xx][yy]; /* may be moving! */
+ else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
+ border_element = Feld[xx][yy];
+ else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
+ border_element = MovingOrBlocked2Element(xx, yy);
+ else
+ continue; /* center and border element do not touch */
+
+ /* check for change of center element (but change it only once) */
+ if (IS_CUSTOM_ELEMENT(center_element) &&
+ HAS_ANY_CHANGE_EVENT(center_element, CE_OTHER_IS_TOUCHING) &&
+ !change_center_element)
+ {
+ for (j = 0; j < element_info[center_element].num_change_pages; j++)
+ {
+ struct ElementChangeInfo *change =
+ &element_info[center_element].change_page[j];
+
+ if (change->can_change &&
+ change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
+ change->trigger_side & border_side &&
+#if 1
+ IS_EQUAL_OR_IN_GROUP(border_element, change->trigger_element)
+#else
+ change->trigger_element == border_element
+#endif
+ )
+ {
+ change_center_element = TRUE;
+ center_element_change_page = j;
+
+ break;
+ }
+ }
+ }
+
+ /* check for change of border element */
+ if (IS_CUSTOM_ELEMENT(border_element) &&
+ HAS_ANY_CHANGE_EVENT(border_element, CE_OTHER_IS_TOUCHING))
+ {
+ for (j = 0; j < element_info[border_element].num_change_pages; j++)
+ {
+ struct ElementChangeInfo *change =
+ &element_info[border_element].change_page[j];
+
+ if (change->can_change &&
+ change->events & CH_EVENT_BIT(CE_OTHER_IS_TOUCHING) &&
+ change->trigger_side & center_side &&
+#if 1
+ IS_EQUAL_OR_IN_GROUP(center_element, change->trigger_element)
+#else
+ change->trigger_element == center_element
+#endif
+ )
+ {
+ CheckElementChangePage(xx, yy, border_element, CE_OTHER_IS_TOUCHING,
+ j);
+ break;
+ }
+ }
+ }
+ }
+
+ if (change_center_element)
+ CheckElementChangePage(x, y, center_element, CE_OTHER_IS_TOUCHING,
+ center_element_change_page);
+}
+
+void TestIfElementHitsCustomElement(int x, int y, int direction)
+{
+ int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
+ int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
+ int hitx = x + dx, hity = y + dy;
+ int hitting_element = Feld[x][y];
+#if 0
+ boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
+ !IS_FREE(hitx, hity) &&
+ (!IS_MOVING(hitx, hity) ||
+ MovDir[hitx][hity] != direction ||
+ ABS(MovPos[hitx][hity]) <= TILEY / 2));