+void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
+ int tilesize)
+{
+ DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
+ graphic, frame, tilesize);
+ MarkTileDirty(x / tilesize, y / tilesize);
+}
+
+void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
+ int tilesize)
+{
+ Bitmap *src_bitmap;
+ int src_x, src_y;
+
+ getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
+ BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
+}
+
+void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
+ int frame, int tilesize)
+{
+ Bitmap *src_bitmap;
+ int src_x, src_y;
+
+ getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
+ BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
+}
+
+void DrawMiniGraphic(int x, int y, int graphic)
+{
+ DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX, SY + y * MINI_TILEY, graphic);
+ MarkTileDirty(x / 2, y / 2);
+}
+
+void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
+{
+ Bitmap *src_bitmap;
+ int src_x, src_y;
+
+ getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
+ BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
+}
+
+static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
+ int graphic, int frame,
+ int cut_mode, int mask_mode)
+{
+ Bitmap *src_bitmap;
+ int src_x, src_y;
+ int dst_x, dst_y;
+ int width = TILEX, height = TILEY;
+ int cx = 0, cy = 0;
+
+ if (dx || dy) // shifted graphic
+ {
+ if (x < BX1) // object enters playfield from the left
+ {
+ x = BX1;
+ width = dx;
+ cx = TILEX - dx;
+ dx = 0;
+ }
+ else if (x > BX2) // object enters playfield from the right
+ {
+ x = BX2;
+ width = -dx;
+ dx = TILEX + dx;
+ }
+ else if (x == BX1 && dx < 0) // object leaves playfield to the left
+ {
+ width += dx;
+ cx = -dx;
+ dx = 0;
+ }
+ else if (x == BX2 && dx > 0) // object leaves playfield to the right
+ width -= dx;
+ else if (dx) // general horizontal movement
+ MarkTileDirty(x + SIGN(dx), y);
+
+ if (y < BY1) // object enters playfield from the top
+ {
+ if (cut_mode == CUT_BELOW) // object completely above top border
+ return;
+
+ y = BY1;
+ height = dy;
+ cy = TILEY - dy;
+ dy = 0;
+ }
+ else if (y > BY2) // object enters playfield from the bottom
+ {
+ y = BY2;
+ height = -dy;
+ dy = TILEY + dy;
+ }
+ else if (y == BY1 && dy < 0) // object leaves playfield to the top
+ {
+ height += dy;
+ cy = -dy;
+ dy = 0;
+ }
+ else if (dy > 0 && cut_mode == CUT_ABOVE)
+ {
+ if (y == BY2) // object completely above bottom border
+ return;
+
+ height = dy;
+ cy = TILEY - dy;
+ dy = TILEY;
+ MarkTileDirty(x, y + 1);
+ } // object leaves playfield to the bottom
+ else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
+ height -= dy;
+ else if (dy) // general vertical movement
+ MarkTileDirty(x, y + SIGN(dy));
+ }
+
+#if DEBUG
+ if (!IN_SCR_FIELD(x, y))
+ {
+ Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
+ x, y, graphic);
+ Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
+
+ return;
+ }
+#endif
+
+ width = width * TILESIZE_VAR / TILESIZE;
+ height = height * TILESIZE_VAR / TILESIZE;
+ cx = cx * TILESIZE_VAR / TILESIZE;
+ cy = cy * TILESIZE_VAR / TILESIZE;
+ dx = dx * TILESIZE_VAR / TILESIZE;
+ dy = dy * TILESIZE_VAR / TILESIZE;
+
+ if (width > 0 && height > 0)
+ {
+ getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
+
+ src_x += cx;
+ src_y += cy;
+
+ dst_x = FX + x * TILEX_VAR + dx;
+ dst_y = FY + y * TILEY_VAR + dy;
+
+ if (mask_mode == USE_MASKING)
+ 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);
+
+ MarkTileDirty(x, y);
+ }
+}
+
+static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
+ int graphic, int frame,
+ int cut_mode, int mask_mode)
+{
+ Bitmap *src_bitmap;
+ int src_x, src_y;
+ int dst_x, dst_y;
+ int width = TILEX_VAR, height = TILEY_VAR;
+ int x1 = x;
+ int y1 = y;
+ int x2 = x + SIGN(dx);
+ int y2 = y + SIGN(dy);
+
+ // movement with two-tile animations must be sync'ed with movement position,
+ // not with current GfxFrame (which can be higher when using slow movement)
+ int anim_pos = (dx ? ABS(dx) : ABS(dy));
+ int anim_frames = graphic_info[graphic].anim_frames;
+
+ // (we also need anim_delay here for movement animations with less frames)
+ int anim_delay = graphic_info[graphic].anim_delay;
+ int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
+
+ boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
+ boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
+
+ // re-calculate animation frame for two-tile movement animation
+ frame = getGraphicAnimationFrame(graphic, sync_frame);
+
+ // check if movement start graphic inside screen area and should be drawn
+ if (draw_start_tile && IN_SCR_FIELD(x1, y1))
+ {
+ getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
+
+ dst_x = FX + x1 * TILEX_VAR;
+ dst_y = FY + y1 * TILEY_VAR;
+
+ if (mask_mode == USE_MASKING)
+ 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);
+
+ MarkTileDirty(x1, y1);
+ }
+
+ // check if movement end graphic inside screen area and should be drawn
+ if (draw_end_tile && IN_SCR_FIELD(x2, y2))
+ {
+ getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
+
+ dst_x = FX + x2 * TILEX_VAR;
+ dst_y = FY + y2 * TILEY_VAR;
+
+ if (mask_mode == USE_MASKING)
+ 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);
+
+ MarkTileDirty(x2, y2);
+ }
+}
+
+static void DrawGraphicShifted(int x, int y, int dx, int dy,
+ int graphic, int frame,
+ int cut_mode, int mask_mode)
+{
+ if (graphic < 0)
+ {
+ DrawGraphic(x, y, graphic, frame);
+
+ return;
+ }
+
+ if (graphic_info[graphic].double_movement) // EM style movement images
+ DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
+ else
+ DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
+}
+
+static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
+ int graphic, int frame, int cut_mode)
+{
+ DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
+}
+
+void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
+ int cut_mode, int mask_mode)
+{
+ int lx = LEVELX(x), ly = LEVELY(y);
+ int graphic;
+ int frame;
+
+ if (IN_LEV_FIELD(lx, ly))
+ {
+ if (element == EL_EMPTY)
+ element = GfxElementEmpty[lx][ly];
+
+ SetRandomAnimationValue(lx, ly);
+
+ graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
+ frame = getGraphicAnimationFrameXY(graphic, lx, ly);
+
+ // do not use double (EM style) movement graphic when not moving
+ if (graphic_info[graphic].double_movement && !dx && !dy)
+ {
+ graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
+ frame = getGraphicAnimationFrameXY(graphic, lx, ly);
+ }
+
+ if (game.use_masked_elements && (dx || dy))
+ mask_mode = USE_MASKING;
+ }
+ else // border element
+ {
+ graphic = el2img(element);
+ frame = getGraphicAnimationFrameXY(graphic, lx, ly);
+ }
+
+ if (element == EL_EXPANDABLE_WALL)
+ {
+ boolean left_stopped = FALSE, right_stopped = FALSE;
+
+ if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
+ left_stopped = TRUE;
+ if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
+ right_stopped = TRUE;
+
+ if (left_stopped && right_stopped)
+ graphic = IMG_WALL;
+ else if (left_stopped)
+ {
+ graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
+ frame = graphic_info[graphic].anim_frames - 1;
+ }
+ else if (right_stopped)
+ {
+ graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
+ frame = graphic_info[graphic].anim_frames - 1;
+ }
+ }
+
+ if (dx || dy)
+ DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
+ else if (mask_mode == USE_MASKING)
+ DrawGraphicThruMask(x, y, graphic, frame);
+ else
+ DrawGraphic(x, y, graphic, frame);
+}
+
+void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
+ int cut_mode, int mask_mode)
+{
+ if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+ DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
+ cut_mode, mask_mode);
+}
+
+void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
+ int cut_mode)
+{
+ DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
+}
+
+void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
+ int cut_mode)
+{
+ DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
+}
+
+void DrawLevelElementThruMask(int x, int y, int element)
+{
+ DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
+}
+
+void DrawLevelFieldThruMask(int x, int y)
+{
+ DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
+}
+
+// !!! implementation of quicksand is totally broken !!!
+#define IS_CRUMBLED_TILE(x, y, e) \
+ (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
+ !IS_MOVING(x, y) || \
+ (e) == EL_QUICKSAND_EMPTYING || \
+ (e) == EL_QUICKSAND_FAST_EMPTYING))
+
+static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
+ int graphic)
+{
+ Bitmap *src_bitmap;
+ int src_x, src_y;
+ int width, height, cx, cy;
+ int sx = SCREENX(x), sy = SCREENY(y);
+ int crumbled_border_size = graphic_info[graphic].border_size;
+ int crumbled_tile_size = graphic_info[graphic].tile_size;
+ int crumbled_border_size_var =
+ crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
+ int i;
+
+ getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
+
+ for (i = 1; i < 4; i++)
+ {
+ int dxx = (i & 1 ? dx : 0);
+ int dyy = (i & 2 ? dy : 0);
+ int xx = x + dxx;
+ int yy = y + dyy;
+ int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
+ BorderElement);
+
+ // check if neighbour field is of same crumble type
+ boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
+ graphic_info[graphic].class ==
+ graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
+
+ // return if check prevents inner corner
+ if (same == (dxx == dx && dyy == dy))
+ return;
+ }
+
+ // if we reach this point, we have an inner corner
+
+ getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
+
+ width = crumbled_border_size_var;
+ height = crumbled_border_size_var;
+ cx = (dx > 0 ? TILESIZE_VAR - width : 0);
+ cy = (dy > 0 ? TILESIZE_VAR - height : 0);
+
+ if (game.use_masked_elements)
+ {
+ int graphic0 = el2img(EL_EMPTY);
+ int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
+ Bitmap *src_bitmap0;
+ int src_x0, src_y0;
+
+ getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
+
+ BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
+ width, height,
+ FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
+
+ BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
+ width, height,
+ FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
+ }
+ else
+ BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
+ width, height,
+ FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
+}
+
+static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
+ int dir)
+{
+ Bitmap *src_bitmap;
+ int src_x, src_y;
+ int width, height, bx, by, cx, cy;
+ int sx = SCREENX(x), sy = SCREENY(y);
+ int crumbled_border_size = graphic_info[graphic].border_size;
+ int crumbled_tile_size = graphic_info[graphic].tile_size;
+ int crumbled_border_size_var =
+ crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
+ int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
+ int i;
+
+ getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
+
+ // only needed when using masked elements
+ int graphic0 = el2img(EL_EMPTY);
+ int frame0 = getGraphicAnimationFrameXY(graphic0, x, y);
+ Bitmap *src_bitmap0;
+ int src_x0, src_y0;
+
+ if (game.use_masked_elements)
+ getGraphicSource(graphic0, frame0, &src_bitmap0, &src_x0, &src_y0);
+
+ // draw simple, sloppy, non-corner-accurate crumbled border
+
+ width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
+ height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
+ cx = (dir == 2 ? crumbled_border_pos_var : 0);
+ cy = (dir == 3 ? crumbled_border_pos_var : 0);
+
+ if (game.use_masked_elements)
+ {
+ BlitBitmap(src_bitmap0, drawto_field, src_x0 + cx, src_y0 + cy,
+ width, height,
+ FX + sx * TILEX_VAR + cx,
+ FY + sy * TILEY_VAR + cy);
+
+ BlitBitmapMasked(src_bitmap, drawto_field, src_x + cx, src_y + cy,
+ width, height,
+ FX + sx * TILEX_VAR + cx,
+ FY + sy * TILEY_VAR + cy);
+ }
+ else
+ BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
+ width, height,
+ FX + sx * TILEX_VAR + cx,
+ FY + sy * TILEY_VAR + cy);
+
+ // (remaining middle border part must be at least as big as corner part)
+ if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
+ crumbled_border_size_var >= TILESIZE_VAR / 3)
+ return;
+
+ // correct corners of crumbled border, if needed
+
+ for (i = -1; i <= 1; i += 2)
+ {
+ int xx = x + (dir == 0 || dir == 3 ? i : 0);
+ int yy = y + (dir == 1 || dir == 2 ? i : 0);
+ int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
+ BorderElement);
+
+ // check if neighbour field is of same crumble type
+ if (IS_CRUMBLED_TILE(xx, yy, element) &&
+ graphic_info[graphic].class ==
+ graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
+ {
+ // no crumbled corner, but continued crumbled border
+
+ int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
+ int c2 = (i == 1 ? crumbled_border_pos_var : 0);
+ int b1 = (i == 1 ? crumbled_border_size_var :
+ TILESIZE_VAR - 2 * crumbled_border_size_var);
+
+ width = crumbled_border_size_var;
+ height = crumbled_border_size_var;
+
+ if (dir == 1 || dir == 2)
+ {
+ cx = c1;
+ cy = c2;
+ bx = cx;
+ by = b1;
+ }
+ else
+ {
+ cx = c2;
+ cy = c1;
+ bx = b1;
+ by = cy;
+ }
+
+ if (game.use_masked_elements)
+ {
+ BlitBitmap(src_bitmap0, drawto_field, src_x0 + bx, src_y0 + by,
+ width, height,
+ FX + sx * TILEX_VAR + cx,
+ FY + sy * TILEY_VAR + cy);
+
+ BlitBitmapMasked(src_bitmap, drawto_field, src_x + bx, src_y + by,
+ width, height,
+ FX + sx * TILEX_VAR + cx,
+ FY + sy * TILEY_VAR + cy);
+ }
+ else
+ BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
+ width, height,
+ FX + sx * TILEX_VAR + cx,
+ FY + sy * TILEY_VAR + cy);
+ }
+ }
+}
+
+static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
+{
+ int sx = SCREENX(x), sy = SCREENY(y);
+ int element;
+ int i;
+ struct XY *xy = xy_topdown;
+
+ if (!IN_LEV_FIELD(x, y))
+ return;
+
+ element = TILE_GFX_ELEMENT(x, y);
+
+ if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
+ {
+ if (!IN_SCR_FIELD(sx, sy))
+ return;
+
+ // crumble field borders towards direct neighbour fields
+ for (i = 0; i < 4; i++)
+ {
+ int xx = x + xy[i].x;
+ int yy = y + xy[i].y;
+
+ element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
+ BorderElement);
+
+ // check if neighbour field is of same crumble type
+ if (IS_CRUMBLED_TILE(xx, yy, element) &&
+ graphic_info[graphic].class ==
+ graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
+ continue;
+
+ DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
+ }
+
+ // crumble inner field corners towards corner neighbour fields
+ if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
+ graphic_info[graphic].anim_frames == 2)
+ {
+ for (i = 0; i < 4; i++)
+ {
+ int dx = (i & 1 ? +1 : -1);
+ int dy = (i & 2 ? +1 : -1);
+
+ DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
+ }
+ }
+
+ MarkTileDirty(sx, sy);
+ }
+ else // center field is not crumbled -- crumble neighbour fields
+ {
+ // crumble field borders of direct neighbour fields
+ for (i = 0; i < 4; i++)
+ {
+ int xx = x + xy[i].x;
+ int yy = y + xy[i].y;
+ int sxx = sx + xy[i].x;
+ int syy = sy + xy[i].y;
+
+ if (!IN_LEV_FIELD(xx, yy) ||
+ !IN_SCR_FIELD(sxx, syy))
+ continue;
+
+ // do not crumble fields that are being digged or snapped
+ if (Tile[xx][yy] == EL_EMPTY ||
+ Tile[xx][yy] == EL_ELEMENT_SNAPPING)
+ continue;
+
+ element = TILE_GFX_ELEMENT(xx, yy);
+
+ if (!IS_CRUMBLED_TILE(xx, yy, element))
+ continue;
+
+ graphic = el_act2crm(element, ACTION_DEFAULT);
+
+ DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
+
+ MarkTileDirty(sxx, syy);
+ }
+
+ // crumble inner field corners of corner neighbour fields
+ for (i = 0; i < 4; i++)
+ {
+ int dx = (i & 1 ? +1 : -1);
+ int dy = (i & 2 ? +1 : -1);
+ int xx = x + dx;
+ int yy = y + dy;
+ int sxx = sx + dx;
+ int syy = sy + dy;
+
+ if (!IN_LEV_FIELD(xx, yy) ||
+ !IN_SCR_FIELD(sxx, syy))
+ continue;
+
+ if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
+ continue;
+
+ element = TILE_GFX_ELEMENT(xx, yy);
+
+ if (!IS_CRUMBLED_TILE(xx, yy, element))
+ continue;
+
+ graphic = el_act2crm(element, ACTION_DEFAULT);
+
+ if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
+ graphic_info[graphic].anim_frames == 2)
+ DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
+
+ MarkTileDirty(sxx, syy);
+ }
+ }
+}
+
+void DrawLevelFieldCrumbled(int x, int y)
+{
+ int graphic;
+
+ if (!IN_LEV_FIELD(x, y))
+ return;
+
+ if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
+ GfxElement[x][y] != EL_UNDEFINED &&
+ GFX_CRUMBLED(GfxElement[x][y]))
+ {
+ DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
+
+ return;
+ }
+
+ graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
+
+ DrawLevelFieldCrumbledExt(x, y, graphic, 0);
+}
+
+void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
+ int step_frame)
+{
+ int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
+ int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
+ int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
+ int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
+ int sx = SCREENX(x), sy = SCREENY(y);
+
+ DrawScreenGraphic(sx, sy, graphic1, frame1);
+ DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
+}
+
+void DrawLevelFieldCrumbledNeighbours(int x, int y)
+{
+ int sx = SCREENX(x), sy = SCREENY(y);
+ struct XY *xy = xy_topdown;
+ int i;
+
+ // crumble direct neighbour fields (required for field borders)
+ for (i = 0; i < 4; i++)
+ {
+ int xx = x + xy[i].x;
+ int yy = y + xy[i].y;
+ int sxx = sx + xy[i].x;
+ int syy = sy + xy[i].y;
+
+ if (!IN_LEV_FIELD(xx, yy) ||
+ !IN_SCR_FIELD(sxx, syy) ||
+ !GFX_CRUMBLED(Tile[xx][yy]) ||
+ IS_MOVING(xx, yy))
+ continue;
+
+ DrawLevelField(xx, yy);
+ }
+
+ // crumble corner neighbour fields (required for inner field corners)
+ for (i = 0; i < 4; i++)
+ {
+ int dx = (i & 1 ? +1 : -1);
+ int dy = (i & 2 ? +1 : -1);
+ int xx = x + dx;
+ int yy = y + dy;
+ int sxx = sx + dx;
+ int syy = sy + dy;
+
+ if (!IN_LEV_FIELD(xx, yy) ||
+ !IN_SCR_FIELD(sxx, syy) ||
+ !GFX_CRUMBLED(Tile[xx][yy]) ||
+ IS_MOVING(xx, yy))
+ continue;
+
+ int element = TILE_GFX_ELEMENT(xx, yy);
+ int graphic = el_act2crm(element, ACTION_DEFAULT);
+
+ if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
+ graphic_info[graphic].anim_frames == 2)
+ DrawLevelField(xx, yy);
+ }
+}
+
+static int getBorderElement(int x, int y)
+{
+ int border[7][2] =
+ {
+ { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
+ { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
+ { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
+ { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
+ { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
+ { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
+ { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
+ };
+ int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
+ int steel_position = (x == -1 && y == -1 ? 0 :
+ x == lev_fieldx && y == -1 ? 1 :
+ x == -1 && y == lev_fieldy ? 2 :
+ x == lev_fieldx && y == lev_fieldy ? 3 :
+ x == -1 || x == lev_fieldx ? 4 :
+ y == -1 || y == lev_fieldy ? 5 : 6);
+
+ return border[steel_position][steel_type];
+}
+
+void DrawScreenGraphic(int x, int y, int graphic, int frame)
+{
+ if (game.use_masked_elements)
+ {
+ if (graphic != el2img(EL_EMPTY))
+ DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
+
+ DrawGraphicThruMask(x, y, graphic, frame);
+ }
+ else
+ {
+ DrawGraphic(x, y, graphic, frame);
+ }
+}
+
+void DrawLevelGraphic(int x, int y, int graphic, int frame)
+{
+ DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
+}
+
+void DrawScreenElement(int x, int y, int element)
+{
+ int mask_mode = NO_MASKING;
+
+ if (game.use_masked_elements)
+ {
+ int lx = LEVELX(x), ly = LEVELY(y);
+
+ if (IN_LEV_FIELD(lx, ly) && element != EL_EMPTY)
+ {
+ DrawScreenElementExt(x, y, 0, 0, EL_EMPTY, NO_CUTTING, NO_MASKING);
+
+ mask_mode = USE_MASKING;
+ }
+ }
+
+ DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, mask_mode);
+ DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
+}
+
+void DrawLevelElement(int x, int y, int element)
+{
+ if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
+ DrawScreenElement(SCREENX(x), SCREENY(y), element);
+}
+
+void DrawScreenField(int x, int y)
+{
+ int lx = LEVELX(x), ly = LEVELY(y);
+ int element, content;
+
+ if (!IN_LEV_FIELD(lx, ly))
+ {
+ if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
+ element = EL_EMPTY;
+ else
+ element = getBorderElement(lx, ly);
+
+ DrawScreenElement(x, y, element);
+
+ return;
+ }
+
+ element = Tile[lx][ly];
+ content = Store[lx][ly];
+
+ if (IS_MOVING(lx, ly))
+ {
+ int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
+ boolean cut_mode = NO_CUTTING;
+
+ if (element == EL_QUICKSAND_EMPTYING ||
+ element == EL_QUICKSAND_FAST_EMPTYING ||
+ element == EL_MAGIC_WALL_EMPTYING ||
+ element == EL_BD_MAGIC_WALL_EMPTYING ||
+ element == EL_DC_MAGIC_WALL_EMPTYING ||
+ element == EL_AMOEBA_DROPPING)
+ cut_mode = CUT_ABOVE;
+ else if (element == EL_QUICKSAND_FILLING ||
+ element == EL_QUICKSAND_FAST_FILLING ||
+ element == EL_MAGIC_WALL_FILLING ||
+ element == EL_BD_MAGIC_WALL_FILLING ||
+ element == EL_DC_MAGIC_WALL_FILLING)
+ cut_mode = CUT_BELOW;
+
+ if (cut_mode == CUT_ABOVE)
+ DrawScreenElement(x, y, element);
+ else
+ DrawScreenElement(x, y, EL_EMPTY);
+
+ if (cut_mode != CUT_BELOW && game.use_masked_elements)
+ {
+ int dir = MovDir[lx][ly];
+ int newx = x + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
+ int newy = y + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
+
+ if (IN_SCR_FIELD(newx, newy))
+ DrawScreenElement(newx, newy, EL_EMPTY);
+ }
+
+ if (horiz_move)
+ DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
+ else if (cut_mode == NO_CUTTING)
+ DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
+ else
+ {
+ DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
+
+ if (cut_mode == CUT_BELOW &&
+ IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
+ DrawLevelElement(lx, ly + 1, element);
+ }
+
+ if (content == EL_ACID)
+ {
+ int dir = MovDir[lx][ly];
+ int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
+ int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
+
+ DrawLevelElementThruMask(newlx, newly, EL_ACID);
+
+ // prevent target field from being drawn again (but without masking)
+ // (this would happen if target field is scanned after moving element)
+ Stop[newlx][newly] = TRUE;
+ }
+ }
+ else if (IS_BLOCKED(lx, ly))
+ {
+ int oldx, oldy;
+ int sx, sy;
+ int horiz_move;
+ boolean cut_mode = NO_CUTTING;
+ int element_old, content_old;
+
+ Blocked2Moving(lx, ly, &oldx, &oldy);
+ sx = SCREENX(oldx);
+ sy = SCREENY(oldy);
+ horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
+ MovDir[oldx][oldy] == MV_RIGHT);
+
+ element_old = Tile[oldx][oldy];
+ content_old = Store[oldx][oldy];
+
+ if (element_old == EL_QUICKSAND_EMPTYING ||
+ element_old == EL_QUICKSAND_FAST_EMPTYING ||
+ element_old == EL_MAGIC_WALL_EMPTYING ||
+ element_old == EL_BD_MAGIC_WALL_EMPTYING ||
+ element_old == EL_DC_MAGIC_WALL_EMPTYING ||
+ element_old == EL_AMOEBA_DROPPING)
+ cut_mode = CUT_ABOVE;
+
+ DrawScreenElement(x, y, EL_EMPTY);
+
+ if (horiz_move)