fixed using wall tiles as gray ball content elements in MM engine
[rocksndiamonds.git] / src / game_mm / mm_game.c
index 267f4c5e106dc3b96ef89e4cedf1a0768910fc3a..4ad6ea61a0f85bc393e5a5647391c61058a38d9d 100644 (file)
@@ -463,7 +463,7 @@ static void InitMovDir_MM(int x, int y)
   }
 }
 
-static void InitField(int x, int y)
+static void InitField(int x, int y, boolean init_game)
 {
   int element = Tile[x][y];
 
@@ -475,7 +475,7 @@ static void InitField(int x, int y)
 
     case EL_KETTLE:
     case EL_CELL:
-      if (native_mm_level.auto_count_kettles)
+      if (init_game && native_mm_level.auto_count_kettles)
        game_mm.kettles_still_needed++;
       break;
 
@@ -535,9 +535,12 @@ static void InitField(int x, int y)
       }
       else if (IS_MCDUFFIN(element) || IS_LASER(element))
       {
-       laser.start_edge.x = x;
-       laser.start_edge.y = y;
-       laser.start_angle = get_element_angle(element);
+       if (init_game)
+       {
+         laser.start_edge.x = x;
+         laser.start_edge.y = y;
+         laser.start_angle = get_element_angle(element);
+       }
 
         if (IS_MCDUFFIN(element))
         {
@@ -699,7 +702,7 @@ void InitGameEngine_MM(void)
       Store[x][y] = Store2[x][y] = 0;
       Stop[x][y] = FALSE;
 
-      InitField(x, y);
+      InitField(x, y, TRUE);
     }
   }
 
