1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 /* select level set with EMC X11 graphics before activating EM GFX debugging */
27 #define DEBUG_EM_GFX FALSE
28 #define DEBUG_FRAME_TIME FALSE
30 /* tool button identifiers */
31 #define TOOL_CTRL_ID_YES 0
32 #define TOOL_CTRL_ID_NO 1
33 #define TOOL_CTRL_ID_CONFIRM 2
34 #define TOOL_CTRL_ID_PLAYER_1 3
35 #define TOOL_CTRL_ID_PLAYER_2 4
36 #define TOOL_CTRL_ID_PLAYER_3 5
37 #define TOOL_CTRL_ID_PLAYER_4 6
39 #define NUM_TOOL_BUTTONS 7
41 /* constants for number of doors and door parts */
43 #define NUM_PANELS NUM_DOORS
44 // #define NUM_PANELS 0
45 #define MAX_PARTS_PER_DOOR 8
46 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
47 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
50 struct DoorPartOrderInfo
56 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
58 struct DoorPartControlInfo
62 struct DoorPartPosInfo *pos;
65 static struct DoorPartControlInfo door_part_controls[] =
69 IMG_GFX_DOOR_1_PART_1,
74 IMG_GFX_DOOR_1_PART_2,
79 IMG_GFX_DOOR_1_PART_3,
84 IMG_GFX_DOOR_1_PART_4,
89 IMG_GFX_DOOR_1_PART_5,
94 IMG_GFX_DOOR_1_PART_6,
99 IMG_GFX_DOOR_1_PART_7,
104 IMG_GFX_DOOR_1_PART_8,
110 IMG_GFX_DOOR_2_PART_1,
115 IMG_GFX_DOOR_2_PART_2,
120 IMG_GFX_DOOR_2_PART_3,
125 IMG_GFX_DOOR_2_PART_4,
130 IMG_GFX_DOOR_2_PART_5,
135 IMG_GFX_DOOR_2_PART_6,
140 IMG_GFX_DOOR_2_PART_7,
145 IMG_GFX_DOOR_2_PART_8,
151 IMG_BACKGROUND_PANEL,
168 /* forward declaration for internal use */
169 static void UnmapToolButtons();
170 static void HandleToolButtons(struct GadgetInfo *);
171 static int el_act_dir2crm(int, int, int);
172 static int el_act2crm(int, int);
174 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
175 static int request_gadget_id = -1;
177 static char *print_if_not_empty(int element)
179 static char *s = NULL;
180 char *token_name = element_info[element].token_name;
185 s = checked_malloc(strlen(token_name) + 10 + 1);
187 if (element != EL_EMPTY)
188 sprintf(s, "%d\t['%s']", element, token_name);
190 sprintf(s, "%d", element);
195 int correctLevelPosX_EM(int lx)
198 lx -= (BorderElement != EL_EMPTY ? 1 : 0);
203 int correctLevelPosY_EM(int ly)
206 ly -= (BorderElement != EL_EMPTY ? 1 : 0);
211 static int getFieldbufferOffsetX_RND()
213 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
214 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
215 int dx_var = dx * TILESIZE_VAR / TILESIZE;
218 if (EVEN(SCR_FIELDX))
220 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
222 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
223 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
225 fx += (dx_var > 0 ? TILEX_VAR : 0);
232 if (full_lev_fieldx <= SCR_FIELDX)
234 if (EVEN(SCR_FIELDX))
235 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
237 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
243 static int getFieldbufferOffsetY_RND()
245 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
246 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
247 int dy_var = dy * TILESIZE_VAR / TILESIZE;
250 if (EVEN(SCR_FIELDY))
252 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
254 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
255 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
257 fy += (dy_var > 0 ? TILEY_VAR : 0);
264 if (full_lev_fieldy <= SCR_FIELDY)
266 if (EVEN(SCR_FIELDY))
267 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
269 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
275 static int getLevelFromScreenX_RND(int sx)
277 int fx = getFieldbufferOffsetX_RND();
280 int lx = LEVELX((px + dx) / TILESIZE_VAR);
285 static int getLevelFromScreenY_RND(int sy)
287 int fy = getFieldbufferOffsetY_RND();
290 int ly = LEVELY((py + dy) / TILESIZE_VAR);
295 static int getLevelFromScreenX_EM(int sx)
297 int level_xsize = level.native_em_level->lev->width;
298 int full_xsize = level_xsize * TILESIZE_VAR;
300 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
302 int fx = getFieldbufferOffsetX_EM();
305 int lx = LEVELX((px + dx) / TILESIZE_VAR);
307 lx = correctLevelPosX_EM(lx);
312 static int getLevelFromScreenY_EM(int sy)
314 int level_ysize = level.native_em_level->lev->height;
315 int full_ysize = level_ysize * TILESIZE_VAR;
317 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
319 int fy = getFieldbufferOffsetY_EM();
322 int ly = LEVELY((py + dy) / TILESIZE_VAR);
324 ly = correctLevelPosY_EM(ly);
329 static int getLevelFromScreenX_SP(int sx)
331 int menBorder = setup.sp_show_border_elements;
332 int level_xsize = level.native_sp_level->width;
333 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
335 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
337 int fx = getFieldbufferOffsetX_SP();
340 int lx = LEVELX((px + dx) / TILESIZE_VAR);
345 static int getLevelFromScreenY_SP(int sy)
347 int menBorder = setup.sp_show_border_elements;
348 int level_ysize = level.native_sp_level->height;
349 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
351 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
353 int fy = getFieldbufferOffsetY_SP();
356 int ly = LEVELY((py + dy) / TILESIZE_VAR);
361 static int getLevelFromScreenX_MM(int sx)
363 int level_xsize = level.native_mm_level->fieldx;
364 int full_xsize = level_xsize * TILESIZE_VAR;
366 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
369 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
374 static int getLevelFromScreenY_MM(int sy)
376 int level_ysize = level.native_mm_level->fieldy;
377 int full_ysize = level_ysize * TILESIZE_VAR;
379 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
382 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
387 int getLevelFromScreenX(int x)
389 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
390 return getLevelFromScreenX_EM(x);
391 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
392 return getLevelFromScreenX_SP(x);
393 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
394 return getLevelFromScreenX_MM(x);
396 return getLevelFromScreenX_RND(x);
399 int getLevelFromScreenY(int y)
401 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
402 return getLevelFromScreenY_EM(y);
403 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
404 return getLevelFromScreenY_SP(y);
405 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
406 return getLevelFromScreenY_MM(y);
408 return getLevelFromScreenY_RND(y);
411 void DumpTile(int x, int y)
417 printf_line("-", 79);
418 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
419 printf_line("-", 79);
421 if (!IN_LEV_FIELD(x, y))
423 printf("(not in level field)\n");
429 token_name = element_info[Feld[x][y]].token_name;
431 printf(" Feld: %d\t['%s']\n", Feld[x][y], token_name);
432 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
433 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
434 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
435 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
436 printf(" MovPos: %d\n", MovPos[x][y]);
437 printf(" MovDir: %d\n", MovDir[x][y]);
438 printf(" MovDelay: %d\n", MovDelay[x][y]);
439 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
440 printf(" CustomValue: %d\n", CustomValue[x][y]);
441 printf(" GfxElement: %d\n", GfxElement[x][y]);
442 printf(" GfxAction: %d\n", GfxAction[x][y]);
443 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
444 printf(" Player x/y: %d, %d\n", local_player->jx, local_player->jy);
448 void DumpTileFromScreen(int sx, int sy)
450 int lx = getLevelFromScreenX(sx);
451 int ly = getLevelFromScreenY(sy);
456 void SetDrawtoField(int mode)
458 if (mode == DRAW_TO_FIELDBUFFER)
464 BX2 = SCR_FIELDX + 1;
465 BY2 = SCR_FIELDY + 1;
467 drawto_field = fieldbuffer;
469 else /* DRAW_TO_BACKBUFFER */
475 BX2 = SCR_FIELDX - 1;
476 BY2 = SCR_FIELDY - 1;
478 drawto_field = backbuffer;
482 static void RedrawPlayfield_RND()
484 if (game.envelope_active)
487 DrawLevel(REDRAW_ALL);
491 void RedrawPlayfield()
493 if (game_status != GAME_MODE_PLAYING)
496 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
497 RedrawPlayfield_EM(TRUE);
498 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
499 RedrawPlayfield_SP(TRUE);
500 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
501 RedrawPlayfield_MM();
502 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
503 RedrawPlayfield_RND();
505 BlitScreenToBitmap(backbuffer);
507 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
511 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
514 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
515 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
517 if (x == -1 && y == -1)
520 if (draw_target == DRAW_TO_SCREEN)
521 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
523 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
526 static void DrawMaskedBorderExt_FIELD(int draw_target)
528 if (global.border_status >= GAME_MODE_MAIN &&
529 global.border_status <= GAME_MODE_PLAYING &&
530 border.draw_masked[global.border_status])
531 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
535 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
537 // when drawing to backbuffer, never draw border over open doors
538 if (draw_target == DRAW_TO_BACKBUFFER &&
539 (GetDoorState() & DOOR_OPEN_1))
542 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
543 (global.border_status != GAME_MODE_EDITOR ||
544 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
545 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
548 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
550 // when drawing to backbuffer, never draw border over open doors
551 if (draw_target == DRAW_TO_BACKBUFFER &&
552 (GetDoorState() & DOOR_OPEN_2))
555 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
556 global.border_status != GAME_MODE_EDITOR)
557 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
560 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
562 /* currently not available */
565 static void DrawMaskedBorderExt_ALL(int draw_target)
567 DrawMaskedBorderExt_FIELD(draw_target);
568 DrawMaskedBorderExt_DOOR_1(draw_target);
569 DrawMaskedBorderExt_DOOR_2(draw_target);
570 DrawMaskedBorderExt_DOOR_3(draw_target);
573 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
575 /* never draw masked screen borders on borderless screens */
576 if (global.border_status == GAME_MODE_LOADING ||
577 global.border_status == GAME_MODE_TITLE)
580 if (redraw_mask & REDRAW_ALL)
581 DrawMaskedBorderExt_ALL(draw_target);
584 if (redraw_mask & REDRAW_FIELD)
585 DrawMaskedBorderExt_FIELD(draw_target);
586 if (redraw_mask & REDRAW_DOOR_1)
587 DrawMaskedBorderExt_DOOR_1(draw_target);
588 if (redraw_mask & REDRAW_DOOR_2)
589 DrawMaskedBorderExt_DOOR_2(draw_target);
590 if (redraw_mask & REDRAW_DOOR_3)
591 DrawMaskedBorderExt_DOOR_3(draw_target);
595 void DrawMaskedBorder_FIELD()
597 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
600 void DrawMaskedBorder(int redraw_mask)
602 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
605 void DrawMaskedBorderToTarget(int draw_target)
607 if (draw_target == DRAW_TO_BACKBUFFER ||
608 draw_target == DRAW_TO_SCREEN)
610 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
614 int last_border_status = global.border_status;
616 if (draw_target == DRAW_TO_FADE_SOURCE)
618 global.border_status = gfx.fade_border_source_status;
619 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
621 else if (draw_target == DRAW_TO_FADE_TARGET)
623 global.border_status = gfx.fade_border_target_status;
624 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
627 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
629 global.border_status = last_border_status;
630 gfx.masked_border_bitmap_ptr = backbuffer;
634 void DrawTileCursor(int draw_target)
640 int graphic = IMG_GLOBAL_TILE_CURSOR;
642 int tilesize = TILESIZE_VAR;
643 int width = tilesize;
644 int height = tilesize;
646 if (game_status != GAME_MODE_PLAYING)
649 if (!tile_cursor.enabled ||
653 if (tile_cursor.moving)
655 int step = TILESIZE_VAR / 4;
656 int dx = tile_cursor.target_x - tile_cursor.x;
657 int dy = tile_cursor.target_y - tile_cursor.y;
660 tile_cursor.x = tile_cursor.target_x;
662 tile_cursor.x += SIGN(dx) * step;
665 tile_cursor.y = tile_cursor.target_y;
667 tile_cursor.y += SIGN(dy) * step;
669 if (tile_cursor.x == tile_cursor.target_x &&
670 tile_cursor.y == tile_cursor.target_y)
671 tile_cursor.moving = FALSE;
674 dst_x = tile_cursor.x;
675 dst_y = tile_cursor.y;
677 frame = getGraphicAnimationFrame(graphic, -1);
679 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
682 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
683 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
685 if (draw_target == DRAW_TO_SCREEN)
686 BlitToScreenMasked(src_bitmap, src_x, src_y, width, height, dst_x, dst_y);
688 BlitBitmapMasked(src_bitmap, fade_bitmap, src_x, src_y, width, height,
692 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
694 int fx = getFieldbufferOffsetX_RND();
695 int fy = getFieldbufferOffsetY_RND();
697 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
700 void BlitScreenToBitmap(Bitmap *target_bitmap)
702 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
703 BlitScreenToBitmap_EM(target_bitmap);
704 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
705 BlitScreenToBitmap_SP(target_bitmap);
706 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
707 BlitScreenToBitmap_MM(target_bitmap);
708 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
709 BlitScreenToBitmap_RND(target_bitmap);
711 redraw_mask |= REDRAW_FIELD;
714 void DrawFramesPerSecond()
717 int font_nr = FONT_TEXT_2;
718 int font_width = getFontWidth(font_nr);
719 int draw_deactivation_mask = GetDrawDeactivationMask();
720 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
722 /* draw FPS with leading space (needed if field buffer deactivated) */
723 sprintf(text, " %04.1f fps", global.frames_per_second);
725 /* override draw deactivation mask (required for invisible warp mode) */
726 SetDrawDeactivationMask(REDRAW_NONE);
728 /* draw opaque FPS if field buffer deactivated, else draw masked FPS */
729 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
730 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
732 /* set draw deactivation mask to previous value */
733 SetDrawDeactivationMask(draw_deactivation_mask);
735 /* force full-screen redraw in this frame */
736 redraw_mask = REDRAW_ALL;
740 static void PrintFrameTimeDebugging()
742 static unsigned int last_counter = 0;
743 unsigned int counter = Counter();
744 int diff_1 = counter - last_counter;
745 int diff_2 = diff_1 - GAME_FRAME_DELAY;
747 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
748 char diff_bar[2 * diff_2_max + 5];
752 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
754 for (i = 0; i < diff_2_max; i++)
755 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
756 i >= diff_2_max - diff_2_cut ? '-' : ' ');
758 diff_bar[pos++] = '|';
760 for (i = 0; i < diff_2_max; i++)
761 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
763 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
765 diff_bar[pos++] = '\0';
767 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
770 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
773 last_counter = counter;
777 static int unifiedRedrawMask(int mask)
779 if (mask & REDRAW_ALL)
782 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
788 static boolean equalRedrawMasks(int mask_1, int mask_2)
790 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
795 static int last_redraw_mask = REDRAW_NONE;
797 // force screen redraw in every frame to continue drawing global animations
798 // (but always use the last redraw mask to prevent unwanted side effects)
799 if (redraw_mask == REDRAW_NONE)
800 redraw_mask = last_redraw_mask;
802 last_redraw_mask = redraw_mask;
805 // masked border now drawn immediately when blitting backbuffer to window
807 // draw masked border to all viewports, if defined
808 DrawMaskedBorder(redraw_mask);
811 // draw frames per second (only if debug mode is enabled)
812 if (redraw_mask & REDRAW_FPS)
813 DrawFramesPerSecond();
815 // remove playfield redraw before potentially merging with doors redraw
816 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
817 redraw_mask &= ~REDRAW_FIELD;
819 // redraw complete window if both playfield and (some) doors need redraw
820 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
821 redraw_mask = REDRAW_ALL;
823 /* although redrawing the whole window would be fine for normal gameplay,
824 being able to only redraw the playfield is required for deactivating
825 certain drawing areas (mainly playfield) to work, which is needed for
826 warp-forward to be fast enough (by skipping redraw of most frames) */
828 if (redraw_mask & REDRAW_ALL)
830 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
832 else if (redraw_mask & REDRAW_FIELD)
834 BlitBitmap(backbuffer, window,
835 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
837 else if (redraw_mask & REDRAW_DOORS)
839 // merge door areas to prevent calling screen redraw more than once
845 if (redraw_mask & REDRAW_DOOR_1)
849 x2 = MAX(x2, DX + DXSIZE);
850 y2 = MAX(y2, DY + DYSIZE);
853 if (redraw_mask & REDRAW_DOOR_2)
857 x2 = MAX(x2, VX + VXSIZE);
858 y2 = MAX(y2, VY + VYSIZE);
861 if (redraw_mask & REDRAW_DOOR_3)
865 x2 = MAX(x2, EX + EXSIZE);
866 y2 = MAX(y2, EY + EYSIZE);
869 // make sure that at least one pixel is blitted, and inside the screen
870 // (else nothing is blitted, causing the animations not to be updated)
871 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
872 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
873 x2 = MIN(MAX(1, x2), WIN_XSIZE);
874 y2 = MIN(MAX(1, y2), WIN_YSIZE);
876 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
879 redraw_mask = REDRAW_NONE;
882 PrintFrameTimeDebugging();
886 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
888 unsigned int frame_delay_value_old = GetVideoFrameDelay();
890 SetVideoFrameDelay(frame_delay_value);
894 SetVideoFrameDelay(frame_delay_value_old);
897 static int fade_type_skip = FADE_TYPE_NONE;
899 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
901 void (*draw_border_function)(void) = NULL;
902 int x, y, width, height;
903 int fade_delay, post_delay;
905 if (fade_type == FADE_TYPE_FADE_OUT)
907 if (fade_type_skip != FADE_TYPE_NONE)
909 /* skip all fade operations until specified fade operation */
910 if (fade_type & fade_type_skip)
911 fade_type_skip = FADE_TYPE_NONE;
916 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
920 redraw_mask |= fade_mask;
922 if (fade_type == FADE_TYPE_SKIP)
924 fade_type_skip = fade_mode;
929 fade_delay = fading.fade_delay;
930 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
932 if (fade_type_skip != FADE_TYPE_NONE)
934 /* skip all fade operations until specified fade operation */
935 if (fade_type & fade_type_skip)
936 fade_type_skip = FADE_TYPE_NONE;
941 if (global.autoplay_leveldir)
946 if (fade_mask == REDRAW_FIELD)
951 height = FADE_SYSIZE;
953 if (border.draw_masked_when_fading)
954 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
956 DrawMaskedBorder_FIELD(); /* draw once */
958 else /* REDRAW_ALL */
966 if (!setup.fade_screens ||
968 fading.fade_mode == FADE_MODE_NONE)
970 if (fade_mode == FADE_MODE_FADE_OUT)
973 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
975 redraw_mask &= ~fade_mask;
980 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
981 draw_border_function);
983 redraw_mask &= ~fade_mask;
986 static void SetScreenStates_BeforeFadingIn()
988 // temporarily set screen mode for animations to screen after fading in
989 global.anim_status = global.anim_status_next;
991 // store backbuffer with all animations that will be started after fading in
992 if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
993 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
995 // set screen mode for animations back to fading
996 global.anim_status = GAME_MODE_PSEUDO_FADING;
999 static void SetScreenStates_AfterFadingIn()
1001 // store new source screen (to use correct masked border for fading)
1002 gfx.fade_border_source_status = global.border_status;
1004 global.anim_status = global.anim_status_next;
1007 static void SetScreenStates_BeforeFadingOut()
1009 // store new target screen (to use correct masked border for fading)
1010 gfx.fade_border_target_status = game_status;
1012 // set screen mode for animations to fading
1013 global.anim_status = GAME_MODE_PSEUDO_FADING;
1015 // store backbuffer with all animations that will be stopped for fading out
1016 if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1017 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
1020 static void SetScreenStates_AfterFadingOut()
1022 global.border_status = game_status;
1025 void FadeIn(int fade_mask)
1027 SetScreenStates_BeforeFadingIn();
1030 DrawMaskedBorder(REDRAW_ALL);
1033 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1034 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1036 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1040 FADE_SXSIZE = FULL_SXSIZE;
1041 FADE_SYSIZE = FULL_SYSIZE;
1043 if (game_status == GAME_MODE_PLAYING &&
1044 strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
1045 SetOverlayActive(TRUE);
1047 SetScreenStates_AfterFadingIn();
1049 // force update of global animation status in case of rapid screen changes
1050 redraw_mask = REDRAW_ALL;
1054 void FadeOut(int fade_mask)
1056 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1057 if (!equalRedrawMasks(fade_mask, redraw_mask))
1060 SetScreenStates_BeforeFadingOut();
1062 SetTileCursorActive(FALSE);
1063 SetOverlayActive(FALSE);
1066 DrawMaskedBorder(REDRAW_ALL);
1069 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1070 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1072 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1074 SetScreenStates_AfterFadingOut();
1077 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1079 static struct TitleFadingInfo fading_leave_stored;
1082 fading_leave_stored = fading_leave;
1084 fading = fading_leave_stored;
1087 void FadeSetEnterMenu()
1089 fading = menu.enter_menu;
1091 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1094 void FadeSetLeaveMenu()
1096 fading = menu.leave_menu;
1098 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1101 void FadeSetEnterScreen()
1103 fading = menu.enter_screen[game_status];
1105 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
1108 void FadeSetNextScreen()
1110 fading = menu.next_screen[game_status];
1112 // (do not overwrite fade mode set by FadeSetEnterScreen)
1113 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1116 void FadeSetLeaveScreen()
1118 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
1121 void FadeSetFromType(int type)
1123 if (type & TYPE_ENTER_SCREEN)
1124 FadeSetEnterScreen();
1125 else if (type & TYPE_ENTER)
1127 else if (type & TYPE_LEAVE)
1131 void FadeSetDisabled()
1133 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1135 fading = fading_none;
1138 void FadeSkipNextFadeIn()
1140 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1143 void FadeSkipNextFadeOut()
1145 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1148 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1150 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1152 return (graphic == IMG_UNDEFINED ? NULL :
1153 graphic_info[graphic].bitmap != NULL || redefined ?
1154 graphic_info[graphic].bitmap :
1155 graphic_info[default_graphic].bitmap);
1158 Bitmap *getBackgroundBitmap(int graphic)
1160 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1163 Bitmap *getGlobalBorderBitmap(int graphic)
1165 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1168 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1171 (status == GAME_MODE_MAIN ||
1172 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1173 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1174 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1175 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1178 return getGlobalBorderBitmap(graphic);
1181 void SetWindowBackgroundImageIfDefined(int graphic)
1183 if (graphic_info[graphic].bitmap)
1184 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1187 void SetMainBackgroundImageIfDefined(int graphic)
1189 if (graphic_info[graphic].bitmap)
1190 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1193 void SetDoorBackgroundImageIfDefined(int graphic)
1195 if (graphic_info[graphic].bitmap)
1196 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1199 void SetWindowBackgroundImage(int graphic)
1201 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1204 void SetMainBackgroundImage(int graphic)
1206 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1209 void SetDoorBackgroundImage(int graphic)
1211 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1214 void SetPanelBackground()
1216 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1218 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1219 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1221 SetDoorBackgroundBitmap(bitmap_db_panel);
1224 void DrawBackground(int x, int y, int width, int height)
1226 /* "drawto" might still point to playfield buffer here (hall of fame) */
1227 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1229 if (IN_GFX_FIELD_FULL(x, y))
1230 redraw_mask |= REDRAW_FIELD;
1231 else if (IN_GFX_DOOR_1(x, y))
1232 redraw_mask |= REDRAW_DOOR_1;
1233 else if (IN_GFX_DOOR_2(x, y))
1234 redraw_mask |= REDRAW_DOOR_2;
1235 else if (IN_GFX_DOOR_3(x, y))
1236 redraw_mask |= REDRAW_DOOR_3;
1239 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1241 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1243 if (font->bitmap == NULL)
1246 DrawBackground(x, y, width, height);
1249 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1251 struct GraphicInfo *g = &graphic_info[graphic];
1253 if (g->bitmap == NULL)
1256 DrawBackground(x, y, width, height);
1259 static int game_status_last = -1;
1260 static Bitmap *global_border_bitmap_last = NULL;
1261 static Bitmap *global_border_bitmap = NULL;
1262 static int real_sx_last = -1, real_sy_last = -1;
1263 static int full_sxsize_last = -1, full_sysize_last = -1;
1264 static int dx_last = -1, dy_last = -1;
1265 static int dxsize_last = -1, dysize_last = -1;
1266 static int vx_last = -1, vy_last = -1;
1267 static int vxsize_last = -1, vysize_last = -1;
1269 boolean CheckIfGlobalBorderHasChanged()
1271 // if game status has not changed, global border has not changed either
1272 if (game_status == game_status_last)
1275 // determine and store new global border bitmap for current game status
1276 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1278 return (global_border_bitmap_last != global_border_bitmap);
1281 boolean CheckIfGlobalBorderRedrawIsNeeded()
1283 // if game status has not changed, nothing has to be redrawn
1284 if (game_status == game_status_last)
1287 // redraw if last screen was title screen
1288 if (game_status_last == GAME_MODE_TITLE)
1291 // redraw if global screen border has changed
1292 if (CheckIfGlobalBorderHasChanged())
1295 // redraw if position or size of playfield area has changed
1296 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1297 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1300 // redraw if position or size of door area has changed
1301 if (dx_last != DX || dy_last != DY ||
1302 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1305 // redraw if position or size of tape area has changed
1306 if (vx_last != VX || vy_last != VY ||
1307 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1313 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1316 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1318 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1321 void RedrawGlobalBorder()
1323 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1325 RedrawGlobalBorderFromBitmap(bitmap);
1327 redraw_mask = REDRAW_ALL;
1330 static void RedrawGlobalBorderIfNeeded()
1332 if (game_status == game_status_last)
1335 // copy current draw buffer to later copy back areas that have not changed
1336 if (game_status_last != GAME_MODE_TITLE)
1337 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1339 if (CheckIfGlobalBorderRedrawIsNeeded())
1341 // redraw global screen border (or clear, if defined to be empty)
1342 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1344 // copy previous playfield and door areas, if they are defined on both
1345 // previous and current screen and if they still have the same size
1347 if (real_sx_last != -1 && real_sy_last != -1 &&
1348 REAL_SX != -1 && REAL_SY != -1 &&
1349 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1350 BlitBitmap(bitmap_db_store_1, backbuffer,
1351 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1354 if (dx_last != -1 && dy_last != -1 &&
1355 DX != -1 && DY != -1 &&
1356 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1357 BlitBitmap(bitmap_db_store_1, backbuffer,
1358 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1360 if (vx_last != -1 && vy_last != -1 &&
1361 VX != -1 && VY != -1 &&
1362 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1363 BlitBitmap(bitmap_db_store_1, backbuffer,
1364 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1366 redraw_mask = REDRAW_ALL;
1369 game_status_last = game_status;
1371 global_border_bitmap_last = global_border_bitmap;
1373 real_sx_last = REAL_SX;
1374 real_sy_last = REAL_SY;
1375 full_sxsize_last = FULL_SXSIZE;
1376 full_sysize_last = FULL_SYSIZE;
1379 dxsize_last = DXSIZE;
1380 dysize_last = DYSIZE;
1383 vxsize_last = VXSIZE;
1384 vysize_last = VYSIZE;
1389 RedrawGlobalBorderIfNeeded();
1391 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1392 /* (when entering hall of fame after playing) */
1393 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1395 /* !!! maybe this should be done before clearing the background !!! */
1396 if (game_status == GAME_MODE_PLAYING)
1398 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1399 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1403 SetDrawtoField(DRAW_TO_BACKBUFFER);
1407 void MarkTileDirty(int x, int y)
1409 redraw_mask |= REDRAW_FIELD;
1412 void SetBorderElement()
1416 BorderElement = EL_EMPTY;
1418 /* the MM game engine does not use a visible border element */
1419 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1422 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1424 for (x = 0; x < lev_fieldx; x++)
1426 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1427 BorderElement = EL_STEELWALL;
1429 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1435 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1436 int max_array_fieldx, int max_array_fieldy,
1437 short field[max_array_fieldx][max_array_fieldy],
1438 int max_fieldx, int max_fieldy)
1442 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1443 static int safety = 0;
1445 /* check if starting field still has the desired content */
1446 if (field[from_x][from_y] == fill_element)
1451 if (safety > max_fieldx * max_fieldy)
1452 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1454 old_element = field[from_x][from_y];
1455 field[from_x][from_y] = fill_element;
1457 for (i = 0; i < 4; i++)
1459 x = from_x + check[i][0];
1460 y = from_y + check[i][1];
1462 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1463 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1464 field, max_fieldx, max_fieldy);
1470 void FloodFillLevel(int from_x, int from_y, int fill_element,
1471 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1472 int max_fieldx, int max_fieldy)
1474 FloodFillLevelExt(from_x, from_y, fill_element,
1475 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1476 max_fieldx, max_fieldy);
1479 void SetRandomAnimationValue(int x, int y)
1481 gfx.anim_random_frame = GfxRandom[x][y];
1484 int getGraphicAnimationFrame(int graphic, int sync_frame)
1486 /* animation synchronized with global frame counter, not move position */
1487 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1488 sync_frame = FrameCounter;
1490 return getAnimationFrame(graphic_info[graphic].anim_frames,
1491 graphic_info[graphic].anim_delay,
1492 graphic_info[graphic].anim_mode,
1493 graphic_info[graphic].anim_start_frame,
1497 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1499 struct GraphicInfo *g = &graphic_info[graphic];
1500 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1502 if (tilesize == gfx.standard_tile_size)
1503 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1504 else if (tilesize == game.tile_size)
1505 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1507 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1510 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1511 boolean get_backside)
1513 struct GraphicInfo *g = &graphic_info[graphic];
1514 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1515 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1517 if (g->offset_y == 0) /* frames are ordered horizontally */
1519 int max_width = g->anim_frames_per_line * g->width;
1520 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1522 *x = pos % max_width;
1523 *y = src_y % g->height + pos / max_width * g->height;
1525 else if (g->offset_x == 0) /* frames are ordered vertically */
1527 int max_height = g->anim_frames_per_line * g->height;
1528 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1530 *x = src_x % g->width + pos / max_height * g->width;
1531 *y = pos % max_height;
1533 else /* frames are ordered diagonally */
1535 *x = src_x + frame * g->offset_x;
1536 *y = src_y + frame * g->offset_y;
1540 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1541 Bitmap **bitmap, int *x, int *y,
1542 boolean get_backside)
1544 struct GraphicInfo *g = &graphic_info[graphic];
1546 // if no in-game graphics defined, always use standard graphic size
1547 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1548 tilesize = TILESIZE;
1550 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1551 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1553 *x = *x * tilesize / g->tile_size;
1554 *y = *y * tilesize / g->tile_size;
1557 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1558 Bitmap **bitmap, int *x, int *y)
1560 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1563 void getFixedGraphicSource(int graphic, int frame,
1564 Bitmap **bitmap, int *x, int *y)
1566 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1569 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1571 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1574 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1575 int *x, int *y, boolean get_backside)
1577 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1581 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1583 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1586 void DrawGraphic(int x, int y, int graphic, int frame)
1589 if (!IN_SCR_FIELD(x, y))
1591 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1592 printf("DrawGraphic(): This should never happen!\n");
1597 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1600 MarkTileDirty(x, y);
1603 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1606 if (!IN_SCR_FIELD(x, y))
1608 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1609 printf("DrawGraphic(): This should never happen!\n");
1614 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1616 MarkTileDirty(x, y);
1619 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1625 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1627 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1630 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1636 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1637 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1640 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1643 if (!IN_SCR_FIELD(x, y))
1645 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1646 printf("DrawGraphicThruMask(): This should never happen!\n");
1651 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1654 MarkTileDirty(x, y);
1657 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1660 if (!IN_SCR_FIELD(x, y))
1662 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1663 printf("DrawGraphicThruMask(): This should never happen!\n");
1668 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1670 MarkTileDirty(x, y);
1673 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1679 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1681 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1685 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1686 int graphic, int frame)
1691 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1693 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1697 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1699 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1701 MarkTileDirty(x / tilesize, y / tilesize);
1704 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1707 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1708 graphic, frame, tilesize);
1709 MarkTileDirty(x / tilesize, y / tilesize);
1712 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1718 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1719 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1722 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1723 int frame, int tilesize)
1728 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1729 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1732 void DrawMiniGraphic(int x, int y, int graphic)
1734 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1735 MarkTileDirty(x / 2, y / 2);
1738 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1743 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1744 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1747 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1748 int graphic, int frame,
1749 int cut_mode, int mask_mode)
1754 int width = TILEX, height = TILEY;
1757 if (dx || dy) /* shifted graphic */
1759 if (x < BX1) /* object enters playfield from the left */
1766 else if (x > BX2) /* object enters playfield from the right */
1772 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1778 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1780 else if (dx) /* general horizontal movement */
1781 MarkTileDirty(x + SIGN(dx), y);
1783 if (y < BY1) /* object enters playfield from the top */
1785 if (cut_mode == CUT_BELOW) /* object completely above top border */
1793 else if (y > BY2) /* object enters playfield from the bottom */
1799 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1805 else if (dy > 0 && cut_mode == CUT_ABOVE)
1807 if (y == BY2) /* object completely above bottom border */
1813 MarkTileDirty(x, y + 1);
1814 } /* object leaves playfield to the bottom */
1815 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1817 else if (dy) /* general vertical movement */
1818 MarkTileDirty(x, y + SIGN(dy));
1822 if (!IN_SCR_FIELD(x, y))
1824 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1825 printf("DrawGraphicShifted(): This should never happen!\n");
1830 width = width * TILESIZE_VAR / TILESIZE;
1831 height = height * TILESIZE_VAR / TILESIZE;
1832 cx = cx * TILESIZE_VAR / TILESIZE;
1833 cy = cy * TILESIZE_VAR / TILESIZE;
1834 dx = dx * TILESIZE_VAR / TILESIZE;
1835 dy = dy * TILESIZE_VAR / TILESIZE;
1837 if (width > 0 && height > 0)
1839 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1844 dst_x = FX + x * TILEX_VAR + dx;
1845 dst_y = FY + y * TILEY_VAR + dy;
1847 if (mask_mode == USE_MASKING)
1848 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1851 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1854 MarkTileDirty(x, y);
1858 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1859 int graphic, int frame,
1860 int cut_mode, int mask_mode)
1865 int width = TILEX_VAR, height = TILEY_VAR;
1868 int x2 = x + SIGN(dx);
1869 int y2 = y + SIGN(dy);
1871 /* movement with two-tile animations must be sync'ed with movement position,
1872 not with current GfxFrame (which can be higher when using slow movement) */
1873 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1874 int anim_frames = graphic_info[graphic].anim_frames;
1876 /* (we also need anim_delay here for movement animations with less frames) */
1877 int anim_delay = graphic_info[graphic].anim_delay;
1878 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1880 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1881 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1883 /* re-calculate animation frame for two-tile movement animation */
1884 frame = getGraphicAnimationFrame(graphic, sync_frame);
1886 /* check if movement start graphic inside screen area and should be drawn */
1887 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1889 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1891 dst_x = FX + x1 * TILEX_VAR;
1892 dst_y = FY + y1 * TILEY_VAR;
1894 if (mask_mode == USE_MASKING)
1895 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1898 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1901 MarkTileDirty(x1, y1);
1904 /* check if movement end graphic inside screen area and should be drawn */
1905 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1907 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1909 dst_x = FX + x2 * TILEX_VAR;
1910 dst_y = FY + y2 * TILEY_VAR;
1912 if (mask_mode == USE_MASKING)
1913 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1916 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1919 MarkTileDirty(x2, y2);
1923 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1924 int graphic, int frame,
1925 int cut_mode, int mask_mode)
1929 DrawGraphic(x, y, graphic, frame);
1934 if (graphic_info[graphic].double_movement) /* EM style movement images */
1935 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1937 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1940 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1941 int frame, int cut_mode)
1943 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1946 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1947 int cut_mode, int mask_mode)
1949 int lx = LEVELX(x), ly = LEVELY(y);
1953 if (IN_LEV_FIELD(lx, ly))
1955 SetRandomAnimationValue(lx, ly);
1957 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1958 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1960 /* do not use double (EM style) movement graphic when not moving */
1961 if (graphic_info[graphic].double_movement && !dx && !dy)
1963 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1964 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1967 else /* border element */
1969 graphic = el2img(element);
1970 frame = getGraphicAnimationFrame(graphic, -1);
1973 if (element == EL_EXPANDABLE_WALL)
1975 boolean left_stopped = FALSE, right_stopped = FALSE;
1977 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1978 left_stopped = TRUE;
1979 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1980 right_stopped = TRUE;
1982 if (left_stopped && right_stopped)
1984 else if (left_stopped)
1986 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1987 frame = graphic_info[graphic].anim_frames - 1;
1989 else if (right_stopped)
1991 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1992 frame = graphic_info[graphic].anim_frames - 1;
1997 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1998 else if (mask_mode == USE_MASKING)
1999 DrawGraphicThruMask(x, y, graphic, frame);
2001 DrawGraphic(x, y, graphic, frame);
2004 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2005 int cut_mode, int mask_mode)
2007 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2008 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2009 cut_mode, mask_mode);
2012 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2015 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2018 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2021 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2024 void DrawLevelElementThruMask(int x, int y, int element)
2026 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2029 void DrawLevelFieldThruMask(int x, int y)
2031 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
2034 /* !!! implementation of quicksand is totally broken !!! */
2035 #define IS_CRUMBLED_TILE(x, y, e) \
2036 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2037 !IS_MOVING(x, y) || \
2038 (e) == EL_QUICKSAND_EMPTYING || \
2039 (e) == EL_QUICKSAND_FAST_EMPTYING))
2041 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2046 int width, height, cx, cy;
2047 int sx = SCREENX(x), sy = SCREENY(y);
2048 int crumbled_border_size = graphic_info[graphic].border_size;
2049 int crumbled_tile_size = graphic_info[graphic].tile_size;
2050 int crumbled_border_size_var =
2051 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2054 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2056 for (i = 1; i < 4; i++)
2058 int dxx = (i & 1 ? dx : 0);
2059 int dyy = (i & 2 ? dy : 0);
2062 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2065 /* check if neighbour field is of same crumble type */
2066 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2067 graphic_info[graphic].class ==
2068 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2070 /* return if check prevents inner corner */
2071 if (same == (dxx == dx && dyy == dy))
2075 /* if we reach this point, we have an inner corner */
2077 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2079 width = crumbled_border_size_var;
2080 height = crumbled_border_size_var;
2081 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2082 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2084 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2085 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2088 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2093 int width, height, bx, by, cx, cy;
2094 int sx = SCREENX(x), sy = SCREENY(y);
2095 int crumbled_border_size = graphic_info[graphic].border_size;
2096 int crumbled_tile_size = graphic_info[graphic].tile_size;
2097 int crumbled_border_size_var =
2098 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2099 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2102 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2104 /* draw simple, sloppy, non-corner-accurate crumbled border */
2106 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2107 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2108 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2109 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2111 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2112 FX + sx * TILEX_VAR + cx,
2113 FY + sy * TILEY_VAR + cy);
2115 /* (remaining middle border part must be at least as big as corner part) */
2116 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2117 crumbled_border_size_var >= TILESIZE_VAR / 3)
2120 /* correct corners of crumbled border, if needed */
2122 for (i = -1; i <= 1; i += 2)
2124 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2125 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2126 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2129 /* check if neighbour field is of same crumble type */
2130 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2131 graphic_info[graphic].class ==
2132 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2134 /* no crumbled corner, but continued crumbled border */
2136 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2137 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2138 int b1 = (i == 1 ? crumbled_border_size_var :
2139 TILESIZE_VAR - 2 * crumbled_border_size_var);
2141 width = crumbled_border_size_var;
2142 height = crumbled_border_size_var;
2144 if (dir == 1 || dir == 2)
2159 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2161 FX + sx * TILEX_VAR + cx,
2162 FY + sy * TILEY_VAR + cy);
2167 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2169 int sx = SCREENX(x), sy = SCREENY(y);
2172 static int xy[4][2] =
2180 if (!IN_LEV_FIELD(x, y))
2183 element = TILE_GFX_ELEMENT(x, y);
2185 if (IS_CRUMBLED_TILE(x, y, element)) /* crumble field itself */
2187 if (!IN_SCR_FIELD(sx, sy))
2190 /* crumble field borders towards direct neighbour fields */
2191 for (i = 0; i < 4; i++)
2193 int xx = x + xy[i][0];
2194 int yy = y + xy[i][1];
2196 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2199 /* check if neighbour field is of same crumble type */
2200 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2201 graphic_info[graphic].class ==
2202 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2205 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2208 /* crumble inner field corners towards corner neighbour fields */
2209 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2210 graphic_info[graphic].anim_frames == 2)
2212 for (i = 0; i < 4; i++)
2214 int dx = (i & 1 ? +1 : -1);
2215 int dy = (i & 2 ? +1 : -1);
2217 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2221 MarkTileDirty(sx, sy);
2223 else /* center field is not crumbled -- crumble neighbour fields */
2225 /* crumble field borders of direct neighbour fields */
2226 for (i = 0; i < 4; i++)
2228 int xx = x + xy[i][0];
2229 int yy = y + xy[i][1];
2230 int sxx = sx + xy[i][0];
2231 int syy = sy + xy[i][1];
2233 if (!IN_LEV_FIELD(xx, yy) ||
2234 !IN_SCR_FIELD(sxx, syy))
2237 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2240 element = TILE_GFX_ELEMENT(xx, yy);
2242 if (!IS_CRUMBLED_TILE(xx, yy, element))
2245 graphic = el_act2crm(element, ACTION_DEFAULT);
2247 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2249 MarkTileDirty(sxx, syy);
2252 /* crumble inner field corners of corner neighbour fields */
2253 for (i = 0; i < 4; i++)
2255 int dx = (i & 1 ? +1 : -1);
2256 int dy = (i & 2 ? +1 : -1);
2262 if (!IN_LEV_FIELD(xx, yy) ||
2263 !IN_SCR_FIELD(sxx, syy))
2266 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2269 element = TILE_GFX_ELEMENT(xx, yy);
2271 if (!IS_CRUMBLED_TILE(xx, yy, element))
2274 graphic = el_act2crm(element, ACTION_DEFAULT);
2276 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2277 graphic_info[graphic].anim_frames == 2)
2278 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2280 MarkTileDirty(sxx, syy);
2285 void DrawLevelFieldCrumbled(int x, int y)
2289 if (!IN_LEV_FIELD(x, y))
2292 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2293 GfxElement[x][y] != EL_UNDEFINED &&
2294 GFX_CRUMBLED(GfxElement[x][y]))
2296 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2301 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2303 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2306 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2309 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2310 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2311 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2312 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2313 int sx = SCREENX(x), sy = SCREENY(y);
2315 DrawGraphic(sx, sy, graphic1, frame1);
2316 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2319 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2321 int sx = SCREENX(x), sy = SCREENY(y);
2322 static int xy[4][2] =
2331 /* crumble direct neighbour fields (required for field borders) */
2332 for (i = 0; i < 4; i++)
2334 int xx = x + xy[i][0];
2335 int yy = y + xy[i][1];
2336 int sxx = sx + xy[i][0];
2337 int syy = sy + xy[i][1];
2339 if (!IN_LEV_FIELD(xx, yy) ||
2340 !IN_SCR_FIELD(sxx, syy) ||
2341 !GFX_CRUMBLED(Feld[xx][yy]) ||
2345 DrawLevelField(xx, yy);
2348 /* crumble corner neighbour fields (required for inner field corners) */
2349 for (i = 0; i < 4; i++)
2351 int dx = (i & 1 ? +1 : -1);
2352 int dy = (i & 2 ? +1 : -1);
2358 if (!IN_LEV_FIELD(xx, yy) ||
2359 !IN_SCR_FIELD(sxx, syy) ||
2360 !GFX_CRUMBLED(Feld[xx][yy]) ||
2364 int element = TILE_GFX_ELEMENT(xx, yy);
2365 int graphic = el_act2crm(element, ACTION_DEFAULT);
2367 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2368 graphic_info[graphic].anim_frames == 2)
2369 DrawLevelField(xx, yy);
2373 static int getBorderElement(int x, int y)
2377 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2378 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2379 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2380 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2381 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2382 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2383 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2385 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2386 int steel_position = (x == -1 && y == -1 ? 0 :
2387 x == lev_fieldx && y == -1 ? 1 :
2388 x == -1 && y == lev_fieldy ? 2 :
2389 x == lev_fieldx && y == lev_fieldy ? 3 :
2390 x == -1 || x == lev_fieldx ? 4 :
2391 y == -1 || y == lev_fieldy ? 5 : 6);
2393 return border[steel_position][steel_type];
2396 void DrawScreenElement(int x, int y, int element)
2398 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2399 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2402 void DrawLevelElement(int x, int y, int element)
2404 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2405 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2408 void DrawScreenField(int x, int y)
2410 int lx = LEVELX(x), ly = LEVELY(y);
2411 int element, content;
2413 if (!IN_LEV_FIELD(lx, ly))
2415 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2418 element = getBorderElement(lx, ly);
2420 DrawScreenElement(x, y, element);
2425 element = Feld[lx][ly];
2426 content = Store[lx][ly];
2428 if (IS_MOVING(lx, ly))
2430 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2431 boolean cut_mode = NO_CUTTING;
2433 if (element == EL_QUICKSAND_EMPTYING ||
2434 element == EL_QUICKSAND_FAST_EMPTYING ||
2435 element == EL_MAGIC_WALL_EMPTYING ||
2436 element == EL_BD_MAGIC_WALL_EMPTYING ||
2437 element == EL_DC_MAGIC_WALL_EMPTYING ||
2438 element == EL_AMOEBA_DROPPING)
2439 cut_mode = CUT_ABOVE;
2440 else if (element == EL_QUICKSAND_FILLING ||
2441 element == EL_QUICKSAND_FAST_FILLING ||
2442 element == EL_MAGIC_WALL_FILLING ||
2443 element == EL_BD_MAGIC_WALL_FILLING ||
2444 element == EL_DC_MAGIC_WALL_FILLING)
2445 cut_mode = CUT_BELOW;
2447 if (cut_mode == CUT_ABOVE)
2448 DrawScreenElement(x, y, element);
2450 DrawScreenElement(x, y, EL_EMPTY);
2453 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2454 else if (cut_mode == NO_CUTTING)
2455 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2458 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2460 if (cut_mode == CUT_BELOW &&
2461 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2462 DrawLevelElement(lx, ly + 1, element);
2465 if (content == EL_ACID)
2467 int dir = MovDir[lx][ly];
2468 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2469 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2471 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2473 // prevent target field from being drawn again (but without masking)
2474 // (this would happen if target field is scanned after moving element)
2475 Stop[newlx][newly] = TRUE;
2478 else if (IS_BLOCKED(lx, ly))
2483 boolean cut_mode = NO_CUTTING;
2484 int element_old, content_old;
2486 Blocked2Moving(lx, ly, &oldx, &oldy);
2489 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2490 MovDir[oldx][oldy] == MV_RIGHT);
2492 element_old = Feld[oldx][oldy];
2493 content_old = Store[oldx][oldy];
2495 if (element_old == EL_QUICKSAND_EMPTYING ||
2496 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2497 element_old == EL_MAGIC_WALL_EMPTYING ||
2498 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2499 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2500 element_old == EL_AMOEBA_DROPPING)
2501 cut_mode = CUT_ABOVE;
2503 DrawScreenElement(x, y, EL_EMPTY);
2506 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2508 else if (cut_mode == NO_CUTTING)
2509 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2512 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2515 else if (IS_DRAWABLE(element))
2516 DrawScreenElement(x, y, element);
2518 DrawScreenElement(x, y, EL_EMPTY);
2521 void DrawLevelField(int x, int y)
2523 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2524 DrawScreenField(SCREENX(x), SCREENY(y));
2525 else if (IS_MOVING(x, y))
2529 Moving2Blocked(x, y, &newx, &newy);
2530 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2531 DrawScreenField(SCREENX(newx), SCREENY(newy));
2533 else if (IS_BLOCKED(x, y))
2537 Blocked2Moving(x, y, &oldx, &oldy);
2538 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2539 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2543 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2544 int (*el2img_function)(int), boolean masked,
2545 int element_bits_draw)
2547 int element_base = map_mm_wall_element(element);
2548 int element_bits = (IS_DF_WALL(element) ?
2549 element - EL_DF_WALL_START :
2550 IS_MM_WALL(element) ?
2551 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2552 int graphic = el2img_function(element_base);
2553 int tilesize_draw = tilesize / 2;
2558 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2560 for (i = 0; i < 4; i++)
2562 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2563 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2565 if (!(element_bits_draw & (1 << i)))
2568 if (element_bits & (1 << i))
2571 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2572 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2574 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2575 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2580 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2581 tilesize_draw, tilesize_draw);
2586 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2587 boolean masked, int element_bits_draw)
2589 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2590 element, tilesize, el2edimg, masked, element_bits_draw);
2593 void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2594 int (*el2img_function)(int))
2596 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2600 void DrawSizedElementExt(int x, int y, int element, int tilesize,
2603 if (IS_MM_WALL(element))
2605 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2606 element, tilesize, el2edimg, masked, 0x000f);
2610 int graphic = el2edimg(element);
2613 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2615 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2619 void DrawSizedElement(int x, int y, int element, int tilesize)
2621 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2624 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2626 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2629 void DrawMiniElement(int x, int y, int element)
2633 graphic = el2edimg(element);
2634 DrawMiniGraphic(x, y, graphic);
2637 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2640 int x = sx + scroll_x, y = sy + scroll_y;
2642 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2643 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2644 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2645 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2647 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2650 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2652 int x = sx + scroll_x, y = sy + scroll_y;
2654 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2655 DrawMiniElement(sx, sy, EL_EMPTY);
2656 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2657 DrawMiniElement(sx, sy, Feld[x][y]);
2659 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2662 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2663 int x, int y, int xsize, int ysize,
2664 int tile_width, int tile_height)
2668 int dst_x = startx + x * tile_width;
2669 int dst_y = starty + y * tile_height;
2670 int width = graphic_info[graphic].width;
2671 int height = graphic_info[graphic].height;
2672 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2673 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2674 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2675 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2676 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2677 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2678 boolean draw_masked = graphic_info[graphic].draw_masked;
2680 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2682 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2684 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2688 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2689 inner_sx + (x - 1) * tile_width % inner_width);
2690 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2691 inner_sy + (y - 1) * tile_height % inner_height);
2694 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2697 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2701 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2702 int x, int y, int xsize, int ysize, int font_nr)
2704 int font_width = getFontWidth(font_nr);
2705 int font_height = getFontHeight(font_nr);
2707 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2708 font_width, font_height);
2711 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2713 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2714 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2715 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2716 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2717 boolean no_delay = (tape.warp_forward);
2718 unsigned int anim_delay = 0;
2719 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2720 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2721 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2722 int font_width = getFontWidth(font_nr);
2723 int font_height = getFontHeight(font_nr);
2724 int max_xsize = level.envelope[envelope_nr].xsize;
2725 int max_ysize = level.envelope[envelope_nr].ysize;
2726 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2727 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2728 int xend = max_xsize;
2729 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2730 int xstep = (xstart < xend ? 1 : 0);
2731 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2733 int end = MAX(xend - xstart, yend - ystart);
2736 for (i = start; i <= end; i++)
2738 int last_frame = end; // last frame of this "for" loop
2739 int x = xstart + i * xstep;
2740 int y = ystart + i * ystep;
2741 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2742 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2743 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2744 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2747 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2749 BlitScreenToBitmap(backbuffer);
2751 SetDrawtoField(DRAW_TO_BACKBUFFER);
2753 for (yy = 0; yy < ysize; yy++)
2754 for (xx = 0; xx < xsize; xx++)
2755 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2757 DrawTextBuffer(sx + font_width, sy + font_height,
2758 level.envelope[envelope_nr].text, font_nr, max_xsize,
2759 xsize - 2, ysize - 2, 0, mask_mode,
2760 level.envelope[envelope_nr].autowrap,
2761 level.envelope[envelope_nr].centered, FALSE);
2763 redraw_mask |= REDRAW_FIELD;
2766 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2770 void ShowEnvelope(int envelope_nr)
2772 int element = EL_ENVELOPE_1 + envelope_nr;
2773 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2774 int sound_opening = element_info[element].sound[ACTION_OPENING];
2775 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2776 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2777 boolean no_delay = (tape.warp_forward);
2778 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2779 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2780 int anim_mode = graphic_info[graphic].anim_mode;
2781 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2782 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2784 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2786 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2788 if (anim_mode == ANIM_DEFAULT)
2789 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2791 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2794 Delay(wait_delay_value);
2796 WaitForEventToContinue();
2798 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2800 if (anim_mode != ANIM_NONE)
2801 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2803 if (anim_mode == ANIM_DEFAULT)
2804 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2806 game.envelope_active = FALSE;
2808 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2810 redraw_mask |= REDRAW_FIELD;
2814 static void setRequestBasePosition(int *x, int *y)
2816 int sx_base, sy_base;
2818 if (request.x != -1)
2819 sx_base = request.x;
2820 else if (request.align == ALIGN_LEFT)
2822 else if (request.align == ALIGN_RIGHT)
2823 sx_base = SX + SXSIZE;
2825 sx_base = SX + SXSIZE / 2;
2827 if (request.y != -1)
2828 sy_base = request.y;
2829 else if (request.valign == VALIGN_TOP)
2831 else if (request.valign == VALIGN_BOTTOM)
2832 sy_base = SY + SYSIZE;
2834 sy_base = SY + SYSIZE / 2;
2840 static void setRequestPositionExt(int *x, int *y, int width, int height,
2841 boolean add_border_size)
2843 int border_size = request.border_size;
2844 int sx_base, sy_base;
2847 setRequestBasePosition(&sx_base, &sy_base);
2849 if (request.align == ALIGN_LEFT)
2851 else if (request.align == ALIGN_RIGHT)
2852 sx = sx_base - width;
2854 sx = sx_base - width / 2;
2856 if (request.valign == VALIGN_TOP)
2858 else if (request.valign == VALIGN_BOTTOM)
2859 sy = sy_base - height;
2861 sy = sy_base - height / 2;
2863 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2864 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2866 if (add_border_size)
2876 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2878 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2881 void DrawEnvelopeRequest(char *text)
2883 char *text_final = text;
2884 char *text_door_style = NULL;
2885 int graphic = IMG_BACKGROUND_REQUEST;
2886 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2887 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2888 int font_nr = FONT_REQUEST;
2889 int font_width = getFontWidth(font_nr);
2890 int font_height = getFontHeight(font_nr);
2891 int border_size = request.border_size;
2892 int line_spacing = request.line_spacing;
2893 int line_height = font_height + line_spacing;
2894 int max_text_width = request.width - 2 * border_size;
2895 int max_text_height = request.height - 2 * border_size;
2896 int line_length = max_text_width / font_width;
2897 int max_lines = max_text_height / line_height;
2898 int text_width = line_length * font_width;
2899 int width = request.width;
2900 int height = request.height;
2901 int tile_size = MAX(request.step_offset, 1);
2902 int x_steps = width / tile_size;
2903 int y_steps = height / tile_size;
2904 int sx_offset = border_size;
2905 int sy_offset = border_size;
2909 if (request.centered)
2910 sx_offset = (request.width - text_width) / 2;
2912 if (request.wrap_single_words && !request.autowrap)
2914 char *src_text_ptr, *dst_text_ptr;
2916 text_door_style = checked_malloc(2 * strlen(text) + 1);
2918 src_text_ptr = text;
2919 dst_text_ptr = text_door_style;
2921 while (*src_text_ptr)
2923 if (*src_text_ptr == ' ' ||
2924 *src_text_ptr == '?' ||
2925 *src_text_ptr == '!')
2926 *dst_text_ptr++ = '\n';
2928 if (*src_text_ptr != ' ')
2929 *dst_text_ptr++ = *src_text_ptr;
2934 *dst_text_ptr = '\0';
2936 text_final = text_door_style;
2939 setRequestPosition(&sx, &sy, FALSE);
2941 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2943 for (y = 0; y < y_steps; y++)
2944 for (x = 0; x < x_steps; x++)
2945 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2946 x, y, x_steps, y_steps,
2947 tile_size, tile_size);
2949 /* force DOOR font inside door area */
2950 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2952 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2953 line_length, -1, max_lines, line_spacing, mask_mode,
2954 request.autowrap, request.centered, FALSE);
2958 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2959 RedrawGadget(tool_gadget[i]);
2961 // store readily prepared envelope request for later use when animating
2962 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2964 if (text_door_style)
2965 free(text_door_style);
2968 void AnimateEnvelopeRequest(int anim_mode, int action)
2970 int graphic = IMG_BACKGROUND_REQUEST;
2971 boolean draw_masked = graphic_info[graphic].draw_masked;
2972 int delay_value_normal = request.step_delay;
2973 int delay_value_fast = delay_value_normal / 2;
2974 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2975 boolean no_delay = (tape.warp_forward);
2976 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2977 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
2978 unsigned int anim_delay = 0;
2980 int tile_size = MAX(request.step_offset, 1);
2981 int max_xsize = request.width / tile_size;
2982 int max_ysize = request.height / tile_size;
2983 int max_xsize_inner = max_xsize - 2;
2984 int max_ysize_inner = max_ysize - 2;
2986 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2987 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2988 int xend = max_xsize_inner;
2989 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2990 int xstep = (xstart < xend ? 1 : 0);
2991 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2993 int end = MAX(xend - xstart, yend - ystart);
2996 if (setup.quick_doors)
3003 for (i = start; i <= end; i++)
3005 int last_frame = end; // last frame of this "for" loop
3006 int x = xstart + i * xstep;
3007 int y = ystart + i * ystep;
3008 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3009 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3010 int xsize_size_left = (xsize - 1) * tile_size;
3011 int ysize_size_top = (ysize - 1) * tile_size;
3012 int max_xsize_pos = (max_xsize - 1) * tile_size;
3013 int max_ysize_pos = (max_ysize - 1) * tile_size;
3014 int width = xsize * tile_size;
3015 int height = ysize * tile_size;
3020 setRequestPosition(&src_x, &src_y, FALSE);
3021 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3023 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3025 for (yy = 0; yy < 2; yy++)
3027 for (xx = 0; xx < 2; xx++)
3029 int src_xx = src_x + xx * max_xsize_pos;
3030 int src_yy = src_y + yy * max_ysize_pos;
3031 int dst_xx = dst_x + xx * xsize_size_left;
3032 int dst_yy = dst_y + yy * ysize_size_top;
3033 int xx_size = (xx ? tile_size : xsize_size_left);
3034 int yy_size = (yy ? tile_size : ysize_size_top);
3037 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3038 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3040 BlitBitmap(bitmap_db_store_2, backbuffer,
3041 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3045 redraw_mask |= REDRAW_FIELD;
3049 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3053 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3055 int graphic = IMG_BACKGROUND_REQUEST;
3056 int sound_opening = SND_REQUEST_OPENING;
3057 int sound_closing = SND_REQUEST_CLOSING;
3058 int anim_mode_1 = request.anim_mode; /* (higher priority) */
3059 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
3060 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3061 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3062 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3064 if (game_status == GAME_MODE_PLAYING)
3065 BlitScreenToBitmap(backbuffer);
3067 SetDrawtoField(DRAW_TO_BACKBUFFER);
3069 // SetDrawBackgroundMask(REDRAW_NONE);
3071 if (action == ACTION_OPENING)
3073 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3075 if (req_state & REQ_ASK)
3077 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3078 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3080 else if (req_state & REQ_CONFIRM)
3082 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3084 else if (req_state & REQ_PLAYER)
3086 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3087 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3088 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3089 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3092 DrawEnvelopeRequest(text);
3095 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
3097 if (action == ACTION_OPENING)
3099 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3101 if (anim_mode == ANIM_DEFAULT)
3102 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3104 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3108 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3110 if (anim_mode != ANIM_NONE)
3111 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3113 if (anim_mode == ANIM_DEFAULT)
3114 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3117 game.envelope_active = FALSE;
3119 if (action == ACTION_CLOSING)
3120 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3122 // SetDrawBackgroundMask(last_draw_background_mask);
3124 redraw_mask |= REDRAW_FIELD;
3128 if (action == ACTION_CLOSING &&
3129 game_status == GAME_MODE_PLAYING &&
3130 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3131 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3134 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3136 if (IS_MM_WALL(element))
3138 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3144 int graphic = el2preimg(element);
3146 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3147 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3152 void DrawLevel(int draw_background_mask)
3156 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3157 SetDrawBackgroundMask(draw_background_mask);
3161 for (x = BX1; x <= BX2; x++)
3162 for (y = BY1; y <= BY2; y++)
3163 DrawScreenField(x, y);
3165 redraw_mask |= REDRAW_FIELD;
3168 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3173 for (x = 0; x < size_x; x++)
3174 for (y = 0; y < size_y; y++)
3175 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3177 redraw_mask |= REDRAW_FIELD;
3180 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3184 for (x = 0; x < size_x; x++)
3185 for (y = 0; y < size_y; y++)
3186 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3188 redraw_mask |= REDRAW_FIELD;
3191 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3193 boolean show_level_border = (BorderElement != EL_EMPTY);
3194 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3195 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3196 int tile_size = preview.tile_size;
3197 int preview_width = preview.xsize * tile_size;
3198 int preview_height = preview.ysize * tile_size;
3199 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3200 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3201 int real_preview_width = real_preview_xsize * tile_size;
3202 int real_preview_height = real_preview_ysize * tile_size;
3203 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3204 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3207 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3210 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3212 dst_x += (preview_width - real_preview_width) / 2;
3213 dst_y += (preview_height - real_preview_height) / 2;
3215 for (x = 0; x < real_preview_xsize; x++)
3217 for (y = 0; y < real_preview_ysize; y++)
3219 int lx = from_x + x + (show_level_border ? -1 : 0);
3220 int ly = from_y + y + (show_level_border ? -1 : 0);
3221 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3222 getBorderElement(lx, ly));
3224 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3225 element, tile_size);
3229 redraw_mask |= REDRAW_FIELD;
3232 #define MICROLABEL_EMPTY 0
3233 #define MICROLABEL_LEVEL_NAME 1
3234 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3235 #define MICROLABEL_LEVEL_AUTHOR 3
3236 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3237 #define MICROLABEL_IMPORTED_FROM 5
3238 #define MICROLABEL_IMPORTED_BY_HEAD 6
3239 #define MICROLABEL_IMPORTED_BY 7
3241 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3243 int max_text_width = SXSIZE;
3244 int font_width = getFontWidth(font_nr);
3246 if (pos->align == ALIGN_CENTER)
3247 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3248 else if (pos->align == ALIGN_RIGHT)
3249 max_text_width = pos->x;
3251 max_text_width = SXSIZE - pos->x;
3253 return max_text_width / font_width;
3256 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3258 char label_text[MAX_OUTPUT_LINESIZE + 1];
3259 int max_len_label_text;
3260 int font_nr = pos->font;
3263 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3266 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3267 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3268 mode == MICROLABEL_IMPORTED_BY_HEAD)
3269 font_nr = pos->font_alt;
3271 max_len_label_text = getMaxTextLength(pos, font_nr);
3273 if (pos->size != -1)
3274 max_len_label_text = pos->size;
3276 for (i = 0; i < max_len_label_text; i++)
3277 label_text[i] = ' ';
3278 label_text[max_len_label_text] = '\0';
3280 if (strlen(label_text) > 0)
3281 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3284 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3285 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3286 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3287 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3288 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3289 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3290 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3291 max_len_label_text);
3292 label_text[max_len_label_text] = '\0';
3294 if (strlen(label_text) > 0)
3295 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3297 redraw_mask |= REDRAW_FIELD;
3300 static void DrawPreviewLevelLabel(int mode)
3302 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3305 static void DrawPreviewLevelInfo(int mode)
3307 if (mode == MICROLABEL_LEVEL_NAME)
3308 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3309 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3310 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3313 static void DrawPreviewLevelExt(boolean restart)
3315 static unsigned int scroll_delay = 0;
3316 static unsigned int label_delay = 0;
3317 static int from_x, from_y, scroll_direction;
3318 static int label_state, label_counter;
3319 unsigned int scroll_delay_value = preview.step_delay;
3320 boolean show_level_border = (BorderElement != EL_EMPTY);
3321 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3322 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3329 if (preview.anim_mode == ANIM_CENTERED)
3331 if (level_xsize > preview.xsize)
3332 from_x = (level_xsize - preview.xsize) / 2;
3333 if (level_ysize > preview.ysize)
3334 from_y = (level_ysize - preview.ysize) / 2;
3337 from_x += preview.xoffset;
3338 from_y += preview.yoffset;
3340 scroll_direction = MV_RIGHT;
3344 DrawPreviewLevelPlayfield(from_x, from_y);
3345 DrawPreviewLevelLabel(label_state);
3347 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3348 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3350 /* initialize delay counters */
3351 DelayReached(&scroll_delay, 0);
3352 DelayReached(&label_delay, 0);
3354 if (leveldir_current->name)
3356 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3357 char label_text[MAX_OUTPUT_LINESIZE + 1];
3358 int font_nr = pos->font;
3359 int max_len_label_text = getMaxTextLength(pos, font_nr);
3361 if (pos->size != -1)
3362 max_len_label_text = pos->size;
3364 strncpy(label_text, leveldir_current->name, max_len_label_text);
3365 label_text[max_len_label_text] = '\0';
3367 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3368 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3374 /* scroll preview level, if needed */
3375 if (preview.anim_mode != ANIM_NONE &&
3376 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3377 DelayReached(&scroll_delay, scroll_delay_value))
3379 switch (scroll_direction)
3384 from_x -= preview.step_offset;
3385 from_x = (from_x < 0 ? 0 : from_x);
3388 scroll_direction = MV_UP;
3392 if (from_x < level_xsize - preview.xsize)
3394 from_x += preview.step_offset;
3395 from_x = (from_x > level_xsize - preview.xsize ?
3396 level_xsize - preview.xsize : from_x);
3399 scroll_direction = MV_DOWN;
3405 from_y -= preview.step_offset;
3406 from_y = (from_y < 0 ? 0 : from_y);
3409 scroll_direction = MV_RIGHT;
3413 if (from_y < level_ysize - preview.ysize)
3415 from_y += preview.step_offset;
3416 from_y = (from_y > level_ysize - preview.ysize ?
3417 level_ysize - preview.ysize : from_y);
3420 scroll_direction = MV_LEFT;
3427 DrawPreviewLevelPlayfield(from_x, from_y);
3430 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
3431 /* redraw micro level label, if needed */
3432 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3433 !strEqual(level.author, ANONYMOUS_NAME) &&
3434 !strEqual(level.author, leveldir_current->name) &&
3435 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3437 int max_label_counter = 23;
3439 if (leveldir_current->imported_from != NULL &&
3440 strlen(leveldir_current->imported_from) > 0)
3441 max_label_counter += 14;
3442 if (leveldir_current->imported_by != NULL &&
3443 strlen(leveldir_current->imported_by) > 0)
3444 max_label_counter += 14;
3446 label_counter = (label_counter + 1) % max_label_counter;
3447 label_state = (label_counter >= 0 && label_counter <= 7 ?
3448 MICROLABEL_LEVEL_NAME :
3449 label_counter >= 9 && label_counter <= 12 ?
3450 MICROLABEL_LEVEL_AUTHOR_HEAD :
3451 label_counter >= 14 && label_counter <= 21 ?
3452 MICROLABEL_LEVEL_AUTHOR :
3453 label_counter >= 23 && label_counter <= 26 ?
3454 MICROLABEL_IMPORTED_FROM_HEAD :
3455 label_counter >= 28 && label_counter <= 35 ?
3456 MICROLABEL_IMPORTED_FROM :
3457 label_counter >= 37 && label_counter <= 40 ?
3458 MICROLABEL_IMPORTED_BY_HEAD :
3459 label_counter >= 42 && label_counter <= 49 ?
3460 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3462 if (leveldir_current->imported_from == NULL &&
3463 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3464 label_state == MICROLABEL_IMPORTED_FROM))
3465 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3466 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3468 DrawPreviewLevelLabel(label_state);
3472 void DrawPreviewLevelInitial()
3474 DrawPreviewLevelExt(TRUE);
3477 void DrawPreviewLevelAnimation()
3479 DrawPreviewLevelExt(FALSE);
3482 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3483 int graphic, int sync_frame,
3486 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3488 if (mask_mode == USE_MASKING)
3489 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3491 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3494 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3495 int graphic, int sync_frame, int mask_mode)
3497 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3499 if (mask_mode == USE_MASKING)
3500 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3502 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3505 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3507 int lx = LEVELX(x), ly = LEVELY(y);
3509 if (!IN_SCR_FIELD(x, y))
3512 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3513 graphic, GfxFrame[lx][ly], NO_MASKING);
3515 MarkTileDirty(x, y);
3518 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3520 int lx = LEVELX(x), ly = LEVELY(y);
3522 if (!IN_SCR_FIELD(x, y))
3525 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3526 graphic, GfxFrame[lx][ly], NO_MASKING);
3527 MarkTileDirty(x, y);
3530 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3532 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3535 void DrawLevelElementAnimation(int x, int y, int element)
3537 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3539 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3542 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3544 int sx = SCREENX(x), sy = SCREENY(y);
3546 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3549 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3552 DrawGraphicAnimation(sx, sy, graphic);
3555 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3556 DrawLevelFieldCrumbled(x, y);
3558 if (GFX_CRUMBLED(Feld[x][y]))
3559 DrawLevelFieldCrumbled(x, y);
3563 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3565 int sx = SCREENX(x), sy = SCREENY(y);
3568 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3571 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3573 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3576 DrawGraphicAnimation(sx, sy, graphic);
3578 if (GFX_CRUMBLED(element))
3579 DrawLevelFieldCrumbled(x, y);
3582 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3584 if (player->use_murphy)
3586 /* this works only because currently only one player can be "murphy" ... */
3587 static int last_horizontal_dir = MV_LEFT;
3588 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3590 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3591 last_horizontal_dir = move_dir;
3593 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3595 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3597 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3603 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3606 static boolean equalGraphics(int graphic1, int graphic2)
3608 struct GraphicInfo *g1 = &graphic_info[graphic1];
3609 struct GraphicInfo *g2 = &graphic_info[graphic2];
3611 return (g1->bitmap == g2->bitmap &&
3612 g1->src_x == g2->src_x &&
3613 g1->src_y == g2->src_y &&
3614 g1->anim_frames == g2->anim_frames &&
3615 g1->anim_delay == g2->anim_delay &&
3616 g1->anim_mode == g2->anim_mode);
3619 void DrawAllPlayers()
3623 for (i = 0; i < MAX_PLAYERS; i++)
3624 if (stored_player[i].active)
3625 DrawPlayer(&stored_player[i]);
3628 void DrawPlayerField(int x, int y)
3630 if (!IS_PLAYER(x, y))
3633 DrawPlayer(PLAYERINFO(x, y));
3636 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3638 void DrawPlayer(struct PlayerInfo *player)
3640 int jx = player->jx;
3641 int jy = player->jy;
3642 int move_dir = player->MovDir;
3643 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3644 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3645 int last_jx = (player->is_moving ? jx - dx : jx);
3646 int last_jy = (player->is_moving ? jy - dy : jy);
3647 int next_jx = jx + dx;
3648 int next_jy = jy + dy;
3649 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3650 boolean player_is_opaque = FALSE;
3651 int sx = SCREENX(jx), sy = SCREENY(jy);
3652 int sxx = 0, syy = 0;
3653 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3655 int action = ACTION_DEFAULT;
3656 int last_player_graphic = getPlayerGraphic(player, move_dir);
3657 int last_player_frame = player->Frame;
3660 /* GfxElement[][] is set to the element the player is digging or collecting;
3661 remove also for off-screen player if the player is not moving anymore */
3662 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3663 GfxElement[jx][jy] = EL_UNDEFINED;
3665 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3669 if (!IN_LEV_FIELD(jx, jy))
3671 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3672 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3673 printf("DrawPlayerField(): This should never happen!\n");
3678 if (element == EL_EXPLOSION)
3681 action = (player->is_pushing ? ACTION_PUSHING :
3682 player->is_digging ? ACTION_DIGGING :
3683 player->is_collecting ? ACTION_COLLECTING :
3684 player->is_moving ? ACTION_MOVING :
3685 player->is_snapping ? ACTION_SNAPPING :
3686 player->is_dropping ? ACTION_DROPPING :
3687 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3689 if (player->is_waiting)
3690 move_dir = player->dir_waiting;
3692 InitPlayerGfxAnimation(player, action, move_dir);
3694 /* ----------------------------------------------------------------------- */
3695 /* draw things in the field the player is leaving, if needed */
3696 /* ----------------------------------------------------------------------- */
3698 if (player->is_moving)
3700 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3702 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3704 if (last_element == EL_DYNAMITE_ACTIVE ||
3705 last_element == EL_EM_DYNAMITE_ACTIVE ||
3706 last_element == EL_SP_DISK_RED_ACTIVE)
3707 DrawDynamite(last_jx, last_jy);
3709 DrawLevelFieldThruMask(last_jx, last_jy);
3711 else if (last_element == EL_DYNAMITE_ACTIVE ||
3712 last_element == EL_EM_DYNAMITE_ACTIVE ||
3713 last_element == EL_SP_DISK_RED_ACTIVE)
3714 DrawDynamite(last_jx, last_jy);
3716 /* !!! this is not enough to prevent flickering of players which are
3717 moving next to each others without a free tile between them -- this
3718 can only be solved by drawing all players layer by layer (first the
3719 background, then the foreground etc.) !!! => TODO */
3720 else if (!IS_PLAYER(last_jx, last_jy))
3721 DrawLevelField(last_jx, last_jy);
3724 DrawLevelField(last_jx, last_jy);
3727 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3728 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3731 if (!IN_SCR_FIELD(sx, sy))
3734 /* ----------------------------------------------------------------------- */
3735 /* draw things behind the player, if needed */
3736 /* ----------------------------------------------------------------------- */
3739 DrawLevelElement(jx, jy, Back[jx][jy]);
3740 else if (IS_ACTIVE_BOMB(element))
3741 DrawLevelElement(jx, jy, EL_EMPTY);
3744 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3746 int old_element = GfxElement[jx][jy];
3747 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3748 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3750 if (GFX_CRUMBLED(old_element))
3751 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3753 DrawGraphic(sx, sy, old_graphic, frame);
3755 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3756 player_is_opaque = TRUE;
3760 GfxElement[jx][jy] = EL_UNDEFINED;
3762 /* make sure that pushed elements are drawn with correct frame rate */
3763 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3765 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3766 GfxFrame[jx][jy] = player->StepFrame;
3768 DrawLevelField(jx, jy);
3772 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3773 /* ----------------------------------------------------------------------- */
3774 /* draw player himself */
3775 /* ----------------------------------------------------------------------- */
3777 graphic = getPlayerGraphic(player, move_dir);
3779 /* in the case of changed player action or direction, prevent the current
3780 animation frame from being restarted for identical animations */
3781 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3782 player->Frame = last_player_frame;
3784 frame = getGraphicAnimationFrame(graphic, player->Frame);
3788 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3789 sxx = player->GfxPos;
3791 syy = player->GfxPos;
3794 if (player_is_opaque)
3795 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3797 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3799 if (SHIELD_ON(player))
3801 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3802 IMG_SHIELD_NORMAL_ACTIVE);
3803 int frame = getGraphicAnimationFrame(graphic, -1);
3805 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3809 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3812 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3813 sxx = player->GfxPos;
3815 syy = player->GfxPos;
3819 /* ----------------------------------------------------------------------- */
3820 /* draw things the player is pushing, if needed */
3821 /* ----------------------------------------------------------------------- */
3823 if (player->is_pushing && player->is_moving)
3825 int px = SCREENX(jx), py = SCREENY(jy);
3826 int pxx = (TILEX - ABS(sxx)) * dx;
3827 int pyy = (TILEY - ABS(syy)) * dy;
3828 int gfx_frame = GfxFrame[jx][jy];
3834 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3836 element = Feld[next_jx][next_jy];
3837 gfx_frame = GfxFrame[next_jx][next_jy];
3840 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3842 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3843 frame = getGraphicAnimationFrame(graphic, sync_frame);
3845 /* draw background element under pushed element (like the Sokoban field) */
3846 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3848 /* this allows transparent pushing animation over non-black background */
3851 DrawLevelElement(jx, jy, Back[jx][jy]);
3853 DrawLevelElement(jx, jy, EL_EMPTY);
3855 if (Back[next_jx][next_jy])
3856 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3858 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3860 else if (Back[next_jx][next_jy])
3861 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3864 /* do not draw (EM style) pushing animation when pushing is finished */
3865 /* (two-tile animations usually do not contain start and end frame) */
3866 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3867 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3869 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3871 /* masked drawing is needed for EMC style (double) movement graphics */
3872 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3873 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3877 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3878 /* ----------------------------------------------------------------------- */
3879 /* draw player himself */
3880 /* ----------------------------------------------------------------------- */
3882 graphic = getPlayerGraphic(player, move_dir);
3884 /* in the case of changed player action or direction, prevent the current
3885 animation frame from being restarted for identical animations */
3886 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3887 player->Frame = last_player_frame;
3889 frame = getGraphicAnimationFrame(graphic, player->Frame);
3893 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3894 sxx = player->GfxPos;
3896 syy = player->GfxPos;
3899 if (player_is_opaque)
3900 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3902 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3904 if (SHIELD_ON(player))
3906 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3907 IMG_SHIELD_NORMAL_ACTIVE);
3908 int frame = getGraphicAnimationFrame(graphic, -1);
3910 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3914 /* ----------------------------------------------------------------------- */
3915 /* draw things in front of player (active dynamite or dynabombs) */
3916 /* ----------------------------------------------------------------------- */
3918 if (IS_ACTIVE_BOMB(element))
3920 graphic = el2img(element);
3921 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3923 if (game.emulation == EMU_SUPAPLEX)
3924 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3926 DrawGraphicThruMask(sx, sy, graphic, frame);
3929 if (player_is_moving && last_element == EL_EXPLOSION)
3931 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3932 GfxElement[last_jx][last_jy] : EL_EMPTY);
3933 int graphic = el_act2img(element, ACTION_EXPLODING);
3934 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3935 int phase = ExplodePhase[last_jx][last_jy] - 1;
3936 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3939 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3942 /* ----------------------------------------------------------------------- */
3943 /* draw elements the player is just walking/passing through/under */
3944 /* ----------------------------------------------------------------------- */
3946 if (player_is_moving)
3948 /* handle the field the player is leaving ... */
3949 if (IS_ACCESSIBLE_INSIDE(last_element))
3950 DrawLevelField(last_jx, last_jy);
3951 else if (IS_ACCESSIBLE_UNDER(last_element))
3952 DrawLevelFieldThruMask(last_jx, last_jy);
3955 /* do not redraw accessible elements if the player is just pushing them */
3956 if (!player_is_moving || !player->is_pushing)
3958 /* ... and the field the player is entering */
3959 if (IS_ACCESSIBLE_INSIDE(element))
3960 DrawLevelField(jx, jy);
3961 else if (IS_ACCESSIBLE_UNDER(element))
3962 DrawLevelFieldThruMask(jx, jy);