added step-based engine snapshots to undo/redo game steps
[rocksndiamonds.git] / src / tools.c
index 082aa5ea08cb503b0fea7bd304ec9ce5ac66f032..e98ac35b9476491b9b6192a282f16c8c447d99e0 100644 (file)
@@ -292,7 +292,6 @@ void DrawMaskedBorder_Rect(int x, int y, int width, int height)
 {
   Bitmap *bitmap = graphic_info[IMG_GLOBAL_BORDER].bitmap;
 
-  SetClipOrigin(bitmap, bitmap->stored_clip_gc, 0, 0);
   BlitBitmapMasked(bitmap, backbuffer, x, y, width, height, x, y);
 }
 
@@ -531,7 +530,7 @@ void BackToFront()
      this could mean that we have to wait for the graphics to complete,
      although we could go on doing calculations for the next frame */
 
-  SyncDisplay();
+  /* SyncDisplay(); */
 
   /* never draw masked border to backbuffer when using playfield buffer */
   if (game_status != GAME_MODE_PLAYING ||
@@ -664,8 +663,6 @@ void BackToFront()
     DrawTextExt(window, SX + SXSIZE + SX, 0, text, FONT_TEXT_2, BLIT_OPAQUE);
   }
 
-  FlushDisplay();
-
   for (x = 0; x < MAX_BUF_XSIZE; x++)
     for (y = 0; y < MAX_BUF_YSIZE; y++)
       redraw[x][y] = 0;
@@ -1055,6 +1052,10 @@ void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
   int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
   int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
 
+  // if no in-game graphics defined, always use standard graphic size
+  if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
+    tilesize = TILESIZE;
+
   if (tilesize == gfx.standard_tile_size)
     src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
   else if (tilesize == game.tile_size)
@@ -1249,9 +1250,6 @@ void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
 
   getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
 
-  SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
-               dst_x - src_x, dst_y - src_y);
-
   BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
                   dst_x, dst_y);
 }
@@ -1259,14 +1257,14 @@ void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
                                 int graphic, int frame)
 {
+  struct GraphicInfo *g = &graphic_info[graphic];
   Bitmap *src_bitmap;
   int src_x, src_y;
 
   getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
 
-  SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
-               dst_x - src_x, dst_y - src_y);
-  BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY, dst_x, dst_y);
+  BlitBitmapMasked(src_bitmap, d, src_x, src_y, g->width, g->height,
+                  dst_x, dst_y);
 }
 
 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
@@ -1402,12 +1400,8 @@ inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
     dst_y = FY + y * TILEY_VAR + dy;
 
     if (mask_mode == USE_MASKING)
-    {
-      SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
-                   dst_x - src_x, dst_y - src_y);
       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
                       dst_x, dst_y);
-    }
     else
       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
                 dst_x, dst_y);
@@ -1453,12 +1447,8 @@ inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
     dst_y = FY + y1 * TILEY_VAR;
 
     if (mask_mode == USE_MASKING)
-    {
-      SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
-                   dst_x - src_x, dst_y - src_y);
       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
                       dst_x, dst_y);
-    }
     else
       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
                 dst_x, dst_y);
@@ -1475,12 +1465,8 @@ inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
     dst_y = FY + y2 * TILEY_VAR;
 
     if (mask_mode == USE_MASKING)
-    {
-      SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
-                   dst_x - src_x, dst_y - src_y);
       BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
                       dst_x, dst_y);
-    }
     else
       BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
                 dst_x, dst_y);
@@ -2042,6 +2028,14 @@ void DrawLevelField(int x, int y)
   }
 }
 
+void DrawSizedElement(int x, int y, int element, int tilesize)
+{
+  int graphic;
+
+  graphic = el2edimg(element);
+  DrawSizedGraphic(x, y, graphic, 0, tilesize);
+}
+
 void DrawMiniElement(int x, int y, int element)
 {
   int graphic;
@@ -2050,6 +2044,19 @@ void DrawMiniElement(int x, int y, int element)
   DrawMiniGraphic(x, y, graphic);
 }
 
