+ else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+ RedrawPlayfield_SP(TRUE);
+ else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
+ RedrawPlayfield_MM();
+ else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
+ RedrawPlayfield_RND();
+
+ BlitScreenToBitmap(backbuffer);
+
+ BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
+ gfx.sx, gfx.sy);
+}
+
+static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
+ int draw_target)
+{
+ Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
+ Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
+
+ if (x == -1 && y == -1)
+ return;
+
+ if (draw_target == DRAW_TO_SCREEN)
+ BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
+ else
+ BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
+}
+
+static void DrawMaskedBorderExt_FIELD(int draw_target)
+{
+ if (global.border_status >= GAME_MODE_MAIN &&
+ global.border_status <= GAME_MODE_PLAYING &&
+ border.draw_masked[global.border_status])
+ DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
+ draw_target);
+}
+
+static void DrawMaskedBorderExt_DOOR_1(int draw_target)
+{
+ // when drawing to backbuffer, never draw border over open doors
+ if (draw_target == DRAW_TO_BACKBUFFER &&
+ (GetDoorState() & DOOR_OPEN_1))
+ return;
+
+ if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
+ (global.border_status != GAME_MODE_EDITOR ||
+ border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
+ DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
+}
+
+static void DrawMaskedBorderExt_DOOR_2(int draw_target)
+{
+ // when drawing to backbuffer, never draw border over open doors
+ if (draw_target == DRAW_TO_BACKBUFFER &&
+ (GetDoorState() & DOOR_OPEN_2))
+ return;
+
+ if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
+ global.border_status != GAME_MODE_EDITOR)
+ DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
+}
+
+static void DrawMaskedBorderExt_DOOR_3(int draw_target)
+{
+ /* currently not available */
+}
+
+static void DrawMaskedBorderExt_ALL(int draw_target)
+{
+ DrawMaskedBorderExt_FIELD(draw_target);
+ DrawMaskedBorderExt_DOOR_1(draw_target);
+ DrawMaskedBorderExt_DOOR_2(draw_target);
+ DrawMaskedBorderExt_DOOR_3(draw_target);
+}
+
+static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
+{
+ /* never draw masked screen borders on borderless screens */
+ if (global.border_status == GAME_MODE_LOADING ||
+ global.border_status == GAME_MODE_TITLE)
+ return;
+
+ if (redraw_mask & REDRAW_ALL)
+ DrawMaskedBorderExt_ALL(draw_target);
+ else
+ {
+ if (redraw_mask & REDRAW_FIELD)
+ DrawMaskedBorderExt_FIELD(draw_target);
+ if (redraw_mask & REDRAW_DOOR_1)
+ DrawMaskedBorderExt_DOOR_1(draw_target);
+ if (redraw_mask & REDRAW_DOOR_2)
+ DrawMaskedBorderExt_DOOR_2(draw_target);
+ if (redraw_mask & REDRAW_DOOR_3)
+ DrawMaskedBorderExt_DOOR_3(draw_target);
+ }
+}
+
+void DrawMaskedBorder_FIELD()
+{
+ DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
+}
+
+void DrawMaskedBorder(int redraw_mask)
+{
+ DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
+}
+
+void DrawMaskedBorderToTarget(int draw_target)
+{
+ if (draw_target == DRAW_TO_BACKBUFFER ||
+ draw_target == DRAW_TO_SCREEN)
+ {
+ DrawMaskedBorderExt(REDRAW_ALL, draw_target);
+ }
+ else
+ {
+ int last_border_status = global.border_status;
+
+ if (draw_target == DRAW_TO_FADE_SOURCE)
+ {
+ global.border_status = gfx.fade_border_source_status;
+ gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
+ }
+ else if (draw_target == DRAW_TO_FADE_TARGET)
+ {
+ global.border_status = gfx.fade_border_target_status;
+ gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
+ }
+
+ DrawMaskedBorderExt(REDRAW_ALL, draw_target);
+
+ global.border_status = last_border_status;
+ gfx.masked_border_bitmap_ptr = backbuffer;
+ }
+}
+
+void DrawTileCursor(int draw_target)
+{
+ Bitmap *fade_bitmap;
+ Bitmap *src_bitmap;
+ int src_x, src_y;
+ int dst_x, dst_y;
+ int graphic = IMG_GLOBAL_TILE_CURSOR;
+ int frame = 0;
+ int tilesize = TILESIZE_VAR;
+ int width = tilesize;
+ int height = tilesize;
+
+ if (game_status != GAME_MODE_PLAYING)
+ return;
+
+ if (!tile_cursor.enabled ||
+ !tile_cursor.active)
+ return;
+
+ if (tile_cursor.x != tile_cursor.target_x ||
+ tile_cursor.y != tile_cursor.target_y)
+ {
+ int step = TILESIZE_VAR / (GADGET_FRAME_DELAY / video.frame_delay_value);
+ int dx = tile_cursor.target_x - tile_cursor.x;
+ int dy = tile_cursor.target_y - tile_cursor.y;
+
+ if (ABS(dx) < step)
+ tile_cursor.x = tile_cursor.target_x;
+ else
+ tile_cursor.x += SIGN(dx) * step;
+
+ if (ABS(dy) < step)
+ tile_cursor.y = tile_cursor.target_y;
+ else
+ tile_cursor.y += SIGN(dy) * step;
+ }
+
+ dst_x = tile_cursor.x;
+ dst_y = tile_cursor.y;
+
+ frame = getGraphicAnimationFrame(graphic, -1);
+
+ getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
+
+ fade_bitmap =
+ (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
+ draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
+
+ if (draw_target == DRAW_TO_SCREEN)
+ BlitToScreenMasked(src_bitmap, src_x, src_y, width, height, dst_x, dst_y);
+ else
+ BlitBitmapMasked(src_bitmap, fade_bitmap, src_x, src_y, width, height,
+ dst_x, dst_y);
+}
+
+void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
+{
+ int fx = getFieldbufferOffsetX_RND();
+ int fy = getFieldbufferOffsetY_RND();
+
+ BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
+}
+
+void BlitScreenToBitmap(Bitmap *target_bitmap)
+{
+ if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
+ BlitScreenToBitmap_EM(target_bitmap);
+ else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
+ BlitScreenToBitmap_SP(target_bitmap);
+ else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
+ BlitScreenToBitmap_MM(target_bitmap);
+ else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
+ BlitScreenToBitmap_RND(target_bitmap);
+
+ redraw_mask |= REDRAW_FIELD;
+}
+
+void DrawFramesPerSecond()
+{
+ char text[100];
+ int font_nr = FONT_TEXT_2;
+ int font_width = getFontWidth(font_nr);
+ int draw_deactivation_mask = GetDrawDeactivationMask();
+ boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
+
+ /* draw FPS with leading space (needed if field buffer deactivated) */
+ sprintf(text, " %04.1f fps", global.frames_per_second);
+
+ /* override draw deactivation mask (required for invisible warp mode) */
+ SetDrawDeactivationMask(REDRAW_NONE);
+
+ /* draw opaque FPS if field buffer deactivated, else draw masked FPS */
+ DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
+ font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
+
+ /* set draw deactivation mask to previous value */
+ SetDrawDeactivationMask(draw_deactivation_mask);
+
+ /* force full-screen redraw in this frame */
+ redraw_mask = REDRAW_ALL;
+}
+
+#if DEBUG_FRAME_TIME
+static void PrintFrameTimeDebugging()
+{
+ static unsigned int last_counter = 0;
+ unsigned int counter = Counter();
+ int diff_1 = counter - last_counter;
+ int diff_2 = diff_1 - GAME_FRAME_DELAY;
+ int diff_2_max = 20;
+ int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
+ char diff_bar[2 * diff_2_max + 5];
+ int pos = 0;
+ int i;
+
+ diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
+
+ for (i = 0; i < diff_2_max; i++)
+ diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
+ i >= diff_2_max - diff_2_cut ? '-' : ' ');
+
+ diff_bar[pos++] = '|';
+
+ for (i = 0; i < diff_2_max; i++)
+ diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
+
+ diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
+
+ diff_bar[pos++] = '\0';
+
+ Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
+ counter,
+ diff_1,
+ (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
+ diff_bar);
+
+ last_counter = counter;
+}
+#endif
+
+static int unifiedRedrawMask(int mask)
+{
+ if (mask & REDRAW_ALL)
+ return REDRAW_ALL;
+
+ if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
+ return REDRAW_ALL;
+
+ return mask;
+}
+
+static boolean equalRedrawMasks(int mask_1, int mask_2)
+{
+ return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
+}
+
+void BackToFront()
+{
+ static int last_redraw_mask = REDRAW_NONE;
+
+ // force screen redraw in every frame to continue drawing global animations
+ // (but always use the last redraw mask to prevent unwanted side effects)
+ if (redraw_mask == REDRAW_NONE)
+ redraw_mask = last_redraw_mask;
+
+ last_redraw_mask = redraw_mask;
+
+#if 1
+ // masked border now drawn immediately when blitting backbuffer to window
+#else
+ // draw masked border to all viewports, if defined
+ DrawMaskedBorder(redraw_mask);
+#endif
+
+ // draw frames per second (only if debug mode is enabled)
+ if (redraw_mask & REDRAW_FPS)
+ DrawFramesPerSecond();
+
+ // remove playfield redraw before potentially merging with doors redraw
+ if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
+ redraw_mask &= ~REDRAW_FIELD;
+
+ // redraw complete window if both playfield and (some) doors need redraw
+ if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
+ redraw_mask = REDRAW_ALL;
+
+ /* although redrawing the whole window would be fine for normal gameplay,
+ being able to only redraw the playfield is required for deactivating
+ certain drawing areas (mainly playfield) to work, which is needed for
+ warp-forward to be fast enough (by skipping redraw of most frames) */
+
+ if (redraw_mask & REDRAW_ALL)
+ {
+ BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
+ }
+ else if (redraw_mask & REDRAW_FIELD)
+ {
+ BlitBitmap(backbuffer, window,
+ REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
+ }
+ else if (redraw_mask & REDRAW_DOORS)
+ {
+ // merge door areas to prevent calling screen redraw more than once
+ int x1 = WIN_XSIZE;
+ int y1 = WIN_YSIZE;
+ int x2 = 0;
+ int y2 = 0;
+
+ if (redraw_mask & REDRAW_DOOR_1)
+ {
+ x1 = MIN(x1, DX);
+ y1 = MIN(y1, DY);
+ x2 = MAX(x2, DX + DXSIZE);
+ y2 = MAX(y2, DY + DYSIZE);
+ }
+
+ if (redraw_mask & REDRAW_DOOR_2)
+ {
+ x1 = MIN(x1, VX);
+ y1 = MIN(y1, VY);
+ x2 = MAX(x2, VX + VXSIZE);
+ y2 = MAX(y2, VY + VYSIZE);
+ }
+
+ if (redraw_mask & REDRAW_DOOR_3)
+ {
+ x1 = MIN(x1, EX);
+ y1 = MIN(y1, EY);
+ x2 = MAX(x2, EX + EXSIZE);
+ y2 = MAX(y2, EY + EYSIZE);
+ }
+
+ // make sure that at least one pixel is blitted, and inside the screen
+ // (else nothing is blitted, causing the animations not to be updated)
+ x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
+ y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
+ x2 = MIN(MAX(1, x2), WIN_XSIZE);
+ y2 = MIN(MAX(1, y2), WIN_YSIZE);
+
+ BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
+ }
+
+ redraw_mask = REDRAW_NONE;
+
+#if DEBUG_FRAME_TIME
+ PrintFrameTimeDebugging();
+#endif
+}
+
+void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
+{
+ unsigned int frame_delay_value_old = GetVideoFrameDelay();
+
+ SetVideoFrameDelay(frame_delay_value);
+
+ BackToFront();
+
+ SetVideoFrameDelay(frame_delay_value_old);
+}
+
+static int fade_type_skip = FADE_TYPE_NONE;
+
+static void FadeExt(int fade_mask, int fade_mode, int fade_type)
+{
+ void (*draw_border_function)(void) = NULL;
+ int x, y, width, height;
+ int fade_delay, post_delay;