added drawing global borders when fading screens
[rocksndiamonds.git] / src / tools.c
index dbeba50c151b71240f4558ffc780909718ec1f46..3dcf88af26d04ebdcc2cff721e38734d10bcdc03 100644 (file)
@@ -24,7 +24,8 @@
 
 
 /* select level set with EMC X11 graphics before activating EM GFX debugging */
-#define DEBUG_EM_GFX   0
+#define DEBUG_EM_GFX           FALSE
+#define DEBUG_FRAME_TIME       FALSE
 
 /* tool button identifiers */
 #define TOOL_CTRL_ID_YES       0
@@ -287,50 +288,69 @@ void RedrawPlayfield()
             gfx.sx, gfx.sy);
 }
 
-void DrawMaskedBorder_Rect(int x, int y, int width, int height)
+static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
+                                    int draw_target)
 {
-  Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
+  Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
+  Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
 
-  BlitBitmapMasked(bitmap, backbuffer, x, y, width, height, x, y);
+  if (x == -1 && y == -1)
+    return;
+
+  if (draw_target == DRAW_BORDER_TO_SCREEN)
+    BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
+  else
+    BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
 }
 
-void DrawMaskedBorder_FIELD()
+static void DrawMaskedBorderExt_FIELD(int draw_target)
 {
   if (global.border_status >= GAME_MODE_TITLE &&
       global.border_status <= GAME_MODE_PLAYING &&
       border.draw_masked[global.border_status])
-    DrawMaskedBorder_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
+    DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
+                            draw_target);
 }
 
-void DrawMaskedBorder_DOOR_1()
+static void DrawMaskedBorderExt_DOOR_1(int draw_target)
 {
+  // when drawing to backbuffer, never draw border over open doors
+  if (draw_target == DRAW_BORDER_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]))
-    DrawMaskedBorder_Rect(DX, DY, DXSIZE, DYSIZE);
+    DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
 }
 
-void DrawMaskedBorder_DOOR_2()
+static void DrawMaskedBorderExt_DOOR_2(int draw_target)
 {
+  // when drawing to backbuffer, never draw border over open doors
+  if (draw_target == DRAW_BORDER_TO_BACKBUFFER &&
+      (GetDoorState() & DOOR_OPEN_2))
+    return;
+
   if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
       global.border_status != GAME_MODE_EDITOR)
-    DrawMaskedBorder_Rect(VX, VY, VXSIZE, VYSIZE);
+    DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
 }
 
-void DrawMaskedBorder_DOOR_3()
+static void DrawMaskedBorderExt_DOOR_3(int draw_target)
 {
   /* currently not available */
 }
 
-void DrawMaskedBorder_ALL()
+static void DrawMaskedBorderExt_ALL(int draw_target)
 {
-  DrawMaskedBorder_FIELD();
-  DrawMaskedBorder_DOOR_1();
-  DrawMaskedBorder_DOOR_2();
-  DrawMaskedBorder_DOOR_3();
+  DrawMaskedBorderExt_FIELD(draw_target);
+  DrawMaskedBorderExt_DOOR_1(draw_target);
+  DrawMaskedBorderExt_DOOR_2(draw_target);
+  DrawMaskedBorderExt_DOOR_3(draw_target);
 }
 
-void DrawMaskedBorder(int redraw_mask)
+static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
 {
   /* never draw masked screen borders on borderless screens */
   if (game_status == GAME_MODE_LOADING ||
@@ -338,17 +358,55 @@ void DrawMaskedBorder(int redraw_mask)
     return;
 
   if (redraw_mask & REDRAW_ALL)
-    DrawMaskedBorder_ALL();
+    DrawMaskedBorderExt_ALL(draw_target);
   else
   {
     if (redraw_mask & REDRAW_FIELD)
-      DrawMaskedBorder_FIELD();
+      DrawMaskedBorderExt_FIELD(draw_target);
     if (redraw_mask & REDRAW_DOOR_1)
-      DrawMaskedBorder_DOOR_1();
+      DrawMaskedBorderExt_DOOR_1(draw_target);
     if (redraw_mask & REDRAW_DOOR_2)
-      DrawMaskedBorder_DOOR_2();
+      DrawMaskedBorderExt_DOOR_2(draw_target);
     if (redraw_mask & REDRAW_DOOR_3)
-      DrawMaskedBorder_DOOR_3();
+      DrawMaskedBorderExt_DOOR_3(draw_target);
+  }
+}
+
+void DrawMaskedBorder_FIELD()
+{
+  DrawMaskedBorderExt_FIELD(DRAW_BORDER_TO_BACKBUFFER);
+}
+
+void DrawMaskedBorder(int redraw_mask)
+{
+  DrawMaskedBorderExt(redraw_mask, DRAW_BORDER_TO_BACKBUFFER);
+}
+
+void DrawMaskedBorderToTarget(int draw_target)
+{
+  if (draw_target == DRAW_BORDER_TO_SCREEN)
+  {
+    DrawMaskedBorderExt(REDRAW_ALL, draw_target);
+  }
+  else
+  {
+    int last_border_status = global.border_status;
+
+    if (draw_target == DRAW_BORDER_TO_FADE_SOURCE)
+    {
+      global.border_status = gfx.fade_border_source_status;
+      gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
+    }
+    else
+    {
+      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;
   }
 }
 
@@ -434,13 +492,61 @@ void DrawFramesPerSecond()
              font_nr, BLIT_OPAQUE);
 }
 