@@ -1493,6 +1496,8 @@ boolean HitElement(int element, int hit_mask)
   // this is more precise: check if laser would go through the center
   if ((ELX * TILEX + 14 - LX) * YS != (ELY * TILEY + 14 - LY) * XS)
   {
+    int skip_count = 0;
+
     // prevent cutting through laser emitter with laser beam
     if (IS_LASER(element))
       return TRUE;
@@ -1502,15 +1507,18 @@ boolean HitElement(int element, int hit_mask)
     {
       LX += XS;
       LY += YS;
+
+      skip_count++;
     }
     while (ELX == LX/TILEX && ELY == LY/TILEY && LX > 0 && LY > 0);
 
-    if (LX/TILEX > ELX || LY/TILEY > ELY)
+    if ((LX/TILEX > ELX || LY/TILEY > ELY) && skip_count > 1)
     {
       /* skipping scan positions to the right and down skips one scan
         position too much, because this is only the top left scan position
         of totally four scan positions (plus one to the right, one to the
         bottom and one to the bottom right) */
+      /* ... but only roll back scan position if more than one step done */
 
       LX -= XS;
       LY -= YS;
@@ -2455,7 +2463,20 @@ static void OpenSurpriseBall(int x, int y)
   int delay = 2;
 
   if (!MovDelay[x][y])         // next animation frame
+  {
+    if (IS_WALL(Store[x][y]))
+    {
+      DrawWalls_MM(x, y, Store[x][y]);
+
+      // copy wall tile to spare bitmap for "melting" animation
+      BlitBitmap(drawto, bitmap_db_field, cSX + x * TILEX, cSY + y * TILEY,
+                TILEX, TILEY, x * TILEX, y * TILEY);
+
+      DrawElement_MM(x, y, EL_BALL_GRAY);
+    }
+
     MovDelay[x][y] = 50 * delay;
+  }
 
   if (MovDelay[x][y])          // wait some time before next frame
   {
@@ -2464,11 +2485,22 @@ static void OpenSurpriseBall(int x, int y)
     if (!(MovDelay[x][y] % delay) && IN_SCR_FIELD(x, y))
     {
       Bitmap *bitmap;
-      int graphic = el2gfx(Store[x][y]);
       int gx, gy;
       int dx = RND(26), dy = RND(26);
 
-      getGraphicSource(graphic, 0, &bitmap, &gx, &gy);
+      if (IS_WALL(Store[x][y]))
+      {
+       // copy wall tile from spare bitmap for "melting" animation
+       bitmap = bitmap_db_field;
+       gx = x * TILEX;
+       gy = y * TILEY;
+      }
+      else
+      {
+       int graphic = el2gfx(Store[x][y]);
+
+       getGraphicSource(graphic, 0, &bitmap, &gx, &gy);
+      }
 
       BlitBitmap(bitmap, drawto, gx + dx, gy + dy, 6, 6,
                 cSX + x * TILEX + dx, cSY + y * TILEY + dy);
@@ -2480,11 +2512,24 @@ static void OpenSurpriseBall(int x, int y)
 
     if (!MovDelay[x][y])
     {
+      int i;
+
       Tile[x][y] = Store[x][y];
       Store[x][y] = Store2[x][y] = 0;
+      MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
 
+      InitField(x, y, FALSE);
       DrawField_MM(x, y);
 
+      for (i = (laser.num_damages > 0 ? laser.num_damages - 1 : 0); i >= 0; i--)
+       if (laser.damage[i].is_mirror)
+         break;
+
+      if (i > 0)
+       DrawLaser(laser.damage[i].edge - 1, DL_LASER_DISABLED);
+      else
+       DrawLaser(0, DL_LASER_DISABLED);
+
       ScanLaser();
     }
   }
@@ -2668,15 +2713,14 @@ static void Explode_MM(int x, int y, int phase, int mode)
       Tile[x][y] = center_element;
     }
 
-    if (center_element == EL_BOMB_ACTIVE || IS_MCDUFFIN(center_element))
-      Store[x][y] = center_element;
-    else
-      Store[x][y] = EL_EMPTY;
-
+    Store[x][y] = center_element;
     Store2[x][y] = mode;
 
     Tile[x][y] = EL_EXPLODING_OPAQUE;
-    GfxElement[x][y] = center_element;
+
+    GfxElement[x][y] = (center_element == EL_BOMB_ACTIVE ? EL_BOMB :
+                       IS_MCDUFFIN(center_element) ? EL_MCDUFFIN :
+                       center_element);
 
     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
 
@@ -2706,7 +2750,6 @@ static void Explode_MM(int x, int y, int phase, int mode)
       InitLaser();
 
       Bang_MM(laser.start_edge.x, laser.start_edge.y);
-      Store[x][y] = EL_EMPTY;
 
       GameOver_MM(GAME_OVER_DELAYED);
 
@@ -2714,16 +2757,15 @@ static void Explode_MM(int x, int y, int phase, int mode)
     }
     else if (IS_MCDUFFIN(Store[x][y]))
     {
-      Store[x][y] = EL_EMPTY;
-
       GameOver_MM(GAME_OVER_BOMB);
     }
 
-    Tile[x][y] = Store[x][y];
+    Tile[x][y] = EL_EMPTY;
+
     Store[x][y] = Store2[x][y] = 0;
     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
 
-    InitField(x, y);
+    InitField(x, y, FALSE);
     DrawField_MM(x, y);
   }
   else if (!(phase % delay) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
@@ -3492,10 +3534,19 @@ static void GameActions_MM_Ext(void)
       game_mm.ball_choice_pos++;
 
       int new_element = native_mm_level.ball_content[element_pos];
+      int new_element_unmapped = unmap_element(new_element);
 
-      // randomly rotate newly created game element, if needed
-      if (native_mm_level.rotate_ball_content)
+      if (IS_WALL(new_element_unmapped))
+      {
+       // always use completely filled wall element
+       new_element = new_element_unmapped | 0x000f;
+      }
+      else if (native_mm_level.rotate_ball_content &&
+              get_num_elements(new_element) > 1)
+      {
+       // randomly rotate newly created game element
        new_element = get_rotated_element(new_element, RND(16));
+      }
 
       Store[ELX][ELY] = new_element;
       Store2[ELX][ELY] = TRUE;