+void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
+                           int tilesize)
+{
+  int x = sx + scroll_x, y = sy + scroll_y;
+
+  if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
+    DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
+  else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
+    DrawSizedElement(sx, sy, Feld[x][y], tilesize);
+  else
+    DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
+}
+
 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
 {
   int x = sx + scroll_x, y = sy + scroll_y;
@@ -2094,12 +2101,8 @@ void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
            inner_sy + (y - 1) * tile_height % inner_height);
 
   if (draw_masked)
-  {
-    SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
-                 dst_x - src_x, dst_y - src_y);
     BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
                     dst_x, dst_y);
-  }
   else
     BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
               dst_x, dst_y);
@@ -2519,6 +2522,18 @@ void DrawLevel(int draw_background_mask)
   redraw_mask |= REDRAW_FIELD;
 }
 
+void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
+                   int tilesize)
+{
+  int x,y;
+
+  for (x = 0; x < size_x; x++)
+    for (y = 0; y < size_y; y++)
+      DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
+
+  redraw_mask |= REDRAW_FIELD;
+}
+
 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
 {
   int x,y;
@@ -3374,109 +3389,110 @@ static int RequestHandleEvents(unsigned int req_state)
     {
       Event event;
 
-      NextEvent(&event);
-
-      switch (event.type)
+      while (NextValidEvent(&event))
       {
-       case EVENT_BUTTONPRESS:
-       case EVENT_BUTTONRELEASE:
-       case EVENT_MOTIONNOTIFY:
+       switch (event.type)
        {
-         if (event.type == EVENT_MOTIONNOTIFY)
+         case EVENT_BUTTONPRESS:
+         case EVENT_BUTTONRELEASE:
+         case EVENT_MOTIONNOTIFY:
          {
-           if (!PointerInWindow(window))
-             continue; /* window and pointer are on different screens */
-
-           if (!button_status)
-             continue;
-
-           motion_status = TRUE;
-           mx = ((MotionEvent *) &event)->x;
-           my = ((MotionEvent *) &event)->y;
-         }
-         else
-         {
-           motion_status = FALSE;
-           mx = ((ButtonEvent *) &event)->x;
-           my = ((ButtonEvent *) &event)->y;
-           if (event.type == EVENT_BUTTONPRESS)
-             button_status = ((ButtonEvent *) &event)->button;
+           if (event.type == EVENT_MOTIONNOTIFY)
+           {
+             if (!PointerInWindow(window))
+               continue;       /* window and pointer on different screens */
+
+             if (!button_status)
+               continue;
+
+             motion_status = TRUE;
+             mx = ((MotionEvent *) &event)->x;
+             my = ((MotionEvent *) &event)->y;
+           }
            else
-             button_status = MB_RELEASED;
-         }
-
-         /* this sets 'request_gadget_id' */
-         HandleGadgets(mx, my, button_status);
-
-         switch (request_gadget_id)
-         {
-           case TOOL_CTRL_ID_YES:
-             result = TRUE;
-             break;
-           case TOOL_CTRL_ID_NO:
-             result = FALSE;
-             break;
-           case TOOL_CTRL_ID_CONFIRM:
-             result = TRUE | FALSE;
-             break;
-
-           case TOOL_CTRL_ID_PLAYER_1:
-             result = 1;
-             break;
-           case TOOL_CTRL_ID_PLAYER_2:
-             result = 2;
-             break;
-           case TOOL_CTRL_ID_PLAYER_3:
-             result = 3;
-             break;
-           case TOOL_CTRL_ID_PLAYER_4:
-             result = 4;
-             break;
-
-           default:
-             break;
+           {
+             motion_status = FALSE;
+             mx = ((ButtonEvent *) &event)->x;
+             my = ((ButtonEvent *) &event)->y;
+             if (event.type == EVENT_BUTTONPRESS)
+               button_status = ((ButtonEvent *) &event)->button;
+             else
+               button_status = MB_RELEASED;
+           }
+
+           /* this sets 'request_gadget_id' */
+           HandleGadgets(mx, my, button_status);
+
+           switch (request_gadget_id)
+           {
+             case TOOL_CTRL_ID_YES:
+               result = TRUE;
+               break;
+             case TOOL_CTRL_ID_NO:
+               result = FALSE;
+               break;
+             case TOOL_CTRL_ID_CONFIRM:
+               result = TRUE | FALSE;
+               break;
+
+             case TOOL_CTRL_ID_PLAYER_1:
+               result = 1;
+               break;
+             case TOOL_CTRL_ID_PLAYER_2:
+               result = 2;
+               break;
+             case TOOL_CTRL_ID_PLAYER_3:
+               result = 3;
+               break;
+             case TOOL_CTRL_ID_PLAYER_4:
+               result = 4;
+               break;
+
+             default:
+               break;
+           }
+
+           break;
          }
 
-         break;
-       }
-
-       case EVENT_KEYPRESS:
-         switch (GetEventKey((KeyEvent *)&event, TRUE))
-         {
-           case KSYM_space:
-             if (req_state & REQ_CONFIRM)
-               result = 1;
-             break;
+         case EVENT_KEYPRESS:
+           switch (GetEventKey((KeyEvent *)&event, TRUE))
+           {
+             case KSYM_space:
+               if (req_state & REQ_CONFIRM)
+                 result = 1;
+               break;
 
-           case KSYM_Return:
+             case KSYM_Return:
 #if defined(TARGET_SDL2)
-           case KSYM_Menu:
+             case KSYM_Menu:
 #endif
-             result = 1;
-             break;
+               result = 1;
+               break;
 
-           case KSYM_Escape:
+             case KSYM_Escape:
 #if defined(TARGET_SDL2)
-           case KSYM_Back:
+             case KSYM_Back:
 #endif
-             result = 0;
-             break;
+               result = 0;
+               break;
 
-           default:
-             break;
-         }
+             default:
+               break;
+           }
 
-         if (req_state & REQ_PLAYER)
-           result = 0;
-         break;
+           if (req_state & REQ_PLAYER)
+             result = 0;
+           break;
 
-       case EVENT_KEYRELEASE:
-         ClearPlayerAction();
-         break;
+         case EVENT_KEYRELEASE:
+           ClearPlayerAction();
+           break;
 
-       default:
-         HandleOtherEvents(&event);
-         break;
+         default:
+           HandleOtherEvents(&event);
+           break;
+       }
       }
     }
     else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