+#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
+
 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)
-    return;
+    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)
@@ -477,6 +583,10 @@ void BackToFront()
   }
 
   redraw_mask = REDRAW_NONE;
+
+#if DEBUG_FRAME_TIME
+  PrintFrameTimeDebugging();
+#endif
 }
 
 static void FadeCrossSaveBackbuffer()
@@ -596,8 +706,43 @@ static void FadeExt(int fade_mask, int fade_mode, int fade_type)
   redraw_mask &= ~fade_mask;
 }
 
+static void SetScreenStates_BeforeFadingIn()
+{
+}
+
+static void SetScreenStates_AfterFadingIn()
+{
+  // store new source screen (to use correct masked border for fading)
+  gfx.fade_border_source_status = global.border_status;
+
+  global.anim_status = global.anim_status_next;
+
+  // force update of global animation status in case of rapid screen changes
+  redraw_mask = REDRAW_ALL;
+  BackToFront();
+}
+
+static void SetScreenStates_BeforeFadingOut()
+{
+  // store new target screen (to use correct masked border for fading)
+  gfx.fade_border_target_status = game_status;
+
+  global.anim_status = GAME_MODE_PSEUDO_FADING;
+}
+
+static void SetScreenStates_AfterFadingOut()
+{
+  global.border_status = game_status;
+}
+
 void FadeIn(int fade_mask)
 {
+  SetScreenStates_BeforeFadingIn();
+
+#if 1
+  DrawMaskedBorder(REDRAW_ALL);
+#endif
+
   if (fading.fade_mode & FADE_TYPE_TRANSFORM)
     FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
   else
@@ -607,16 +752,24 @@ void FadeIn(int fade_mask)
   FADE_SY = REAL_SY;
   FADE_SXSIZE = FULL_SXSIZE;
   FADE_SYSIZE = FULL_SYSIZE;
+
+  SetScreenStates_AfterFadingIn();
 }
 
 void FadeOut(int fade_mask)
 {
+  SetScreenStates_BeforeFadingOut();
+
+#if 0
+  DrawMaskedBorder(REDRAW_ALL);
+#endif
+
   if (fading.fade_mode & FADE_TYPE_TRANSFORM)
     FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
   else
     FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
 
-  global.border_status = game_status;
+  SetScreenStates_AfterFadingOut();
 }
 
 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
@@ -710,14 +863,14 @@ Bitmap *getGlobalBorderBitmap(int graphic)
   return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
 }
 
-Bitmap *getGlobalBorderBitmapFromGameStatus()
+Bitmap *getGlobalBorderBitmapFromStatus(int status)
 {
   int graphic =
-    (game_status == GAME_MODE_MAIN ||
-     game_status == GAME_MODE_PSEUDO_TYPENAME  ? IMG_GLOBAL_BORDER_MAIN :
-     game_status == GAME_MODE_SCORES           ? IMG_GLOBAL_BORDER_SCORES :
-     game_status == GAME_MODE_EDITOR           ? IMG_GLOBAL_BORDER_EDITOR :
-     game_status == GAME_MODE_PLAYING          ? IMG_GLOBAL_BORDER_PLAYING :
+    (status == GAME_MODE_MAIN ||
+     status == GAME_MODE_PSEUDO_TYPENAME       ? IMG_GLOBAL_BORDER_MAIN :
+     status == GAME_MODE_SCORES                        ? IMG_GLOBAL_BORDER_SCORES :
+     status == GAME_MODE_EDITOR                        ? IMG_GLOBAL_BORDER_EDITOR :
+     status == GAME_MODE_PLAYING               ? IMG_GLOBAL_BORDER_PLAYING :
      IMG_GLOBAL_BORDER);
 
   return getGlobalBorderBitmap(graphic);
@@ -818,7 +971,7 @@ boolean CheckIfGlobalBorderHasChanged()
     return FALSE;
 
   // determine and store new global border bitmap for current game status
-  global_border_bitmap = getGlobalBorderBitmapFromGameStatus();
+  global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
 
   return (global_border_bitmap_last != global_border_bitmap);
 }
@@ -865,7 +1018,7 @@ void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
 
 void RedrawGlobalBorder()
 {
-  Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
+  Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
 
   RedrawGlobalBorderFromBitmap(bitmap);
 
@@ -2342,13 +2495,13 @@ void DrawEnvelopeRequest(char *text)
                                  tile_size, tile_size);
 
   /* force DOOR font inside door area */
-  game_status = GAME_MODE_PSEUDO_DOOR;
+  SetGameStatus(GAME_MODE_PSEUDO_DOOR);
 
   DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
                 line_length, -1, max_lines, line_spacing, mask_mode,
                 request.autowrap, request.centered, FALSE);
 
