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(void);
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(void)
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 sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
221 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
223 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
224 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
226 fx += (dx_var > 0 ? TILEX_VAR : 0);
233 if (full_lev_fieldx <= SCR_FIELDX)
235 if (EVEN(SCR_FIELDX))
236 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
238 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
244 static int getFieldbufferOffsetY_RND(void)
246 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
247 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
248 int dy_var = dy * TILESIZE_VAR / TILESIZE;
251 if (EVEN(SCR_FIELDY))
253 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
254 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
256 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
257 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
259 fy += (dy_var > 0 ? TILEY_VAR : 0);
266 if (full_lev_fieldy <= SCR_FIELDY)
268 if (EVEN(SCR_FIELDY))
269 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
271 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
277 static int getLevelFromScreenX_RND(int sx)
279 int fx = getFieldbufferOffsetX_RND();
282 int lx = LEVELX((px + dx) / TILESIZE_VAR);
287 static int getLevelFromScreenY_RND(int sy)
289 int fy = getFieldbufferOffsetY_RND();
292 int ly = LEVELY((py + dy) / TILESIZE_VAR);
297 static int getLevelFromScreenX_EM(int sx)
299 int level_xsize = level.native_em_level->lev->width;
300 int full_xsize = level_xsize * TILESIZE_VAR;
302 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
304 int fx = getFieldbufferOffsetX_EM();
307 int lx = LEVELX((px + dx) / TILESIZE_VAR);
309 lx = correctLevelPosX_EM(lx);
314 static int getLevelFromScreenY_EM(int sy)
316 int level_ysize = level.native_em_level->lev->height;
317 int full_ysize = level_ysize * TILESIZE_VAR;
319 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
321 int fy = getFieldbufferOffsetY_EM();
324 int ly = LEVELY((py + dy) / TILESIZE_VAR);
326 ly = correctLevelPosY_EM(ly);
331 static int getLevelFromScreenX_SP(int sx)
333 int menBorder = setup.sp_show_border_elements;
334 int level_xsize = level.native_sp_level->width;
335 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
337 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
339 int fx = getFieldbufferOffsetX_SP();
342 int lx = LEVELX((px + dx) / TILESIZE_VAR);
347 static int getLevelFromScreenY_SP(int sy)
349 int menBorder = setup.sp_show_border_elements;
350 int level_ysize = level.native_sp_level->height;
351 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
353 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
355 int fy = getFieldbufferOffsetY_SP();
358 int ly = LEVELY((py + dy) / TILESIZE_VAR);
363 static int getLevelFromScreenX_MM(int sx)
365 int level_xsize = level.native_mm_level->fieldx;
366 int full_xsize = level_xsize * TILESIZE_VAR;
368 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
371 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
376 static int getLevelFromScreenY_MM(int sy)
378 int level_ysize = level.native_mm_level->fieldy;
379 int full_ysize = level_ysize * TILESIZE_VAR;
381 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
384 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
389 int getLevelFromScreenX(int x)
391 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
392 return getLevelFromScreenX_EM(x);
393 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
394 return getLevelFromScreenX_SP(x);
395 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
396 return getLevelFromScreenX_MM(x);
398 return getLevelFromScreenX_RND(x);
401 int getLevelFromScreenY(int y)
403 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
404 return getLevelFromScreenY_EM(y);
405 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
406 return getLevelFromScreenY_SP(y);
407 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
408 return getLevelFromScreenY_MM(y);
410 return getLevelFromScreenY_RND(y);
413 void DumpTile(int x, int y)
419 printf_line("-", 79);
420 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
421 printf_line("-", 79);
423 if (!IN_LEV_FIELD(x, y))
425 printf("(not in level field)\n");
431 token_name = element_info[Feld[x][y]].token_name;
433 printf(" Feld: %d\t['%s']\n", Feld[x][y], token_name);
434 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
435 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
436 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
437 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
438 printf(" MovPos: %d\n", MovPos[x][y]);
439 printf(" MovDir: %d\n", MovDir[x][y]);
440 printf(" MovDelay: %d\n", MovDelay[x][y]);
441 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
442 printf(" CustomValue: %d\n", CustomValue[x][y]);
443 printf(" GfxElement: %d\n", GfxElement[x][y]);
444 printf(" GfxAction: %d\n", GfxAction[x][y]);
445 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
446 printf(" Player x/y: %d, %d\n", local_player->jx, local_player->jy);
450 void DumpTileFromScreen(int sx, int sy)
452 int lx = getLevelFromScreenX(sx);
453 int ly = getLevelFromScreenY(sy);
458 void SetDrawtoField(int mode)
460 if (mode == DRAW_TO_FIELDBUFFER)
466 BX2 = SCR_FIELDX + 1;
467 BY2 = SCR_FIELDY + 1;
469 drawto_field = fieldbuffer;
471 else // DRAW_TO_BACKBUFFER
477 BX2 = SCR_FIELDX - 1;
478 BY2 = SCR_FIELDY - 1;
480 drawto_field = backbuffer;
484 static void RedrawPlayfield_RND(void)
486 if (game.envelope_active)
489 DrawLevel(REDRAW_ALL);
493 void RedrawPlayfield(void)
495 if (game_status != GAME_MODE_PLAYING)
498 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
499 RedrawPlayfield_EM(TRUE);
500 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
501 RedrawPlayfield_SP(TRUE);
502 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
503 RedrawPlayfield_MM();
504 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
505 RedrawPlayfield_RND();
507 BlitScreenToBitmap(backbuffer);
509 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
513 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
516 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
517 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
519 if (x == -1 && y == -1)
522 if (draw_target == DRAW_TO_SCREEN)
523 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
525 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
528 static void DrawMaskedBorderExt_FIELD(int draw_target)
530 if (global.border_status >= GAME_MODE_MAIN &&
531 global.border_status <= GAME_MODE_PLAYING &&
532 border.draw_masked[global.border_status])
533 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
537 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
539 // when drawing to backbuffer, never draw border over open doors
540 if (draw_target == DRAW_TO_BACKBUFFER &&
541 (GetDoorState() & DOOR_OPEN_1))
544 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
545 (global.border_status != GAME_MODE_EDITOR ||
546 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
547 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
550 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
552 // when drawing to backbuffer, never draw border over open doors
553 if (draw_target == DRAW_TO_BACKBUFFER &&
554 (GetDoorState() & DOOR_OPEN_2))
557 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
558 global.border_status != GAME_MODE_EDITOR)
559 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
562 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
564 // currently not available
567 static void DrawMaskedBorderExt_ALL(int draw_target)
569 DrawMaskedBorderExt_FIELD(draw_target);
570 DrawMaskedBorderExt_DOOR_1(draw_target);
571 DrawMaskedBorderExt_DOOR_2(draw_target);
572 DrawMaskedBorderExt_DOOR_3(draw_target);
575 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
577 // never draw masked screen borders on borderless screens
578 if (global.border_status == GAME_MODE_LOADING ||
579 global.border_status == GAME_MODE_TITLE)
582 if (redraw_mask & REDRAW_ALL)
583 DrawMaskedBorderExt_ALL(draw_target);
586 if (redraw_mask & REDRAW_FIELD)
587 DrawMaskedBorderExt_FIELD(draw_target);
588 if (redraw_mask & REDRAW_DOOR_1)
589 DrawMaskedBorderExt_DOOR_1(draw_target);
590 if (redraw_mask & REDRAW_DOOR_2)
591 DrawMaskedBorderExt_DOOR_2(draw_target);
592 if (redraw_mask & REDRAW_DOOR_3)
593 DrawMaskedBorderExt_DOOR_3(draw_target);
597 void DrawMaskedBorder_FIELD(void)
599 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
602 void DrawMaskedBorder(int redraw_mask)
604 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
607 void DrawMaskedBorderToTarget(int draw_target)
609 if (draw_target == DRAW_TO_BACKBUFFER ||
610 draw_target == DRAW_TO_SCREEN)
612 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
616 int last_border_status = global.border_status;
618 if (draw_target == DRAW_TO_FADE_SOURCE)
620 global.border_status = gfx.fade_border_source_status;
621 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
623 else if (draw_target == DRAW_TO_FADE_TARGET)
625 global.border_status = gfx.fade_border_target_status;
626 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
629 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
631 global.border_status = last_border_status;
632 gfx.masked_border_bitmap_ptr = backbuffer;
636 void DrawTileCursor(int draw_target)
642 int graphic = IMG_GLOBAL_TILE_CURSOR;
644 int tilesize = TILESIZE_VAR;
645 int width = tilesize;
646 int height = tilesize;
648 if (game_status != GAME_MODE_PLAYING)
651 if (!tile_cursor.enabled ||
655 if (tile_cursor.moving)
657 int step = TILESIZE_VAR / 4;
658 int dx = tile_cursor.target_x - tile_cursor.x;
659 int dy = tile_cursor.target_y - tile_cursor.y;
662 tile_cursor.x = tile_cursor.target_x;
664 tile_cursor.x += SIGN(dx) * step;
667 tile_cursor.y = tile_cursor.target_y;
669 tile_cursor.y += SIGN(dy) * step;
671 if (tile_cursor.x == tile_cursor.target_x &&
672 tile_cursor.y == tile_cursor.target_y)
673 tile_cursor.moving = FALSE;
676 dst_x = tile_cursor.x;
677 dst_y = tile_cursor.y;
679 frame = getGraphicAnimationFrame(graphic, -1);
681 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
684 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
685 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
687 if (draw_target == DRAW_TO_SCREEN)
688 BlitToScreenMasked(src_bitmap, src_x, src_y, width, height, dst_x, dst_y);
690 BlitBitmapMasked(src_bitmap, fade_bitmap, src_x, src_y, width, height,
694 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
696 int fx = getFieldbufferOffsetX_RND();
697 int fy = getFieldbufferOffsetY_RND();
699 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
702 void BlitScreenToBitmap(Bitmap *target_bitmap)
704 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
705 BlitScreenToBitmap_EM(target_bitmap);
706 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
707 BlitScreenToBitmap_SP(target_bitmap);
708 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
709 BlitScreenToBitmap_MM(target_bitmap);
710 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
711 BlitScreenToBitmap_RND(target_bitmap);
713 redraw_mask |= REDRAW_FIELD;
716 static void DrawFramesPerSecond(void)
719 int font_nr = FONT_TEXT_2;
720 int font_width = getFontWidth(font_nr);
721 int draw_deactivation_mask = GetDrawDeactivationMask();
722 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
724 // draw FPS with leading space (needed if field buffer deactivated)
725 sprintf(text, " %04.1f fps", global.frames_per_second);
727 // override draw deactivation mask (required for invisible warp mode)
728 SetDrawDeactivationMask(REDRAW_NONE);
730 // draw opaque FPS if field buffer deactivated, else draw masked FPS
731 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
732 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
734 // set draw deactivation mask to previous value
735 SetDrawDeactivationMask(draw_deactivation_mask);
737 // force full-screen redraw in this frame
738 redraw_mask = REDRAW_ALL;
742 static void PrintFrameTimeDebugging(void)
744 static unsigned int last_counter = 0;
745 unsigned int counter = Counter();
746 int diff_1 = counter - last_counter;
747 int diff_2 = diff_1 - GAME_FRAME_DELAY;
749 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
750 char diff_bar[2 * diff_2_max + 5];
754 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
756 for (i = 0; i < diff_2_max; i++)
757 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
758 i >= diff_2_max - diff_2_cut ? '-' : ' ');
760 diff_bar[pos++] = '|';
762 for (i = 0; i < diff_2_max; i++)
763 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
765 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
767 diff_bar[pos++] = '\0';
769 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
772 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
775 last_counter = counter;
779 static int unifiedRedrawMask(int mask)
781 if (mask & REDRAW_ALL)
784 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
790 static boolean equalRedrawMasks(int mask_1, int mask_2)
792 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
795 void BackToFront(void)
797 static int last_redraw_mask = REDRAW_NONE;
799 // force screen redraw in every frame to continue drawing global animations
800 // (but always use the last redraw mask to prevent unwanted side effects)
801 if (redraw_mask == REDRAW_NONE)
802 redraw_mask = last_redraw_mask;
804 last_redraw_mask = redraw_mask;
807 // masked border now drawn immediately when blitting backbuffer to window
809 // draw masked border to all viewports, if defined
810 DrawMaskedBorder(redraw_mask);
813 // draw frames per second (only if debug mode is enabled)
814 if (redraw_mask & REDRAW_FPS)
815 DrawFramesPerSecond();
817 // remove playfield redraw before potentially merging with doors redraw
818 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
819 redraw_mask &= ~REDRAW_FIELD;
821 // redraw complete window if both playfield and (some) doors need redraw
822 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
823 redraw_mask = REDRAW_ALL;
825 /* although redrawing the whole window would be fine for normal gameplay,
826 being able to only redraw the playfield is required for deactivating
827 certain drawing areas (mainly playfield) to work, which is needed for
828 warp-forward to be fast enough (by skipping redraw of most frames) */
830 if (redraw_mask & REDRAW_ALL)
832 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
834 else if (redraw_mask & REDRAW_FIELD)
836 BlitBitmap(backbuffer, window,
837 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
839 else if (redraw_mask & REDRAW_DOORS)
841 // merge door areas to prevent calling screen redraw more than once
847 if (redraw_mask & REDRAW_DOOR_1)
851 x2 = MAX(x2, DX + DXSIZE);
852 y2 = MAX(y2, DY + DYSIZE);
855 if (redraw_mask & REDRAW_DOOR_2)
859 x2 = MAX(x2, VX + VXSIZE);
860 y2 = MAX(y2, VY + VYSIZE);
863 if (redraw_mask & REDRAW_DOOR_3)
867 x2 = MAX(x2, EX + EXSIZE);
868 y2 = MAX(y2, EY + EYSIZE);
871 // make sure that at least one pixel is blitted, and inside the screen
872 // (else nothing is blitted, causing the animations not to be updated)
873 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
874 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
875 x2 = MIN(MAX(1, x2), WIN_XSIZE);
876 y2 = MIN(MAX(1, y2), WIN_YSIZE);
878 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
881 redraw_mask = REDRAW_NONE;
884 PrintFrameTimeDebugging();
888 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
890 unsigned int frame_delay_value_old = GetVideoFrameDelay();
892 SetVideoFrameDelay(frame_delay_value);
896 SetVideoFrameDelay(frame_delay_value_old);
899 static int fade_type_skip = FADE_TYPE_NONE;
901 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
903 void (*draw_border_function)(void) = NULL;
904 int x, y, width, height;
905 int fade_delay, post_delay;
907 if (fade_type == FADE_TYPE_FADE_OUT)
909 if (fade_type_skip != FADE_TYPE_NONE)
911 // skip all fade operations until specified fade operation
912 if (fade_type & fade_type_skip)
913 fade_type_skip = FADE_TYPE_NONE;
918 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
922 redraw_mask |= fade_mask;
924 if (fade_type == FADE_TYPE_SKIP)
926 fade_type_skip = fade_mode;
931 fade_delay = fading.fade_delay;
932 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
934 if (fade_type_skip != FADE_TYPE_NONE)
936 // skip all fade operations until specified fade operation
937 if (fade_type & fade_type_skip)
938 fade_type_skip = FADE_TYPE_NONE;
943 if (global.autoplay_leveldir)
948 if (fade_mask == REDRAW_FIELD)
953 height = FADE_SYSIZE;
955 if (border.draw_masked_when_fading)
956 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
958 DrawMaskedBorder_FIELD(); // draw once
968 if (!setup.fade_screens ||
970 fading.fade_mode == FADE_MODE_NONE)
972 if (fade_mode == FADE_MODE_FADE_OUT)
975 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
977 redraw_mask &= ~fade_mask;
982 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
983 draw_border_function);
985 redraw_mask &= ~fade_mask;
987 ClearAutoRepeatKeyEvents();
990 static void SetScreenStates_BeforeFadingIn(void)
992 // temporarily set screen mode for animations to screen after fading in
993 global.anim_status = global.anim_status_next;
995 // store backbuffer with all animations that will be started after fading in
996 if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
997 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
999 // set screen mode for animations back to fading
1000 global.anim_status = GAME_MODE_PSEUDO_FADING;
1003 static void SetScreenStates_AfterFadingIn(void)
1005 // store new source screen (to use correct masked border for fading)
1006 gfx.fade_border_source_status = global.border_status;
1008 global.anim_status = global.anim_status_next;
1011 static void SetScreenStates_BeforeFadingOut(void)
1013 // store new target screen (to use correct masked border for fading)
1014 gfx.fade_border_target_status = game_status;
1016 // set screen mode for animations to fading
1017 global.anim_status = GAME_MODE_PSEUDO_FADING;
1019 // store backbuffer with all animations that will be stopped for fading out
1020 if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1021 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
1024 static void SetScreenStates_AfterFadingOut(void)
1026 global.border_status = game_status;
1029 void FadeIn(int fade_mask)
1031 SetScreenStates_BeforeFadingIn();
1034 DrawMaskedBorder(REDRAW_ALL);
1037 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1038 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1040 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1044 FADE_SXSIZE = FULL_SXSIZE;
1045 FADE_SYSIZE = FULL_SYSIZE;
1047 // activate virtual buttons depending on upcoming game status
1048 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1049 game_status == GAME_MODE_PLAYING && !tape.playing)
1050 SetOverlayActive(TRUE);
1052 SetScreenStates_AfterFadingIn();
1054 // force update of global animation status in case of rapid screen changes
1055 redraw_mask = REDRAW_ALL;
1059 void FadeOut(int fade_mask)
1061 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1062 if (!equalRedrawMasks(fade_mask, redraw_mask))
1065 SetScreenStates_BeforeFadingOut();
1067 SetTileCursorActive(FALSE);
1068 SetOverlayActive(FALSE);
1071 DrawMaskedBorder(REDRAW_ALL);
1074 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1075 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1077 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1079 SetScreenStates_AfterFadingOut();
1082 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1084 static struct TitleFadingInfo fading_leave_stored;
1087 fading_leave_stored = fading_leave;
1089 fading = fading_leave_stored;
1092 void FadeSetEnterMenu(void)
1094 fading = menu.enter_menu;
1096 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1099 void FadeSetLeaveMenu(void)
1101 fading = menu.leave_menu;
1103 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1106 void FadeSetEnterScreen(void)
1108 fading = menu.enter_screen[game_status];
1110 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1113 void FadeSetNextScreen(void)
1115 fading = menu.next_screen[game_status];
1117 // (do not overwrite fade mode set by FadeSetEnterScreen)
1118 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1121 void FadeSetLeaveScreen(void)
1123 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1126 void FadeSetFromType(int type)
1128 if (type & TYPE_ENTER_SCREEN)
1129 FadeSetEnterScreen();
1130 else if (type & TYPE_ENTER)
1132 else if (type & TYPE_LEAVE)
1136 void FadeSetDisabled(void)
1138 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1140 fading = fading_none;
1143 void FadeSkipNextFadeIn(void)
1145 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1148 void FadeSkipNextFadeOut(void)
1150 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1153 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1155 if (graphic == IMG_UNDEFINED)
1158 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1160 return (graphic_info[graphic].bitmap != NULL || redefined ?
1161 graphic_info[graphic].bitmap :
1162 graphic_info[default_graphic].bitmap);
1165 static Bitmap *getBackgroundBitmap(int graphic)
1167 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1170 static Bitmap *getGlobalBorderBitmap(int graphic)
1172 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1175 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1178 (status == GAME_MODE_MAIN ||
1179 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1180 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1181 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1182 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1185 return getGlobalBorderBitmap(graphic);
1188 void SetWindowBackgroundImageIfDefined(int graphic)
1190 if (graphic_info[graphic].bitmap)
1191 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1194 void SetMainBackgroundImageIfDefined(int graphic)
1196 if (graphic_info[graphic].bitmap)
1197 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1200 void SetDoorBackgroundImageIfDefined(int graphic)
1202 if (graphic_info[graphic].bitmap)
1203 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1206 void SetWindowBackgroundImage(int graphic)
1208 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1211 void SetMainBackgroundImage(int graphic)
1213 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1216 void SetDoorBackgroundImage(int graphic)
1218 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1221 void SetPanelBackground(void)
1223 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1225 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1226 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1228 SetDoorBackgroundBitmap(bitmap_db_panel);
1231 void DrawBackground(int x, int y, int width, int height)
1233 // "drawto" might still point to playfield buffer here (hall of fame)
1234 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1236 if (IN_GFX_FIELD_FULL(x, y))
1237 redraw_mask |= REDRAW_FIELD;
1238 else if (IN_GFX_DOOR_1(x, y))
1239 redraw_mask |= REDRAW_DOOR_1;
1240 else if (IN_GFX_DOOR_2(x, y))
1241 redraw_mask |= REDRAW_DOOR_2;
1242 else if (IN_GFX_DOOR_3(x, y))
1243 redraw_mask |= REDRAW_DOOR_3;
1246 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1248 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1250 if (font->bitmap == NULL)
1253 DrawBackground(x, y, width, height);
1256 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1258 struct GraphicInfo *g = &graphic_info[graphic];
1260 if (g->bitmap == NULL)
1263 DrawBackground(x, y, width, height);
1266 static int game_status_last = -1;
1267 static Bitmap *global_border_bitmap_last = NULL;
1268 static Bitmap *global_border_bitmap = NULL;
1269 static int real_sx_last = -1, real_sy_last = -1;
1270 static int full_sxsize_last = -1, full_sysize_last = -1;
1271 static int dx_last = -1, dy_last = -1;
1272 static int dxsize_last = -1, dysize_last = -1;
1273 static int vx_last = -1, vy_last = -1;
1274 static int vxsize_last = -1, vysize_last = -1;
1275 static int ex_last = -1, ey_last = -1;
1276 static int exsize_last = -1, eysize_last = -1;
1278 boolean CheckIfGlobalBorderHasChanged(void)
1280 // if game status has not changed, global border has not changed either
1281 if (game_status == game_status_last)
1284 // determine and store new global border bitmap for current game status
1285 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1287 return (global_border_bitmap_last != global_border_bitmap);
1290 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1292 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1293 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1295 // if game status has not changed, nothing has to be redrawn
1296 if (game_status == game_status_last)
1299 // redraw if last screen was title screen
1300 if (game_status_last == GAME_MODE_TITLE)
1303 // redraw if global screen border has changed
1304 if (CheckIfGlobalBorderHasChanged())
1307 // redraw if position or size of playfield area has changed
1308 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1309 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1312 // redraw if position or size of door area has changed
1313 if (dx_last != DX || dy_last != DY ||
1314 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1317 // redraw if position or size of tape area has changed
1318 if (vx_last != VX || vy_last != VY ||
1319 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1322 // redraw if position or size of editor area has changed
1323 if (ex_last != EX || ey_last != EY ||
1324 exsize_last != EXSIZE || eysize_last != EYSIZE)
1331 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1334 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1336 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1339 void RedrawGlobalBorder(void)
1341 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1343 RedrawGlobalBorderFromBitmap(bitmap);
1345 redraw_mask = REDRAW_ALL;
1348 static void RedrawGlobalBorderIfNeeded(void)
1350 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1351 if (game_status == game_status_last)
1355 // copy current draw buffer to later copy back areas that have not changed
1356 if (game_status_last != GAME_MODE_TITLE)
1357 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1359 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1360 if (CheckIfGlobalBorderRedrawIsNeeded())
1363 // redraw global screen border (or clear, if defined to be empty)
1364 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1366 if (game_status == GAME_MODE_EDITOR)
1367 DrawSpecialEditorDoor();
1369 // copy previous playfield and door areas, if they are defined on both
1370 // previous and current screen and if they still have the same size
1372 if (real_sx_last != -1 && real_sy_last != -1 &&
1373 REAL_SX != -1 && REAL_SY != -1 &&
1374 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1375 BlitBitmap(bitmap_db_store_1, backbuffer,
1376 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1379 if (dx_last != -1 && dy_last != -1 &&
1380 DX != -1 && DY != -1 &&
1381 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1382 BlitBitmap(bitmap_db_store_1, backbuffer,
1383 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1385 if (game_status != GAME_MODE_EDITOR)
1387 if (vx_last != -1 && vy_last != -1 &&
1388 VX != -1 && VY != -1 &&
1389 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1390 BlitBitmap(bitmap_db_store_1, backbuffer,
1391 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1395 if (ex_last != -1 && ey_last != -1 &&
1396 EX != -1 && EY != -1 &&
1397 exsize_last == EXSIZE && eysize_last == EYSIZE)
1398 BlitBitmap(bitmap_db_store_1, backbuffer,
1399 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1402 redraw_mask = REDRAW_ALL;
1405 game_status_last = game_status;
1407 global_border_bitmap_last = global_border_bitmap;
1409 real_sx_last = REAL_SX;
1410 real_sy_last = REAL_SY;
1411 full_sxsize_last = FULL_SXSIZE;
1412 full_sysize_last = FULL_SYSIZE;
1415 dxsize_last = DXSIZE;
1416 dysize_last = DYSIZE;
1419 vxsize_last = VXSIZE;
1420 vysize_last = VYSIZE;
1423 exsize_last = EXSIZE;
1424 eysize_last = EYSIZE;
1427 void ClearField(void)
1429 RedrawGlobalBorderIfNeeded();
1431 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1432 // (when entering hall of fame after playing)
1433 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1435 // !!! maybe this should be done before clearing the background !!!
1436 if (game_status == GAME_MODE_PLAYING)
1438 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1439 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1443 SetDrawtoField(DRAW_TO_BACKBUFFER);
1447 void MarkTileDirty(int x, int y)
1449 redraw_mask |= REDRAW_FIELD;
1452 void SetBorderElement(void)
1456 BorderElement = EL_EMPTY;
1458 // the MM game engine does not use a visible border element
1459 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1462 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1464 for (x = 0; x < lev_fieldx; x++)
1466 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1467 BorderElement = EL_STEELWALL;
1469 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1475 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1476 int max_array_fieldx, int max_array_fieldy,
1477 short field[max_array_fieldx][max_array_fieldy],
1478 int max_fieldx, int max_fieldy)
1482 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1483 static int safety = 0;
1485 // check if starting field still has the desired content
1486 if (field[from_x][from_y] == fill_element)
1491 if (safety > max_fieldx * max_fieldy)
1492 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1494 old_element = field[from_x][from_y];
1495 field[from_x][from_y] = fill_element;
1497 for (i = 0; i < 4; i++)
1499 x = from_x + check[i][0];
1500 y = from_y + check[i][1];
1502 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1503 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1504 field, max_fieldx, max_fieldy);
1510 void FloodFillLevel(int from_x, int from_y, int fill_element,
1511 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1512 int max_fieldx, int max_fieldy)
1514 FloodFillLevelExt(from_x, from_y, fill_element,
1515 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1516 max_fieldx, max_fieldy);
1519 void SetRandomAnimationValue(int x, int y)
1521 gfx.anim_random_frame = GfxRandom[x][y];
1524 int getGraphicAnimationFrame(int graphic, int sync_frame)
1526 // animation synchronized with global frame counter, not move position
1527 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1528 sync_frame = FrameCounter;
1530 return getAnimationFrame(graphic_info[graphic].anim_frames,
1531 graphic_info[graphic].anim_delay,
1532 graphic_info[graphic].anim_mode,
1533 graphic_info[graphic].anim_start_frame,
1537 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1539 struct GraphicInfo *g = &graphic_info[graphic];
1540 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1542 if (tilesize == gfx.standard_tile_size)
1543 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1544 else if (tilesize == game.tile_size)
1545 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1547 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1550 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1551 boolean get_backside)
1553 struct GraphicInfo *g = &graphic_info[graphic];
1554 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1555 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1557 if (g->offset_y == 0) // frames are ordered horizontally
1559 int max_width = g->anim_frames_per_line * g->width;
1560 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1562 *x = pos % max_width;
1563 *y = src_y % g->height + pos / max_width * g->height;
1565 else if (g->offset_x == 0) // frames are ordered vertically
1567 int max_height = g->anim_frames_per_line * g->height;
1568 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1570 *x = src_x % g->width + pos / max_height * g->width;
1571 *y = pos % max_height;
1573 else // frames are ordered diagonally
1575 *x = src_x + frame * g->offset_x;
1576 *y = src_y + frame * g->offset_y;
1580 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1581 Bitmap **bitmap, int *x, int *y,
1582 boolean get_backside)
1584 struct GraphicInfo *g = &graphic_info[graphic];
1586 // if no graphics defined at all, use fallback graphics
1587 if (g->bitmaps == NULL)
1588 *g = graphic_info[IMG_CHAR_EXCLAM];
1590 // if no in-game graphics defined, always use standard graphic size
1591 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1592 tilesize = TILESIZE;
1594 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1595 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1597 *x = *x * tilesize / g->tile_size;
1598 *y = *y * tilesize / g->tile_size;
1601 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1602 Bitmap **bitmap, int *x, int *y)
1604 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1607 void getFixedGraphicSource(int graphic, int frame,
1608 Bitmap **bitmap, int *x, int *y)
1610 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1613 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1615 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1618 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1619 int *x, int *y, boolean get_backside)
1621 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1625 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1627 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1630 void DrawGraphic(int x, int y, int graphic, int frame)
1633 if (!IN_SCR_FIELD(x, y))
1635 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1636 printf("DrawGraphic(): This should never happen!\n");
1641 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1644 MarkTileDirty(x, y);
1647 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1650 if (!IN_SCR_FIELD(x, y))
1652 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1653 printf("DrawGraphic(): This should never happen!\n");
1658 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1660 MarkTileDirty(x, y);
1663 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1669 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1671 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1674 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1680 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1681 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1684 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1687 if (!IN_SCR_FIELD(x, y))
1689 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1690 printf("DrawGraphicThruMask(): This should never happen!\n");
1695 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1698 MarkTileDirty(x, y);
1701 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1704 if (!IN_SCR_FIELD(x, y))
1706 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1707 printf("DrawGraphicThruMask(): This should never happen!\n");
1712 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1714 MarkTileDirty(x, y);
1717 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1723 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1725 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1729 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1730 int graphic, int frame)
1735 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1737 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1741 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1743 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1745 MarkTileDirty(x / tilesize, y / tilesize);
1748 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1751 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1752 graphic, frame, tilesize);
1753 MarkTileDirty(x / tilesize, y / tilesize);
1756 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1762 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1763 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1766 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1767 int frame, int tilesize)
1772 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1773 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1776 void DrawMiniGraphic(int x, int y, int graphic)
1778 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1779 MarkTileDirty(x / 2, y / 2);
1782 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1787 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1788 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1791 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1792 int graphic, int frame,
1793 int cut_mode, int mask_mode)
1798 int width = TILEX, height = TILEY;
1801 if (dx || dy) // shifted graphic
1803 if (x < BX1) // object enters playfield from the left
1810 else if (x > BX2) // object enters playfield from the right
1816 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1822 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1824 else if (dx) // general horizontal movement
1825 MarkTileDirty(x + SIGN(dx), y);
1827 if (y < BY1) // object enters playfield from the top
1829 if (cut_mode == CUT_BELOW) // object completely above top border
1837 else if (y > BY2) // object enters playfield from the bottom
1843 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1849 else if (dy > 0 && cut_mode == CUT_ABOVE)
1851 if (y == BY2) // object completely above bottom border
1857 MarkTileDirty(x, y + 1);
1858 } // object leaves playfield to the bottom
1859 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1861 else if (dy) // general vertical movement
1862 MarkTileDirty(x, y + SIGN(dy));
1866 if (!IN_SCR_FIELD(x, y))
1868 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1869 printf("DrawGraphicShifted(): This should never happen!\n");
1874 width = width * TILESIZE_VAR / TILESIZE;
1875 height = height * TILESIZE_VAR / TILESIZE;
1876 cx = cx * TILESIZE_VAR / TILESIZE;
1877 cy = cy * TILESIZE_VAR / TILESIZE;
1878 dx = dx * TILESIZE_VAR / TILESIZE;
1879 dy = dy * TILESIZE_VAR / TILESIZE;
1881 if (width > 0 && height > 0)
1883 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1888 dst_x = FX + x * TILEX_VAR + dx;
1889 dst_y = FY + y * TILEY_VAR + dy;
1891 if (mask_mode == USE_MASKING)
1892 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1895 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1898 MarkTileDirty(x, y);
1902 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1903 int graphic, int frame,
1904 int cut_mode, int mask_mode)
1909 int width = TILEX_VAR, height = TILEY_VAR;
1912 int x2 = x + SIGN(dx);
1913 int y2 = y + SIGN(dy);
1915 // movement with two-tile animations must be sync'ed with movement position,
1916 // not with current GfxFrame (which can be higher when using slow movement)
1917 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1918 int anim_frames = graphic_info[graphic].anim_frames;
1920 // (we also need anim_delay here for movement animations with less frames)
1921 int anim_delay = graphic_info[graphic].anim_delay;
1922 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1924 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1925 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1927 // re-calculate animation frame for two-tile movement animation
1928 frame = getGraphicAnimationFrame(graphic, sync_frame);
1930 // check if movement start graphic inside screen area and should be drawn
1931 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1933 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1935 dst_x = FX + x1 * TILEX_VAR;
1936 dst_y = FY + y1 * TILEY_VAR;
1938 if (mask_mode == USE_MASKING)
1939 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1942 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1945 MarkTileDirty(x1, y1);
1948 // check if movement end graphic inside screen area and should be drawn
1949 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1951 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1953 dst_x = FX + x2 * TILEX_VAR;
1954 dst_y = FY + y2 * TILEY_VAR;
1956 if (mask_mode == USE_MASKING)
1957 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1960 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1963 MarkTileDirty(x2, y2);
1967 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1968 int graphic, int frame,
1969 int cut_mode, int mask_mode)
1973 DrawGraphic(x, y, graphic, frame);
1978 if (graphic_info[graphic].double_movement) // EM style movement images
1979 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1981 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1984 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1985 int graphic, int frame, int cut_mode)
1987 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1990 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1991 int cut_mode, int mask_mode)
1993 int lx = LEVELX(x), ly = LEVELY(y);
1997 if (IN_LEV_FIELD(lx, ly))
1999 SetRandomAnimationValue(lx, ly);
2001 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
2002 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2004 // do not use double (EM style) movement graphic when not moving
2005 if (graphic_info[graphic].double_movement && !dx && !dy)
2007 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2008 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2011 else // border element
2013 graphic = el2img(element);
2014 frame = getGraphicAnimationFrame(graphic, -1);
2017 if (element == EL_EXPANDABLE_WALL)
2019 boolean left_stopped = FALSE, right_stopped = FALSE;
2021 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
2022 left_stopped = TRUE;
2023 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
2024 right_stopped = TRUE;
2026 if (left_stopped && right_stopped)
2028 else if (left_stopped)
2030 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2031 frame = graphic_info[graphic].anim_frames - 1;
2033 else if (right_stopped)
2035 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2036 frame = graphic_info[graphic].anim_frames - 1;
2041 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2042 else if (mask_mode == USE_MASKING)
2043 DrawGraphicThruMask(x, y, graphic, frame);
2045 DrawGraphic(x, y, graphic, frame);
2048 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2049 int cut_mode, int mask_mode)
2051 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2052 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2053 cut_mode, mask_mode);
2056 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2059 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2062 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2065 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2068 void DrawLevelElementThruMask(int x, int y, int element)
2070 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2073 void DrawLevelFieldThruMask(int x, int y)
2075 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
2078 // !!! implementation of quicksand is totally broken !!!
2079 #define IS_CRUMBLED_TILE(x, y, e) \
2080 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2081 !IS_MOVING(x, y) || \
2082 (e) == EL_QUICKSAND_EMPTYING || \
2083 (e) == EL_QUICKSAND_FAST_EMPTYING))
2085 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2090 int width, height, cx, cy;
2091 int sx = SCREENX(x), sy = SCREENY(y);
2092 int crumbled_border_size = graphic_info[graphic].border_size;
2093 int crumbled_tile_size = graphic_info[graphic].tile_size;
2094 int crumbled_border_size_var =
2095 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2098 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2100 for (i = 1; i < 4; i++)
2102 int dxx = (i & 1 ? dx : 0);
2103 int dyy = (i & 2 ? dy : 0);
2106 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2109 // check if neighbour field is of same crumble type
2110 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2111 graphic_info[graphic].class ==
2112 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2114 // return if check prevents inner corner
2115 if (same == (dxx == dx && dyy == dy))
2119 // if we reach this point, we have an inner corner
2121 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2123 width = crumbled_border_size_var;
2124 height = crumbled_border_size_var;
2125 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2126 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2128 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2129 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2132 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2137 int width, height, bx, by, cx, cy;
2138 int sx = SCREENX(x), sy = SCREENY(y);
2139 int crumbled_border_size = graphic_info[graphic].border_size;
2140 int crumbled_tile_size = graphic_info[graphic].tile_size;
2141 int crumbled_border_size_var =
2142 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2143 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2146 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2148 // draw simple, sloppy, non-corner-accurate crumbled border
2150 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2151 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2152 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2153 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2155 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2156 FX + sx * TILEX_VAR + cx,
2157 FY + sy * TILEY_VAR + cy);
2159 // (remaining middle border part must be at least as big as corner part)
2160 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2161 crumbled_border_size_var >= TILESIZE_VAR / 3)
2164 // correct corners of crumbled border, if needed
2166 for (i = -1; i <= 1; i += 2)
2168 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2169 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2170 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2173 // check if neighbour field is of same crumble type
2174 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2175 graphic_info[graphic].class ==
2176 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2178 // no crumbled corner, but continued crumbled border
2180 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2181 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2182 int b1 = (i == 1 ? crumbled_border_size_var :
2183 TILESIZE_VAR - 2 * crumbled_border_size_var);
2185 width = crumbled_border_size_var;
2186 height = crumbled_border_size_var;
2188 if (dir == 1 || dir == 2)
2203 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2205 FX + sx * TILEX_VAR + cx,
2206 FY + sy * TILEY_VAR + cy);
2211 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2213 int sx = SCREENX(x), sy = SCREENY(y);
2216 static int xy[4][2] =
2224 if (!IN_LEV_FIELD(x, y))
2227 element = TILE_GFX_ELEMENT(x, y);
2229 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2231 if (!IN_SCR_FIELD(sx, sy))
2234 // crumble field borders towards direct neighbour fields
2235 for (i = 0; i < 4; i++)
2237 int xx = x + xy[i][0];
2238 int yy = y + xy[i][1];
2240 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2243 // check if neighbour field is of same crumble type
2244 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2245 graphic_info[graphic].class ==
2246 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2249 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2252 // crumble inner field corners towards corner neighbour fields
2253 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2254 graphic_info[graphic].anim_frames == 2)
2256 for (i = 0; i < 4; i++)
2258 int dx = (i & 1 ? +1 : -1);
2259 int dy = (i & 2 ? +1 : -1);
2261 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2265 MarkTileDirty(sx, sy);
2267 else // center field is not crumbled -- crumble neighbour fields
2269 // crumble field borders of direct neighbour fields
2270 for (i = 0; i < 4; i++)
2272 int xx = x + xy[i][0];
2273 int yy = y + xy[i][1];
2274 int sxx = sx + xy[i][0];
2275 int syy = sy + xy[i][1];
2277 if (!IN_LEV_FIELD(xx, yy) ||
2278 !IN_SCR_FIELD(sxx, syy))
2281 // do not crumble fields that are being digged or snapped
2282 if (Feld[xx][yy] == EL_EMPTY ||
2283 Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2286 element = TILE_GFX_ELEMENT(xx, yy);
2288 if (!IS_CRUMBLED_TILE(xx, yy, element))
2291 graphic = el_act2crm(element, ACTION_DEFAULT);
2293 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2295 MarkTileDirty(sxx, syy);
2298 // crumble inner field corners of corner neighbour fields
2299 for (i = 0; i < 4; i++)
2301 int dx = (i & 1 ? +1 : -1);
2302 int dy = (i & 2 ? +1 : -1);
2308 if (!IN_LEV_FIELD(xx, yy) ||
2309 !IN_SCR_FIELD(sxx, syy))
2312 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2315 element = TILE_GFX_ELEMENT(xx, yy);
2317 if (!IS_CRUMBLED_TILE(xx, yy, element))
2320 graphic = el_act2crm(element, ACTION_DEFAULT);
2322 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2323 graphic_info[graphic].anim_frames == 2)
2324 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2326 MarkTileDirty(sxx, syy);
2331 void DrawLevelFieldCrumbled(int x, int y)
2335 if (!IN_LEV_FIELD(x, y))
2338 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2339 GfxElement[x][y] != EL_UNDEFINED &&
2340 GFX_CRUMBLED(GfxElement[x][y]))
2342 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2347 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2349 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2352 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2355 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2356 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2357 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2358 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2359 int sx = SCREENX(x), sy = SCREENY(y);
2361 DrawGraphic(sx, sy, graphic1, frame1);
2362 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2365 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2367 int sx = SCREENX(x), sy = SCREENY(y);
2368 static int xy[4][2] =
2377 // crumble direct neighbour fields (required for field borders)
2378 for (i = 0; i < 4; i++)
2380 int xx = x + xy[i][0];
2381 int yy = y + xy[i][1];
2382 int sxx = sx + xy[i][0];
2383 int syy = sy + xy[i][1];
2385 if (!IN_LEV_FIELD(xx, yy) ||
2386 !IN_SCR_FIELD(sxx, syy) ||
2387 !GFX_CRUMBLED(Feld[xx][yy]) ||
2391 DrawLevelField(xx, yy);
2394 // crumble corner neighbour fields (required for inner field corners)
2395 for (i = 0; i < 4; i++)
2397 int dx = (i & 1 ? +1 : -1);
2398 int dy = (i & 2 ? +1 : -1);
2404 if (!IN_LEV_FIELD(xx, yy) ||
2405 !IN_SCR_FIELD(sxx, syy) ||
2406 !GFX_CRUMBLED(Feld[xx][yy]) ||
2410 int element = TILE_GFX_ELEMENT(xx, yy);
2411 int graphic = el_act2crm(element, ACTION_DEFAULT);
2413 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2414 graphic_info[graphic].anim_frames == 2)
2415 DrawLevelField(xx, yy);
2419 static int getBorderElement(int x, int y)
2423 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2424 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2425 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2426 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2427 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2428 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2429 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2431 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2432 int steel_position = (x == -1 && y == -1 ? 0 :
2433 x == lev_fieldx && y == -1 ? 1 :
2434 x == -1 && y == lev_fieldy ? 2 :
2435 x == lev_fieldx && y == lev_fieldy ? 3 :
2436 x == -1 || x == lev_fieldx ? 4 :
2437 y == -1 || y == lev_fieldy ? 5 : 6);
2439 return border[steel_position][steel_type];
2442 void DrawScreenElement(int x, int y, int element)
2444 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2445 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2448 void DrawLevelElement(int x, int y, int element)
2450 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2451 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2454 void DrawScreenField(int x, int y)
2456 int lx = LEVELX(x), ly = LEVELY(y);
2457 int element, content;
2459 if (!IN_LEV_FIELD(lx, ly))
2461 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2464 element = getBorderElement(lx, ly);
2466 DrawScreenElement(x, y, element);
2471 element = Feld[lx][ly];
2472 content = Store[lx][ly];
2474 if (IS_MOVING(lx, ly))
2476 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2477 boolean cut_mode = NO_CUTTING;
2479 if (element == EL_QUICKSAND_EMPTYING ||
2480 element == EL_QUICKSAND_FAST_EMPTYING ||
2481 element == EL_MAGIC_WALL_EMPTYING ||
2482 element == EL_BD_MAGIC_WALL_EMPTYING ||
2483 element == EL_DC_MAGIC_WALL_EMPTYING ||
2484 element == EL_AMOEBA_DROPPING)
2485 cut_mode = CUT_ABOVE;
2486 else if (element == EL_QUICKSAND_FILLING ||
2487 element == EL_QUICKSAND_FAST_FILLING ||
2488 element == EL_MAGIC_WALL_FILLING ||
2489 element == EL_BD_MAGIC_WALL_FILLING ||
2490 element == EL_DC_MAGIC_WALL_FILLING)
2491 cut_mode = CUT_BELOW;
2493 if (cut_mode == CUT_ABOVE)
2494 DrawScreenElement(x, y, element);
2496 DrawScreenElement(x, y, EL_EMPTY);
2499 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2500 else if (cut_mode == NO_CUTTING)
2501 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2504 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2506 if (cut_mode == CUT_BELOW &&
2507 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2508 DrawLevelElement(lx, ly + 1, element);
2511 if (content == EL_ACID)
2513 int dir = MovDir[lx][ly];
2514 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2515 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2517 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2519 // prevent target field from being drawn again (but without masking)
2520 // (this would happen if target field is scanned after moving element)
2521 Stop[newlx][newly] = TRUE;
2524 else if (IS_BLOCKED(lx, ly))
2529 boolean cut_mode = NO_CUTTING;
2530 int element_old, content_old;
2532 Blocked2Moving(lx, ly, &oldx, &oldy);
2535 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2536 MovDir[oldx][oldy] == MV_RIGHT);
2538 element_old = Feld[oldx][oldy];
2539 content_old = Store[oldx][oldy];
2541 if (element_old == EL_QUICKSAND_EMPTYING ||
2542 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2543 element_old == EL_MAGIC_WALL_EMPTYING ||
2544 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2545 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2546 element_old == EL_AMOEBA_DROPPING)
2547 cut_mode = CUT_ABOVE;
2549 DrawScreenElement(x, y, EL_EMPTY);
2552 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2554 else if (cut_mode == NO_CUTTING)
2555 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2558 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2561 else if (IS_DRAWABLE(element))
2562 DrawScreenElement(x, y, element);
2564 DrawScreenElement(x, y, EL_EMPTY);
2567 void DrawLevelField(int x, int y)
2569 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2570 DrawScreenField(SCREENX(x), SCREENY(y));
2571 else if (IS_MOVING(x, y))
2575 Moving2Blocked(x, y, &newx, &newy);
2576 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2577 DrawScreenField(SCREENX(newx), SCREENY(newy));
2579 else if (IS_BLOCKED(x, y))
2583 Blocked2Moving(x, y, &oldx, &oldy);
2584 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2585 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2589 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2590 int (*el2img_function)(int), boolean masked,
2591 int element_bits_draw)
2593 int element_base = map_mm_wall_element(element);
2594 int element_bits = (IS_DF_WALL(element) ?
2595 element - EL_DF_WALL_START :
2596 IS_MM_WALL(element) ?
2597 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2598 int graphic = el2img_function(element_base);
2599 int tilesize_draw = tilesize / 2;
2604 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2606 for (i = 0; i < 4; i++)
2608 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2609 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2611 if (!(element_bits_draw & (1 << i)))
2614 if (element_bits & (1 << i))
2617 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2618 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2620 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2621 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2626 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2627 tilesize_draw, tilesize_draw);
2632 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2633 boolean masked, int element_bits_draw)
2635 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2636 element, tilesize, el2edimg, masked, element_bits_draw);
2639 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2640 int (*el2img_function)(int))
2642 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2646 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2649 if (IS_MM_WALL(element))
2651 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2652 element, tilesize, el2edimg, masked, 0x000f);
2656 int graphic = el2edimg(element);
2659 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2661 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2665 void DrawSizedElement(int x, int y, int element, int tilesize)
2667 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2670 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2672 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2675 void DrawMiniElement(int x, int y, int element)
2679 graphic = el2edimg(element);
2680 DrawMiniGraphic(x, y, graphic);
2683 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2686 int x = sx + scroll_x, y = sy + scroll_y;
2688 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2689 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2690 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2691 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2693 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2696 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2698 int x = sx + scroll_x, y = sy + scroll_y;
2700 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2701 DrawMiniElement(sx, sy, EL_EMPTY);
2702 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2703 DrawMiniElement(sx, sy, Feld[x][y]);
2705 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2708 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2709 int x, int y, int xsize, int ysize,
2710 int tile_width, int tile_height)
2714 int dst_x = startx + x * tile_width;
2715 int dst_y = starty + y * tile_height;
2716 int width = graphic_info[graphic].width;
2717 int height = graphic_info[graphic].height;
2718 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2719 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2720 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2721 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2722 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2723 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2724 boolean draw_masked = graphic_info[graphic].draw_masked;
2726 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2728 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2730 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2734 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2735 inner_sx + (x - 1) * tile_width % inner_width);
2736 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2737 inner_sy + (y - 1) * tile_height % inner_height);
2740 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2743 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2747 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2748 int x, int y, int xsize, int ysize,
2751 int font_width = getFontWidth(font_nr);
2752 int font_height = getFontHeight(font_nr);
2754 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2755 font_width, font_height);
2758 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2760 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2761 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2762 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2763 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2764 boolean no_delay = (tape.warp_forward);
2765 unsigned int anim_delay = 0;
2766 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2767 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2768 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2769 int font_width = getFontWidth(font_nr);
2770 int font_height = getFontHeight(font_nr);
2771 int max_xsize = level.envelope[envelope_nr].xsize;
2772 int max_ysize = level.envelope[envelope_nr].ysize;
2773 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2774 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2775 int xend = max_xsize;
2776 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2777 int xstep = (xstart < xend ? 1 : 0);
2778 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2780 int end = MAX(xend - xstart, yend - ystart);
2783 for (i = start; i <= end; i++)
2785 int last_frame = end; // last frame of this "for" loop
2786 int x = xstart + i * xstep;
2787 int y = ystart + i * ystep;
2788 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2789 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2790 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2791 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2794 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2796 BlitScreenToBitmap(backbuffer);
2798 SetDrawtoField(DRAW_TO_BACKBUFFER);
2800 for (yy = 0; yy < ysize; yy++)
2801 for (xx = 0; xx < xsize; xx++)
2802 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2804 DrawTextBuffer(sx + font_width, sy + font_height,
2805 level.envelope[envelope_nr].text, font_nr, max_xsize,
2806 xsize - 2, ysize - 2, 0, mask_mode,
2807 level.envelope[envelope_nr].autowrap,
2808 level.envelope[envelope_nr].centered, FALSE);
2810 redraw_mask |= REDRAW_FIELD;
2813 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2816 ClearAutoRepeatKeyEvents();
2819 void ShowEnvelope(int envelope_nr)
2821 int element = EL_ENVELOPE_1 + envelope_nr;
2822 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2823 int sound_opening = element_info[element].sound[ACTION_OPENING];
2824 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2825 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2826 boolean no_delay = (tape.warp_forward);
2827 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2828 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2829 int anim_mode = graphic_info[graphic].anim_mode;
2830 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2831 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2833 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2835 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2837 if (anim_mode == ANIM_DEFAULT)
2838 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2840 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2843 Delay(wait_delay_value);
2845 WaitForEventToContinue();
2847 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2849 if (anim_mode != ANIM_NONE)
2850 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2852 if (anim_mode == ANIM_DEFAULT)
2853 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2855 game.envelope_active = FALSE;
2857 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2859 redraw_mask |= REDRAW_FIELD;
2863 static void setRequestBasePosition(int *x, int *y)
2865 int sx_base, sy_base;
2867 if (request.x != -1)
2868 sx_base = request.x;
2869 else if (request.align == ALIGN_LEFT)
2871 else if (request.align == ALIGN_RIGHT)
2872 sx_base = SX + SXSIZE;
2874 sx_base = SX + SXSIZE / 2;
2876 if (request.y != -1)
2877 sy_base = request.y;
2878 else if (request.valign == VALIGN_TOP)
2880 else if (request.valign == VALIGN_BOTTOM)
2881 sy_base = SY + SYSIZE;
2883 sy_base = SY + SYSIZE / 2;
2889 static void setRequestPositionExt(int *x, int *y, int width, int height,
2890 boolean add_border_size)
2892 int border_size = request.border_size;
2893 int sx_base, sy_base;
2896 setRequestBasePosition(&sx_base, &sy_base);
2898 if (request.align == ALIGN_LEFT)
2900 else if (request.align == ALIGN_RIGHT)
2901 sx = sx_base - width;
2903 sx = sx_base - width / 2;
2905 if (request.valign == VALIGN_TOP)
2907 else if (request.valign == VALIGN_BOTTOM)
2908 sy = sy_base - height;
2910 sy = sy_base - height / 2;
2912 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2913 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2915 if (add_border_size)
2925 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2927 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2930 static void DrawEnvelopeRequest(char *text)
2932 char *text_final = text;
2933 char *text_door_style = NULL;
2934 int graphic = IMG_BACKGROUND_REQUEST;
2935 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2936 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2937 int font_nr = FONT_REQUEST;
2938 int font_width = getFontWidth(font_nr);
2939 int font_height = getFontHeight(font_nr);
2940 int border_size = request.border_size;
2941 int line_spacing = request.line_spacing;
2942 int line_height = font_height + line_spacing;
2943 int max_text_width = request.width - 2 * border_size;
2944 int max_text_height = request.height - 2 * border_size;
2945 int line_length = max_text_width / font_width;
2946 int max_lines = max_text_height / line_height;
2947 int text_width = line_length * font_width;
2948 int width = request.width;
2949 int height = request.height;
2950 int tile_size = MAX(request.step_offset, 1);
2951 int x_steps = width / tile_size;
2952 int y_steps = height / tile_size;
2953 int sx_offset = border_size;
2954 int sy_offset = border_size;
2958 if (request.centered)
2959 sx_offset = (request.width - text_width) / 2;
2961 if (request.wrap_single_words && !request.autowrap)
2963 char *src_text_ptr, *dst_text_ptr;
2965 text_door_style = checked_malloc(2 * strlen(text) + 1);
2967 src_text_ptr = text;
2968 dst_text_ptr = text_door_style;
2970 while (*src_text_ptr)
2972 if (*src_text_ptr == ' ' ||
2973 *src_text_ptr == '?' ||
2974 *src_text_ptr == '!')
2975 *dst_text_ptr++ = '\n';
2977 if (*src_text_ptr != ' ')
2978 *dst_text_ptr++ = *src_text_ptr;
2983 *dst_text_ptr = '\0';
2985 text_final = text_door_style;
2988 setRequestPosition(&sx, &sy, FALSE);
2990 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2992 for (y = 0; y < y_steps; y++)
2993 for (x = 0; x < x_steps; x++)
2994 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2995 x, y, x_steps, y_steps,
2996 tile_size, tile_size);
2998 // force DOOR font inside door area
2999 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3001 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3002 line_length, -1, max_lines, line_spacing, mask_mode,
3003 request.autowrap, request.centered, FALSE);
3007 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3008 RedrawGadget(tool_gadget[i]);
3010 // store readily prepared envelope request for later use when animating
3011 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3013 if (text_door_style)
3014 free(text_door_style);
3017 static void AnimateEnvelopeRequest(int anim_mode, int action)
3019 int graphic = IMG_BACKGROUND_REQUEST;
3020 boolean draw_masked = graphic_info[graphic].draw_masked;
3021 int delay_value_normal = request.step_delay;
3022 int delay_value_fast = delay_value_normal / 2;
3023 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3024 boolean no_delay = (tape.warp_forward);
3025 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3026 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3027 unsigned int anim_delay = 0;
3029 int tile_size = MAX(request.step_offset, 1);
3030 int max_xsize = request.width / tile_size;
3031 int max_ysize = request.height / tile_size;
3032 int max_xsize_inner = max_xsize - 2;
3033 int max_ysize_inner = max_ysize - 2;
3035 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3036 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3037 int xend = max_xsize_inner;
3038 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3039 int xstep = (xstart < xend ? 1 : 0);
3040 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3042 int end = MAX(xend - xstart, yend - ystart);
3045 if (setup.quick_doors)
3052 for (i = start; i <= end; i++)
3054 int last_frame = end; // last frame of this "for" loop
3055 int x = xstart + i * xstep;
3056 int y = ystart + i * ystep;
3057 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3058 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3059 int xsize_size_left = (xsize - 1) * tile_size;
3060 int ysize_size_top = (ysize - 1) * tile_size;
3061 int max_xsize_pos = (max_xsize - 1) * tile_size;
3062 int max_ysize_pos = (max_ysize - 1) * tile_size;
3063 int width = xsize * tile_size;
3064 int height = ysize * tile_size;
3069 setRequestPosition(&src_x, &src_y, FALSE);
3070 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3072 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3074 for (yy = 0; yy < 2; yy++)
3076 for (xx = 0; xx < 2; xx++)
3078 int src_xx = src_x + xx * max_xsize_pos;
3079 int src_yy = src_y + yy * max_ysize_pos;
3080 int dst_xx = dst_x + xx * xsize_size_left;
3081 int dst_yy = dst_y + yy * ysize_size_top;
3082 int xx_size = (xx ? tile_size : xsize_size_left);
3083 int yy_size = (yy ? tile_size : ysize_size_top);
3086 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3087 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3089 BlitBitmap(bitmap_db_store_2, backbuffer,
3090 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3094 redraw_mask |= REDRAW_FIELD;
3098 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3101 ClearAutoRepeatKeyEvents();
3104 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3106 int graphic = IMG_BACKGROUND_REQUEST;
3107 int sound_opening = SND_REQUEST_OPENING;
3108 int sound_closing = SND_REQUEST_CLOSING;
3109 int anim_mode_1 = request.anim_mode; // (higher priority)
3110 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3111 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3112 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3113 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3115 if (game_status == GAME_MODE_PLAYING)
3116 BlitScreenToBitmap(backbuffer);
3118 SetDrawtoField(DRAW_TO_BACKBUFFER);
3120 // SetDrawBackgroundMask(REDRAW_NONE);
3122 if (action == ACTION_OPENING)
3124 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3126 if (req_state & REQ_ASK)
3128 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3129 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3131 else if (req_state & REQ_CONFIRM)
3133 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3135 else if (req_state & REQ_PLAYER)
3137 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3138 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3139 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3140 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3143 DrawEnvelopeRequest(text);
3146 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3148 if (action == ACTION_OPENING)
3150 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3152 if (anim_mode == ANIM_DEFAULT)
3153 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3155 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3159 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3161 if (anim_mode != ANIM_NONE)
3162 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3164 if (anim_mode == ANIM_DEFAULT)
3165 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3168 game.envelope_active = FALSE;
3170 if (action == ACTION_CLOSING)
3171 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3173 // SetDrawBackgroundMask(last_draw_background_mask);
3175 redraw_mask |= REDRAW_FIELD;
3179 if (action == ACTION_CLOSING &&
3180 game_status == GAME_MODE_PLAYING &&
3181 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3182 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3185 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3187 if (IS_MM_WALL(element))
3189 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3195 int graphic = el2preimg(element);
3197 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3198 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3203 void DrawLevel(int draw_background_mask)
3207 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3208 SetDrawBackgroundMask(draw_background_mask);
3212 for (x = BX1; x <= BX2; x++)
3213 for (y = BY1; y <= BY2; y++)
3214 DrawScreenField(x, y);
3216 redraw_mask |= REDRAW_FIELD;
3219 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3224 for (x = 0; x < size_x; x++)
3225 for (y = 0; y < size_y; y++)
3226 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3228 redraw_mask |= REDRAW_FIELD;
3231 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3235 for (x = 0; x < size_x; x++)
3236 for (y = 0; y < size_y; y++)
3237 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3239 redraw_mask |= REDRAW_FIELD;
3242 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3244 boolean show_level_border = (BorderElement != EL_EMPTY);
3245 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3246 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3247 int tile_size = preview.tile_size;
3248 int preview_width = preview.xsize * tile_size;
3249 int preview_height = preview.ysize * tile_size;
3250 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3251 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3252 int real_preview_width = real_preview_xsize * tile_size;
3253 int real_preview_height = real_preview_ysize * tile_size;
3254 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3255 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3258 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3261 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3263 dst_x += (preview_width - real_preview_width) / 2;
3264 dst_y += (preview_height - real_preview_height) / 2;
3266 for (x = 0; x < real_preview_xsize; x++)
3268 for (y = 0; y < real_preview_ysize; y++)
3270 int lx = from_x + x + (show_level_border ? -1 : 0);
3271 int ly = from_y + y + (show_level_border ? -1 : 0);
3272 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3273 getBorderElement(lx, ly));
3275 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3276 element, tile_size);
3280 redraw_mask |= REDRAW_FIELD;
3283 #define MICROLABEL_EMPTY 0
3284 #define MICROLABEL_LEVEL_NAME 1
3285 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3286 #define MICROLABEL_LEVEL_AUTHOR 3
3287 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3288 #define MICROLABEL_IMPORTED_FROM 5
3289 #define MICROLABEL_IMPORTED_BY_HEAD 6
3290 #define MICROLABEL_IMPORTED_BY 7
3292 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3294 int max_text_width = SXSIZE;
3295 int font_width = getFontWidth(font_nr);
3297 if (pos->align == ALIGN_CENTER)
3298 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3299 else if (pos->align == ALIGN_RIGHT)
3300 max_text_width = pos->x;
3302 max_text_width = SXSIZE - pos->x;
3304 return max_text_width / font_width;
3307 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3309 char label_text[MAX_OUTPUT_LINESIZE + 1];
3310 int max_len_label_text;
3311 int font_nr = pos->font;
3314 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3317 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3318 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3319 mode == MICROLABEL_IMPORTED_BY_HEAD)
3320 font_nr = pos->font_alt;
3322 max_len_label_text = getMaxTextLength(pos, font_nr);
3324 if (pos->size != -1)
3325 max_len_label_text = pos->size;
3327 for (i = 0; i < max_len_label_text; i++)
3328 label_text[i] = ' ';
3329 label_text[max_len_label_text] = '\0';
3331 if (strlen(label_text) > 0)
3332 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3335 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3336 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3337 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3338 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3339 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3340 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3341 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3342 max_len_label_text);
3343 label_text[max_len_label_text] = '\0';
3345 if (strlen(label_text) > 0)
3346 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3348 redraw_mask |= REDRAW_FIELD;
3351 static void DrawPreviewLevelLabel(int mode)
3353 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3356 static void DrawPreviewLevelInfo(int mode)
3358 if (mode == MICROLABEL_LEVEL_NAME)
3359 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3360 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3361 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3364 static void DrawPreviewLevelExt(boolean restart)
3366 static unsigned int scroll_delay = 0;
3367 static unsigned int label_delay = 0;
3368 static int from_x, from_y, scroll_direction;
3369 static int label_state, label_counter;
3370 unsigned int scroll_delay_value = preview.step_delay;
3371 boolean show_level_border = (BorderElement != EL_EMPTY);
3372 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3373 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3380 if (preview.anim_mode == ANIM_CENTERED)
3382 if (level_xsize > preview.xsize)
3383 from_x = (level_xsize - preview.xsize) / 2;
3384 if (level_ysize > preview.ysize)
3385 from_y = (level_ysize - preview.ysize) / 2;
3388 from_x += preview.xoffset;
3389 from_y += preview.yoffset;
3391 scroll_direction = MV_RIGHT;
3395 DrawPreviewLevelPlayfield(from_x, from_y);
3396 DrawPreviewLevelLabel(label_state);
3398 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3399 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3401 // initialize delay counters
3402 DelayReached(&scroll_delay, 0);
3403 DelayReached(&label_delay, 0);
3405 if (leveldir_current->name)
3407 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3408 char label_text[MAX_OUTPUT_LINESIZE + 1];
3409 int font_nr = pos->font;
3410 int max_len_label_text = getMaxTextLength(pos, font_nr);
3412 if (pos->size != -1)
3413 max_len_label_text = pos->size;
3415 strncpy(label_text, leveldir_current->name, max_len_label_text);
3416 label_text[max_len_label_text] = '\0';
3418 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3419 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3425 // scroll preview level, if needed
3426 if (preview.anim_mode != ANIM_NONE &&
3427 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3428 DelayReached(&scroll_delay, scroll_delay_value))
3430 switch (scroll_direction)
3435 from_x -= preview.step_offset;
3436 from_x = (from_x < 0 ? 0 : from_x);
3439 scroll_direction = MV_UP;
3443 if (from_x < level_xsize - preview.xsize)
3445 from_x += preview.step_offset;
3446 from_x = (from_x > level_xsize - preview.xsize ?
3447 level_xsize - preview.xsize : from_x);
3450 scroll_direction = MV_DOWN;
3456 from_y -= preview.step_offset;
3457 from_y = (from_y < 0 ? 0 : from_y);
3460 scroll_direction = MV_RIGHT;
3464 if (from_y < level_ysize - preview.ysize)
3466 from_y += preview.step_offset;
3467 from_y = (from_y > level_ysize - preview.ysize ?
3468 level_ysize - preview.ysize : from_y);
3471 scroll_direction = MV_LEFT;
3478 DrawPreviewLevelPlayfield(from_x, from_y);
3481 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3482 // redraw micro level label, if needed
3483 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3484 !strEqual(level.author, ANONYMOUS_NAME) &&
3485 !strEqual(level.author, leveldir_current->name) &&
3486 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3488 int max_label_counter = 23;
3490 if (leveldir_current->imported_from != NULL &&
3491 strlen(leveldir_current->imported_from) > 0)
3492 max_label_counter += 14;
3493 if (leveldir_current->imported_by != NULL &&
3494 strlen(leveldir_current->imported_by) > 0)
3495 max_label_counter += 14;
3497 label_counter = (label_counter + 1) % max_label_counter;
3498 label_state = (label_counter >= 0 && label_counter <= 7 ?
3499 MICROLABEL_LEVEL_NAME :
3500 label_counter >= 9 && label_counter <= 12 ?
3501 MICROLABEL_LEVEL_AUTHOR_HEAD :
3502 label_counter >= 14 && label_counter <= 21 ?
3503 MICROLABEL_LEVEL_AUTHOR :
3504 label_counter >= 23 && label_counter <= 26 ?
3505 MICROLABEL_IMPORTED_FROM_HEAD :
3506 label_counter >= 28 && label_counter <= 35 ?
3507 MICROLABEL_IMPORTED_FROM :
3508 label_counter >= 37 && label_counter <= 40 ?
3509 MICROLABEL_IMPORTED_BY_HEAD :
3510 label_counter >= 42 && label_counter <= 49 ?
3511 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3513 if (leveldir_current->imported_from == NULL &&
3514 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3515 label_state == MICROLABEL_IMPORTED_FROM))
3516 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3517 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3519 DrawPreviewLevelLabel(label_state);
3523 void DrawPreviewPlayers(void)
3525 if (game_status != GAME_MODE_MAIN)
3528 // do not draw preview players if level preview redefined, but players aren't
3529 if (preview.redefined && !menu.main.preview_players.redefined)
3532 boolean player_found[MAX_PLAYERS];
3533 int num_players = 0;
3536 for (i = 0; i < MAX_PLAYERS; i++)
3537 player_found[i] = FALSE;
3539 // check which players can be found in the level (simple approach)
3540 for (x = 0; x < lev_fieldx; x++)
3542 for (y = 0; y < lev_fieldy; y++)
3544 int element = level.field[x][y];
3546 if (ELEM_IS_PLAYER(element))
3548 int player_nr = GET_PLAYER_NR(element);
3550 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3552 if (!player_found[player_nr])
3555 player_found[player_nr] = TRUE;
3560 struct TextPosInfo *pos = &menu.main.preview_players;
3561 int tile_size = pos->tile_size;
3562 int border_size = pos->border_size;
3563 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3564 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3565 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3566 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3567 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3568 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3569 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3570 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3571 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3572 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3573 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3574 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3576 // clear area in which the players will be drawn
3577 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3578 max_players_width, max_players_height);
3580 if (!network.enabled && !setup.team_mode)
3583 // only draw players if level is suited for team mode
3584 if (num_players < 2)
3587 // draw all players that were found in the level
3588 for (i = 0; i < MAX_PLAYERS; i++)
3590 if (player_found[i])
3592 int graphic = el2img(EL_PLAYER_1 + i);
3594 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3596 xpos += player_xoffset;
3597 ypos += player_yoffset;
3602 void DrawPreviewLevelInitial(void)
3604 DrawPreviewLevelExt(TRUE);
3605 DrawPreviewPlayers();
3608 void DrawPreviewLevelAnimation(void)
3610 DrawPreviewLevelExt(FALSE);
3613 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3614 int border_size, int font_nr)
3616 int graphic = el2img(EL_PLAYER_1 + player_nr);
3617 int font_height = getFontHeight(font_nr);
3618 int player_height = MAX(tile_size, font_height);
3619 int xoffset_text = tile_size + border_size;
3620 int yoffset_text = (player_height - font_height) / 2;
3621 int yoffset_graphic = (player_height - tile_size) / 2;
3622 char *player_name = getNetworkPlayerName(player_nr + 1);
3624 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3626 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3629 static void DrawNetworkPlayersExt(boolean force)
3631 if (game_status != GAME_MODE_MAIN)
3634 if (!network.connected && !force)
3637 // do not draw network players if level preview redefined, but players aren't
3638 if (preview.redefined && !menu.main.network_players.redefined)
3641 int num_players = 0;
3644 for (i = 0; i < MAX_PLAYERS; i++)
3645 if (stored_player[i].connected_network)
3648 struct TextPosInfo *pos = &menu.main.network_players;
3649 int tile_size = pos->tile_size;
3650 int border_size = pos->border_size;
3651 int xoffset_text = tile_size + border_size;
3652 int font_nr = pos->font;
3653 int font_width = getFontWidth(font_nr);
3654 int font_height = getFontHeight(font_nr);
3655 int player_height = MAX(tile_size, font_height);
3656 int player_yoffset = player_height + border_size;
3657 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3658 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3659 int all_players_height = num_players * player_yoffset - border_size;
3660 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3661 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3662 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3664 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3665 max_players_width, max_players_height);
3667 // first draw local network player ...
3668 for (i = 0; i < MAX_PLAYERS; i++)
3670 if (stored_player[i].connected_network &&
3671 stored_player[i].connected_locally)
3673 char *player_name = getNetworkPlayerName(i + 1);
3674 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3675 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3677 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3679 ypos += player_yoffset;
3683 // ... then draw all other network players
3684 for (i = 0; i < MAX_PLAYERS; i++)
3686 if (stored_player[i].connected_network &&
3687 !stored_player[i].connected_locally)
3689 char *player_name = getNetworkPlayerName(i + 1);
3690 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3691 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3693 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3695 ypos += player_yoffset;
3700 void DrawNetworkPlayers(void)
3702 DrawNetworkPlayersExt(FALSE);
3705 void ClearNetworkPlayers(void)
3707 DrawNetworkPlayersExt(TRUE);
3710 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3711 int graphic, int sync_frame,
3714 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3716 if (mask_mode == USE_MASKING)
3717 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3719 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3722 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3723 int graphic, int sync_frame, int mask_mode)
3725 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3727 if (mask_mode == USE_MASKING)
3728 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3730 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3733 static void DrawGraphicAnimation(int x, int y, int graphic)
3735 int lx = LEVELX(x), ly = LEVELY(y);
3737 if (!IN_SCR_FIELD(x, y))
3740 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3741 graphic, GfxFrame[lx][ly], NO_MASKING);
3743 MarkTileDirty(x, y);
3746 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3748 int lx = LEVELX(x), ly = LEVELY(y);
3750 if (!IN_SCR_FIELD(x, y))
3753 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3754 graphic, GfxFrame[lx][ly], NO_MASKING);
3755 MarkTileDirty(x, y);
3758 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3760 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3763 void DrawLevelElementAnimation(int x, int y, int element)
3765 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3767 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3770 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3772 int sx = SCREENX(x), sy = SCREENY(y);
3774 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3777 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3780 DrawGraphicAnimation(sx, sy, graphic);
3783 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3784 DrawLevelFieldCrumbled(x, y);
3786 if (GFX_CRUMBLED(Feld[x][y]))
3787 DrawLevelFieldCrumbled(x, y);
3791 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3793 int sx = SCREENX(x), sy = SCREENY(y);
3796 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3799 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3801 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3804 DrawGraphicAnimation(sx, sy, graphic);
3806 if (GFX_CRUMBLED(element))
3807 DrawLevelFieldCrumbled(x, y);
3810 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3812 if (player->use_murphy)
3814 // this works only because currently only one player can be "murphy" ...
3815 static int last_horizontal_dir = MV_LEFT;
3816 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3818 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3819 last_horizontal_dir = move_dir;
3821 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3823 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3825 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3831 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3834 static boolean equalGraphics(int graphic1, int graphic2)
3836 struct GraphicInfo *g1 = &graphic_info[graphic1];
3837 struct GraphicInfo *g2 = &graphic_info[graphic2];
3839 return (g1->bitmap == g2->bitmap &&
3840 g1->src_x == g2->src_x &&
3841 g1->src_y == g2->src_y &&
3842 g1->anim_frames == g2->anim_frames &&
3843 g1->anim_delay == g2->anim_delay &&
3844 g1->anim_mode == g2->anim_mode);
3847 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3851 DRAW_PLAYER_STAGE_INIT = 0,
3852 DRAW_PLAYER_STAGE_LAST_FIELD,
3853 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
3854 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3855 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3856 DRAW_PLAYER_STAGE_PLAYER,
3858 DRAW_PLAYER_STAGE_PLAYER,
3859 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3861 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
3862 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
3864 NUM_DRAW_PLAYER_STAGES
3867 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
3869 static int static_last_player_graphic[MAX_PLAYERS];
3870 static int static_last_player_frame[MAX_PLAYERS];
3871 static boolean static_player_is_opaque[MAX_PLAYERS];
3872 static boolean draw_player[MAX_PLAYERS];
3873 int pnr = player->index_nr;
3875 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3877 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
3878 static_last_player_frame[pnr] = player->Frame;
3879 static_player_is_opaque[pnr] = FALSE;
3881 draw_player[pnr] = TRUE;
3884 if (!draw_player[pnr])
3888 if (!IN_LEV_FIELD(player->jx, player->jy))
3890 printf("DrawPlayerField(): x = %d, y = %d\n", player->jx, player->jy);
3891 printf("DrawPlayerField(): This should never happen!\n");
3893 draw_player[pnr] = FALSE;
3899 int last_player_graphic = static_last_player_graphic[pnr];
3900 int last_player_frame = static_last_player_frame[pnr];
3901 boolean player_is_opaque = static_player_is_opaque[pnr];
3903 int jx = player->jx;
3904 int jy = player->jy;
3905 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
3906 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3907 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3908 int last_jx = (player->is_moving ? jx - dx : jx);
3909 int last_jy = (player->is_moving ? jy - dy : jy);
3910 int next_jx = jx + dx;
3911 int next_jy = jy + dy;
3912 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
3913 int sx = SCREENX(jx);
3914 int sy = SCREENY(jy);
3915 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
3916 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
3917 int element = Feld[jx][jy];
3918 int last_element = Feld[last_jx][last_jy];
3919 int action = (player->is_pushing ? ACTION_PUSHING :
3920 player->is_digging ? ACTION_DIGGING :
3921 player->is_collecting ? ACTION_COLLECTING :
3922 player->is_moving ? ACTION_MOVING :
3923 player->is_snapping ? ACTION_SNAPPING :
3924 player->is_dropping ? ACTION_DROPPING :
3925 player->is_waiting ? player->action_waiting :
3928 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3930 // ------------------------------------------------------------------------
3931 // initialize drawing the player
3932 // ------------------------------------------------------------------------
3934 draw_player[pnr] = FALSE;
3936 // GfxElement[][] is set to the element the player is digging or collecting;
3937 // remove also for off-screen player if the player is not moving anymore
3938 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3939 GfxElement[jx][jy] = EL_UNDEFINED;
3941 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3944 if (element == EL_EXPLOSION)
3947 InitPlayerGfxAnimation(player, action, move_dir);
3949 draw_player[pnr] = TRUE;
3951 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)
3953 // ------------------------------------------------------------------------
3954 // draw things in the field the player is leaving, if needed
3955 // ------------------------------------------------------------------------
3957 if (!IN_SCR_FIELD(sx, sy))
3958 draw_player[pnr] = FALSE;
3960 if (!player->is_moving)
3963 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3965 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3967 if (last_element == EL_DYNAMITE_ACTIVE ||
3968 last_element == EL_EM_DYNAMITE_ACTIVE ||
3969 last_element == EL_SP_DISK_RED_ACTIVE)