+ player->MovPos =
+ (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
+
+ ScrollFigure(player, SCROLL_INIT);
+
+ return MF_MOVING;
+}
+
+boolean MoveFigure(struct PlayerInfo *player, int dx, int dy)
+{
+ int jx = player->jx, jy = player->jy;
+ int old_jx = jx, old_jy = jy;
+ int moved = MF_NO_ACTION;
+
+ if (player->gone || (!dx && !dy))
+ return FALSE;
+
+ if (!FrameReached(&player->move_delay, player->move_delay_value) &&
+ !tape.playing)
+ return FALSE;
+
+ /* remove the last programmed player action */
+ player->programmed_action = 0;
+
+ if (player->MovPos)
+ {
+ /* should only happen if pre-1.2 tape recordings are played */
+ /* this is only for backward compatibility */
+
+ int original_move_delay_value = player->move_delay_value;
+
+#if DEBUG
+ printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES.\n");
+#endif
+
+ /* scroll remaining steps with finest movement resolution */
+ player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
+
+ while (player->MovPos)
+ {
+ ScrollFigure(player, SCROLL_GO_ON);
+ ScrollScreen(NULL, SCROLL_GO_ON);
+ FrameCounter++;
+ DrawAllPlayers();
+ BackToFront();
+ }
+
+ player->move_delay_value = original_move_delay_value;
+ }
+
+ if (player->last_move_dir & (MV_LEFT | MV_RIGHT))
+ {
+ if (!(moved |= MoveFigureOneStep(player, 0, dy, dx, dy)))
+ moved |= MoveFigureOneStep(player, dx, 0, dx, dy);
+ }
+ else
+ {
+ if (!(moved |= MoveFigureOneStep(player, dx, 0, dx, dy)))
+ moved |= MoveFigureOneStep(player, 0, dy, dx, dy);
+ }
+
+ jx = player->jx;
+ jy = player->jy;
+
+ if (moved & MF_MOVING && !ScreenMovPos &&
+ (player == local_player || !options.network))
+ {
+ int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
+ int offset = (setup.scroll_delay ? 3 : 0);
+
+ if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
+ {
+ /* actual player has left the screen -- scroll in that direction */
+ if (jx != old_jx) /* player has moved horizontally */
+ scroll_x += (jx - old_jx);
+ else /* player has moved vertically */
+ scroll_y += (jy - old_jy);
+ }
+ else
+ {
+ if (jx != old_jx) /* player has moved horizontally */
+ {
+ if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
+ (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
+ scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
+
+ /* don't scroll over playfield boundaries */
+ if (scroll_x < SBX_Left || scroll_x > SBX_Right)
+ scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
+
+ /* don't scroll more than one field at a time */
+ scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
+
+ /* don't scroll against the player's moving direction */
+ if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
+ (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
+ scroll_x = old_scroll_x;
+ }
+ else /* player has moved vertically */
+ {
+ if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
+ (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
+ scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
+
+ /* don't scroll over playfield boundaries */
+ if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
+ scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
+
+ /* don't scroll more than one field at a time */
+ scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
+
+ /* don't scroll against the player's moving direction */
+ if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
+ (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
+ scroll_y = old_scroll_y;
+ }
+ }
+
+ if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
+ {
+ if (!options.network && !AllPlayersInVisibleScreen())
+ {
+ scroll_x = old_scroll_x;
+ scroll_y = old_scroll_y;
+ }
+ else
+ {
+ ScrollScreen(player, SCROLL_INIT);
+ ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
+ }
+ }
+ }
+
+ if (!(moved & MF_MOVING) && !player->Pushing)
+ player->Frame = 0;
+ else
+ player->Frame = (player->Frame + 1) % 4;
+
+ 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 "ErdreichAnbroeckeln()" */
+
+ player->last_move_dir = player->MovDir;
+ }
+ else
+ {
+ CheckGravityMovement(player);
+
+ player->last_move_dir = MV_NO_MOVING;
+ }
+
+ TestIfHeroHitsBadThing(jx, jy);
+
+ if (player->gone)
+ RemoveHero(player);
+
+ return moved;
+}
+
+void ScrollFigure(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->gone || !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_LEERRAUM)
+ Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
+
+ DrawPlayer(player);
+ 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 (Feld[last_jx][last_jy] == EL_PLAYER_IS_LEAVING)
+ Feld[last_jx][last_jy] = EL_LEERRAUM;
+
+ /* before DrawPlayer() to draw correct player graphic for this case */
+ if (player->MovPos == 0)
+ CheckGravityMovement(player);
+
+ DrawPlayer(player);
+
+ if (player->MovPos == 0)
+ {
+ if (IS_QUICK_GATE(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;
+ }
+
+ player->last_jx = jx;
+ player->last_jy = jy;
+
+ if (Feld[jx][jy] == EL_AUSGANG_AUF)
+ {
+ RemoveHero(player);
+
+ if (!local_player->friends_still_needed)
+ player->LevelSolved = player->GameOver = TRUE;
+ }
+ }
+}
+
+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;