added optional button to restart game (door, panel and touch variants)
[rocksndiamonds.git] / src / game_mm / mm_game.c
index 5c5619d0e8e54fc12f51f7ffa9342420323686aa..b258e374b7f0bbeb868be9b873e831e331394af0 100644 (file)
@@ -981,6 +981,11 @@ static int getMaskFromElement(int element)
     return MM_MASK_CIRCLE;
 }
 
+static int getPixelFromMask(int pos, int dx, int dy)
+{
+  return (mm_masks[pos][dy / 2][dx / 2] == 'X' ? 1 : 0);
+}
+
 static int getLevelFromLaserX(int x)
 {
   return x / TILEX - (x < 0 ? 1 : 0);          // correct negative values
@@ -1064,7 +1069,7 @@ static int ScanPixel(void)
        {
          int pos = getMaskFromElement(element);
 
-         pixel = (mm_masks[pos][dy / 2][dx / 2] == 'X' ? 1 : 0);
+         pixel = getPixelFromMask(pos, dx, dy);
        }
       }
       else
@@ -1209,14 +1214,10 @@ static void ScanLaser(void)
       break;
     }
 
-    boolean diagonally_adjacent_hit = FALSE;
-
     // handle special case of laser hitting two diagonally adjacent elements
     // (with or without a third corner element behind these two elements)
     if ((diag_1 || diag_2) && cross_x && cross_y)
     {
-      diagonally_adjacent_hit = TRUE;
-
       // compare the two diagonally adjacent elements
       int xoffset = 2;
       int yoffset = 2 * (diag_1 ? -1 : +1);
@@ -1332,29 +1333,6 @@ static void ScanLaser(void)
          break;
       }
     }