@@ -3954,11 +3970,13 @@ void InitGraphicCompatibilityInfo_Doors()
        {
          num_panel_steps = 2 * door_rect->height / door->step_offset;
          door->panel.start_step = num_panel_steps - num_door_steps;
+         door->panel.start_step_closing = door->panel.start_step;
        }
        else
        {
          num_panel_steps = door_rect->height / door->step_offset;
          door->panel.start_step = num_panel_steps - num_door_steps / 2;
+         door->panel.start_step_closing = door->panel.start_step;
          door->panel.step_delay *= 2;
        }
       }
@@ -4059,15 +4077,6 @@ unsigned int MoveDoor(unsigned int door_state)
   unsigned int door_delay_value;
   int i;
 
-  if (door_1.width < 0 || door_1.width > DXSIZE)
-    door_1.width = DXSIZE;
-  if (door_1.height < 0 || door_1.height > DYSIZE)
-    door_1.height = DYSIZE;
-  if (door_2.width < 0 || door_2.width > VXSIZE)
-    door_2.width = VXSIZE;
-  if (door_2.height < 0 || door_2.height > VYSIZE)
-    door_2.height = VYSIZE;
-
   if (door_state == DOOR_GET_STATE)
     return (door1 | door2);
 
@@ -4176,6 +4185,8 @@ unsigned int MoveDoor(unsigned int door_state)
       }
     }
 
+    max_step_delay = MAX(1, max_step_delay);   // prevent division by zero
+
     num_move_steps = max_move_delay / max_step_delay;
     num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
 
@@ -4383,8 +4394,6 @@ void DrawSpecialEditorDoor()
   int vy = VY - outer_border;
   int exsize = EXSIZE + 2 * outer_border;
 
-  CloseDoor(DOOR_CLOSE_2);
-
   /* draw bigger level editor toolbox window */
   BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
             top_border_width, top_border_height, ex, ey - top_border_height);