-  game_status = last_game_status;      /* restore current game status */
+  SetGameStatus(last_game_status);     /* restore current game status */
 
   for (i = 0; i < NUM_TOOL_BUTTONS; i++)
     RedrawGadget(tool_gadget[i]);
@@ -2753,7 +2906,7 @@ static void DrawPreviewLevelExt(boolean restart)
        DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
     }
 
-    game_status = last_game_status;    /* restore current game status */
+    SetGameStatus(last_game_status);   /* restore current game status */
 
     return;
   }
@@ -2855,7 +3008,7 @@ static void DrawPreviewLevelExt(boolean restart)
     DrawPreviewLevelLabelExt(label_state);
   }
 
-  game_status = last_game_status;      /* restore current game status */
+  SetGameStatus(last_game_status);     /* restore current game status */
 }
 
 void DrawPreviewLevelInitial()
@@ -3640,7 +3793,7 @@ static boolean RequestDoor(char *text, unsigned int req_state)
   DrawBackground(DX, DY, DXSIZE, DYSIZE);
 
   /* force DOOR font inside door area */
-  game_status = GAME_MODE_PSEUDO_DOOR;
+  SetGameStatus(GAME_MODE_PSEUDO_DOOR);
 
   /* write text for request */
   for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
@@ -3680,7 +3833,7 @@ static boolean RequestDoor(char *text, unsigned int req_state)
     // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
   }
 
-  game_status = last_game_status;      /* restore current game status */
+  SetGameStatus(last_game_status);     /* restore current game status */
 
   if (req_state & REQ_ASK)
   {
@@ -4124,7 +4277,7 @@ unsigned int MoveDoor(unsigned int door_state)
     { DX, DY, DXSIZE, DYSIZE },
     { VX, VY, VXSIZE, VYSIZE }
   };
-  static int door1 = DOOR_OPEN_1;
+  static int door1 = DOOR_CLOSE_1;
   static int door2 = DOOR_CLOSE_2;
   unsigned int door_delay = 0;
   unsigned int door_delay_value;
@@ -4438,6 +4591,10 @@ unsigned int MoveDoor(unsigned int door_state)
   if (door_state & DOOR_ACTION_2)
     door2 = door_state & DOOR_ACTION_2;
 
+  // draw masked border over door area
+  DrawMaskedBorder(REDRAW_DOOR_1);
+  DrawMaskedBorder(REDRAW_DOOR_2);
+
   return (door1 | door2);
 }
 
@@ -8134,6 +8291,13 @@ void JoinRectangles(int *x, int *y, int *width, int *height,
   *height = MAX(*height, height2);
 }
 
+void SetGameStatus(int game_status_new)
+{
+  game_status = game_status_new;
+
+  global.anim_status_next = game_status;
+}
+
 void ChangeViewportPropertiesIfNeeded()
 {
   int gfx_game_mode = game_status;
@@ -8189,6 +8353,7 @@ void ChangeViewportPropertiesIfNeeded()
 
     init_video_buffer = TRUE;
     init_gfx_buffers = TRUE;
+    init_gadgets_and_toons = TRUE;
 
     // printf("::: video: init_video_buffer, init_gfx_buffers\n");
   }
@@ -8339,6 +8504,7 @@ void ChangeViewportPropertiesIfNeeded()
 
     InitGadgets();
     InitToons();
+    InitGlobalAnimations();
   }
 
   if (init_em_graphics)