-    else if (IS_DF_SLOPE(element))
-    {
-      if (diagonally_adjacent_hit)
-      {
-       laser.overloaded = TRUE;
-
-       break;
-      }
-
-      if (hit_mask == HIT_MASK_LEFT ||
-         hit_mask == HIT_MASK_RIGHT ||
-         hit_mask == HIT_MASK_TOP ||
-         hit_mask == HIT_MASK_BOTTOM)
-      {
-       if (HitReflectingWalls(element, hit_mask))
-         break;
-      }
-      else
-      {
-       if (HitElement(element, hit_mask))
-         break;
-      }
-    }
     else
     {
       if (HitElement(element, hit_mask))
@@ -1711,16 +1689,64 @@ static boolean HitElement(int element, int hit_mask)
 {
   if (IS_DF_SLOPE(element))
   {
-    int mirrored_angle = get_mirrored_angle(laser.current_angle,
-                                           get_element_angle(element));
+    // check if laser scan has crossed element boundaries (not just mini tiles)
+    boolean cross_x = (getLevelFromLaserX(LX) != getLevelFromLaserX(LX + 2));
+    boolean cross_y = (getLevelFromLaserY(LY) != getLevelFromLaserY(LY + 2));
+    int element_angle = get_element_angle(element);
+    int mirrored_angle = get_mirrored_angle(laser.current_angle, element_angle);
     int opposite_angle = get_opposite_angle(laser.current_angle);
 
+    // check if wall (horizontal or vertical) side of slope was hit
+    if (hit_mask == HIT_MASK_LEFT ||
+       hit_mask == HIT_MASK_RIGHT ||
+       hit_mask == HIT_MASK_TOP ||
+       hit_mask == HIT_MASK_BOTTOM)
+    {
+      boolean hit_slope_corner_in_laser_direction =
+       ((hit_mask == HIT_MASK_LEFT   && (element == EL_DF_SLOPE_01 ||
+                                         element == EL_DF_SLOPE_02)) ||
+        (hit_mask == HIT_MASK_RIGHT  && (element == EL_DF_SLOPE_00 ||
+                                         element == EL_DF_SLOPE_03)) ||
+        (hit_mask == HIT_MASK_TOP    && (element == EL_DF_SLOPE_02 ||
+                                         element == EL_DF_SLOPE_03)) ||
+        (hit_mask == HIT_MASK_BOTTOM && (element == EL_DF_SLOPE_00 ||
+                                         element == EL_DF_SLOPE_01)));
+
+      boolean hit_slope_corner_in_laser_direction_double_checked =
+       (cross_x && cross_y &&
+        laser.current_angle == mirrored_angle &&
+        hit_slope_corner_in_laser_direction);
+
+      // check special case of laser hitting the corner of a slope and another
+      // element (either wall or another slope), following the diagonal side
+      // of the slope which has the same angle as the direction of the laser
+      if (!hit_slope_corner_in_laser_direction_double_checked)
+       return HitReflectingWalls(element, hit_mask);
+    }
+
+    // check if an edge was hit while crossing element borders
+    if (cross_x && cross_y && get_number_of_bits(hit_mask) == 1)
+    {
+      // check both sides of potentially diagonal side of slope
+      int dx1 = (LX + XS) % TILEX;
+      int dy1 = (LY + YS) % TILEY;
+      int dx2 = (LX + XS + 2) % TILEX;
+      int dy2 = (LY + YS + 2) % TILEY;
+      int pos = getMaskFromElement(element);
+
+      // check if we are entering empty space area after hitting edge
+      if (!getPixelFromMask(pos, dx1, dy1) &&
+         !getPixelFromMask(pos, dx2, dy2))
+      {
+       // we already know that we hit an edge, but use this function to go on
+       if (HitOnlyAnEdge(hit_mask))
+         return FALSE;
+      }
+    }
+
     // check if laser is reflected by slope by 180°
     if (mirrored_angle == opposite_angle)
     {
-      LX += XS;
-      LY += YS;
-
       AddDamagedField(LX / TILEX, LY / TILEY);
 
       laser.overloaded = TRUE;
@@ -1901,13 +1927,27 @@ static boolean HitElement(int element, int hit_mask)
     XS = 2 * Step[laser.current_angle].x;
     YS = 2 * Step[laser.current_angle].y;
 
-    if (!IS_22_5_ANGLE(laser.current_angle))   // 90° or 45° angle
-      step_size = 8;
-    else
-      step_size = 4;
+    if (through_center)
+    {
+      // start from center position for all game elements but slope
+      if (!IS_22_5_ANGLE(laser.current_angle)) // 90° or 45° angle
+       step_size = 8;
+      else
+       step_size = 4;
 
-    LX += step_size * XS;
-    LY += step_size * YS;
+      LX += step_size * XS;
+      LY += step_size * YS;
+    }
+    else
+    {
+      // advance laser position until reaching the next tile (slopes)
+      while (LX / TILEX == ELX && (LX + 2) / TILEX == ELX &&
+            LY / TILEY == ELY && (LY + 2) / TILEY == ELY)
+      {
+       LX += XS;
+       LY += YS;
+      }
+    }
 
     // draw sparkles on mirror
     if ((IS_MIRROR(element) ||
@@ -1934,8 +1974,8 @@ static boolean HitElement(int element, int hit_mask)
       {
        int elx, ely;
 
-       elx = getLevelFromLaserX(LX);
-       ely = getLevelFromLaserY(LY);
+       elx = getLevelFromLaserX(LX + XS);
+       ely = getLevelFromLaserY(LY + YS);
 
        if (IN_LEV_FIELD(elx, ely))
        {
@@ -1948,8 +1988,8 @@ static boolean HitElement(int element, int hit_mask)
 
        int nr = element - EL_DF_SLOPE_START;
        int dx = (nr == 0 ? (XS > 0 ? TILEX - 1 : -1) :
-                 nr == 1 ? (XS > 0 ? TILEX     :  1) :
-                 nr == 2 ? (XS > 0 ? TILEX     :  1) :
+                 nr == 1 ? (XS > 0 ? TILEX     :  0) :
+                 nr == 2 ? (XS > 0 ? TILEX     :  0) :
                  nr == 3 ? (XS > 0 ? TILEX - 1 : -1) : 0);
        int dy = (nr == 0 ? (YS > 0 ? TILEY - 1 : -1) :
                  nr == 1 ? (YS > 0 ? TILEY - 1 : -1) :
@@ -1981,7 +2021,7 @@ static boolean HitElement(int element, int hit_mask)
          {
            int pos = getMaskFromElement(element_side);
 
-           if (mm_masks[pos][dx / 2][dy / 2] == 'X')
+           if (getPixelFromMask(pos, dx, dy))
              laser.overloaded = TRUE;
          }
        }