@@ -7785,13 +7794,65 @@ void InitGraphicInfo_EM(void)
 #endif
 }
 
+void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
+                               boolean any_player_moving,
+                               boolean any_player_snapping,
+                               boolean any_player_dropping)
+{
+  static boolean player_was_waiting = TRUE;
+
+  if (!tape.recording)
+    return;
+
+  if (frame == 0 && !any_player_dropping)
+  {
+    if (!player_was_waiting)
+    {
+      SaveEngineSnapshotToList();
+
+      player_was_waiting = TRUE;
+    }
+  }
+  else if (any_player_moving || any_player_snapping || any_player_dropping)
+  {
+    player_was_waiting = FALSE;
+  }
+}
+
+void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
+                               boolean murphy_is_dropping)
+{
+  static boolean player_was_waiting = FALSE;
+
+  if (!tape.recording)
+    return;
+
+  if (murphy_is_waiting)
+  {
+    if (!player_was_waiting)
+    {
+      SaveEngineSnapshotToList();
+
+      player_was_waiting = TRUE;
+    }
+  }
+  else
+  {
+    player_was_waiting = FALSE;
+  }
+}
+
 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
                            boolean any_player_moving,
-                           boolean player_is_dropping)
+                           boolean any_player_snapping,
+                           boolean any_player_dropping)
 {
   if (tape.single_step && tape.recording && !tape.pausing)
-    if (frame == 0 && !player_is_dropping)
+    if (frame == 0 && !any_player_dropping)
       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+
+  CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
+                            any_player_snapping, any_player_dropping);
 }
 
 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
@@ -7800,6 +7861,8 @@ void CheckSingleStepMode_SP(boolean murphy_is_waiting,
   if (tape.single_step && tape.recording && !tape.pausing)
     if (murphy_is_waiting)
       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
+
+  CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
 }
 
 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
@@ -8018,6 +8081,7 @@ void ChangeViewportPropertiesIfNeeded()
   boolean init_video_buffer = FALSE;
   boolean init_gadgets_and_toons = FALSE;
   boolean init_em_graphics = FALSE;
+  boolean drawing_area_changed = FALSE;
 
   if (viewport.window.width  != WIN_XSIZE ||
       viewport.window.height != WIN_YSIZE)
@@ -8070,12 +8134,25 @@ void ChangeViewportPropertiesIfNeeded()
       // printf("::: new_tilesize_var != TILESIZE_VAR\n");
 
       // changing tile size invalidates scroll values of engine snapshots
-      FreeEngineSnapshot();
+      FreeEngineSnapshotSingle();
 
       // changing tile size requires update of graphic mapping for EM engine
       init_em_graphics = TRUE;
     }
 
+    if (new_sx != SX ||
+       new_sy != SY ||
+       new_sxsize != SXSIZE ||
+       new_sysize != SYSIZE ||
+       new_real_sx != REAL_SX ||
+       new_real_sy != REAL_SY ||
+       new_full_sxsize != FULL_SXSIZE ||
+       new_full_sysize != FULL_SYSIZE)
+    {
+      if (!init_video_buffer)
+       drawing_area_changed = TRUE;
+    }
+
     SX = new_sx;
     SY = new_sy;
     DX = new_dx;
@@ -8116,6 +8193,11 @@ void ChangeViewportPropertiesIfNeeded()
 
     SCR_FIELDX = new_scr_fieldx;
     SCR_FIELDY = new_scr_fieldy;
+
+    gfx.drawing_area_changed = drawing_area_changed;
+
+    SetDrawDeactivationMask(REDRAW_NONE);
+    SetDrawBackgroundMask(REDRAW_FIELD);
   }
 
   if (init_video_buffer)
@@ -8123,9 +8205,6 @@ void ChangeViewportPropertiesIfNeeded()
     // printf("::: init_video_buffer\n");
 
     InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
-
-    SetDrawDeactivationMask(REDRAW_NONE);
-    SetDrawBackgroundMask(REDRAW_FIELD);
   }
 
   if (init_gadgets_and_toons)