added tile selection cursor for playing MM levels with keyboard or joystick
[rocksndiamonds.git] / src / tools.c
index 63718a51c691b3e92d8ce743826fc3a2408150cf..3c301a3912f9b03461885a1102dfafe0807dee71 100644 (file)
@@ -635,6 +635,61 @@ void DrawMaskedBorderToTarget(int draw_target)
   }
 }
 
+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();
@@ -1005,6 +1060,7 @@ void FadeOut(int fade_mask)
 
   SetScreenStates_BeforeFadingOut();
 
+  SetTileCursorActive(FALSE);
   SetOverlayActive(FALSE);
 
 #if 0
@@ -2486,12 +2542,14 @@ void DrawLevelField(int x, int y)
 }
 
 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
-                               int (*el2img_function)(int), boolean masked)
+                               int (*el2img_function)(int), boolean masked,
+                               int element_bits_draw)
 {
   int element_base = map_mm_wall_element(element);
   int element_bits = (IS_DF_WALL(element) ?
                      element - EL_DF_WALL_START :
-                     element - EL_MM_WALL_START) & 0x000f;
+                     IS_MM_WALL(element) ?
+                     element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
   int graphic = el2img_function(element_base);
   int tilesize_draw = tilesize / 2;
   Bitmap *src_bitmap;
@@ -2505,6 +2563,9 @@ static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
     int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
     int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
 
+    if (!(element_bits_draw & (1 << i)))
+      continue;
+
     if (element_bits & (1 << i))
     {
       if (masked)
@@ -2523,10 +2584,18 @@ static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
   }
 }
 
+void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
+                          boolean masked, int element_bits_draw)
+{
+  DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
+                     element, tilesize, el2edimg, masked, element_bits_draw);
+}
+
 void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
                      int (*el2img_function)(int))
 {
-  DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE);
+  DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
+                     0x000f);
 }
 
 void DrawSizedElementExt(int x, int y, int element, int tilesize,
@@ -2535,7 +2604,7 @@ void DrawSizedElementExt(int x, int y, int element, int tilesize,
   if (IS_MM_WALL(element))
   {
     DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
-                       element, tilesize, el2edimg, masked);
+                       element, tilesize, el2edimg, masked, 0x000f);
   }
   else
   {
@@ -3973,7 +4042,9 @@ static int RequestHandleEvents(unsigned int req_state)
   {
     if (level_solved)
     {
-      SetDrawtoField(DRAW_TO_FIELDBUFFER);
+      /* the MM game engine does not use a special (scrollable) field buffer */
+      if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
+       SetDrawtoField(DRAW_TO_FIELDBUFFER);
 
       HandleGameActions();
 
@@ -4755,7 +4826,7 @@ unsigned int MoveDoor(unsigned int door_state)
     door_state &= ~DOOR_CLOSE_ALL;
   }
 
-  if (game_status == GAME_MODE_EDITOR)
+  if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
     door_state |= DOOR_NO_DELAY;
 
   if (door_state & DOOR_ACTION)
@@ -4847,9 +4918,23 @@ unsigned int MoveDoor(unsigned int door_state)
     {
       /* opening door sound has priority over simultaneously closing door */
       if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
+      {
         PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
+
+       if (door_state & DOOR_OPEN_1)
+         PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
+       if (door_state & DOOR_OPEN_2)
+         PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
+      }
       else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
+      {
         PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
+
+       if (door_state & DOOR_CLOSE_1)
+         PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
+       if (door_state & DOOR_CLOSE_2)
+         PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
+      }
     }
 
     for (k = start; k < num_move_steps; k++)
@@ -5026,6 +5111,17 @@ unsigned int MoveDoor(unsigned int door_state)
       if (door_part_done_all)
        break;
     }
+
+    if (!(door_state & DOOR_NO_DELAY))
+    {
+      /* wait for specified door action post delay */
+      if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
+       Delay(MAX(door_1.post_delay, door_2.post_delay));
+      else if (door_state & DOOR_ACTION_1)
+       Delay(door_1.post_delay);
+      else if (door_state & DOOR_ACTION_2)
+       Delay(door_2.post_delay);
+    }
   }
 
   if (door_state & DOOR_ACTION_1)
@@ -7394,6 +7490,27 @@ int map_element_MM_to_RND(int element_mm)
          EL_EMPTY);
 }
 
+int map_action_MM_to_RND(int action_mm)
+{
+  /* all MM actions are defined to exactly match their RND counterparts */
+  return action_mm;
+}
+
+int map_sound_MM_to_RND(int sound_mm)
+{
+  switch (sound_mm)
+  {
+    case SND_MM_GAME_LEVELTIME_CHARGING:
+      return SND_GAME_LEVELTIME_CHARGING;
+
+    case SND_MM_GAME_HEALTH_CHARGING:
+      return SND_GAME_HEALTH_CHARGING;
+
+    default:
+      return SND_UNDEFINED;
+  }
+}
+
 int map_mm_wall_element(int element)
 {
   return (element >= EL_MM_STEEL_WALL_START &&
@@ -8912,6 +9029,31 @@ void ResetFontStatus()
   SetFontStatus(-1);
 }
 
+boolean CheckIfPlayfieldViewportHasChanged()
+{
+  // if game status has not changed, playfield viewport has not changed either
+  if (game_status == game_status_last)
+    return FALSE;
+
+  // check if playfield viewport has changed with current game status
+  struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
+  int new_real_sx      = vp_playfield->x;
+  int new_real_sy      = vp_playfield->y;
+  int new_full_sxsize  = vp_playfield->width;
+  int new_full_sysize  = vp_playfield->height;
+
+  return (new_real_sx != REAL_SX ||
+         new_real_sy != REAL_SY ||
+         new_full_sxsize != FULL_SXSIZE ||
+         new_full_sysize != FULL_SYSIZE);
+}
+
+boolean CheckIfGlobalBorderOrPlayfieldViewportHasChanged()
+{
+  return (CheckIfGlobalBorderHasChanged() ||
+         CheckIfPlayfieldViewportHasChanged());
+}
+
 void ChangeViewportPropertiesIfNeeded()
 {
   boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?