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 ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
222 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
223 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
225 fx += (dx_var > 0 ? TILEX_VAR : 0);
232 if (full_lev_fieldx <= SCR_FIELDX)
234 if (EVEN(SCR_FIELDX))
235 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
237 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
243 static int getFieldbufferOffsetY_RND(void)
245 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
246 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
247 int dy_var = dy * TILESIZE_VAR / TILESIZE;
250 if (EVEN(SCR_FIELDY))
252 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
254 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
255 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
257 fy += (dy_var > 0 ? TILEY_VAR : 0);
264 if (full_lev_fieldy <= SCR_FIELDY)
266 if (EVEN(SCR_FIELDY))
267 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
269 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
275 static int getLevelFromScreenX_RND(int sx)
277 int fx = getFieldbufferOffsetX_RND();
280 int lx = LEVELX((px + dx) / TILESIZE_VAR);
285 static int getLevelFromScreenY_RND(int sy)
287 int fy = getFieldbufferOffsetY_RND();
290 int ly = LEVELY((py + dy) / TILESIZE_VAR);
295 static int getLevelFromScreenX_EM(int sx)
297 int level_xsize = level.native_em_level->lev->width;
298 int full_xsize = level_xsize * TILESIZE_VAR;
300 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
302 int fx = getFieldbufferOffsetX_EM();
305 int lx = LEVELX((px + dx) / TILESIZE_VAR);
307 lx = correctLevelPosX_EM(lx);
312 static int getLevelFromScreenY_EM(int sy)
314 int level_ysize = level.native_em_level->lev->height;
315 int full_ysize = level_ysize * TILESIZE_VAR;
317 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
319 int fy = getFieldbufferOffsetY_EM();
322 int ly = LEVELY((py + dy) / TILESIZE_VAR);
324 ly = correctLevelPosY_EM(ly);
329 static int getLevelFromScreenX_SP(int sx)
331 int menBorder = setup.sp_show_border_elements;
332 int level_xsize = level.native_sp_level->width;
333 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
335 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
337 int fx = getFieldbufferOffsetX_SP();
340 int lx = LEVELX((px + dx) / TILESIZE_VAR);
345 static int getLevelFromScreenY_SP(int sy)
347 int menBorder = setup.sp_show_border_elements;
348 int level_ysize = level.native_sp_level->height;
349 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
351 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
353 int fy = getFieldbufferOffsetY_SP();
356 int ly = LEVELY((py + dy) / TILESIZE_VAR);
361 static int getLevelFromScreenX_MM(int sx)
363 int level_xsize = level.native_mm_level->fieldx;
364 int full_xsize = level_xsize * TILESIZE_VAR;
366 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
369 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
374 static int getLevelFromScreenY_MM(int sy)
376 int level_ysize = level.native_mm_level->fieldy;
377 int full_ysize = level_ysize * TILESIZE_VAR;
379 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
382 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
387 int getLevelFromScreenX(int x)
389 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
390 return getLevelFromScreenX_EM(x);
391 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
392 return getLevelFromScreenX_SP(x);
393 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
394 return getLevelFromScreenX_MM(x);
396 return getLevelFromScreenX_RND(x);
399 int getLevelFromScreenY(int y)
401 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
402 return getLevelFromScreenY_EM(y);
403 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
404 return getLevelFromScreenY_SP(y);
405 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
406 return getLevelFromScreenY_MM(y);
408 return getLevelFromScreenY_RND(y);
411 void DumpTile(int x, int y)
417 printf_line("-", 79);
418 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
419 printf_line("-", 79);
421 if (!IN_LEV_FIELD(x, y))
423 printf("(not in level field)\n");
429 token_name = element_info[Feld[x][y]].token_name;
431 printf(" Feld: %d\t['%s']\n", Feld[x][y], token_name);
432 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
433 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
434 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
435 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
436 printf(" MovPos: %d\n", MovPos[x][y]);
437 printf(" MovDir: %d\n", MovDir[x][y]);
438 printf(" MovDelay: %d\n", MovDelay[x][y]);
439 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
440 printf(" CustomValue: %d\n", CustomValue[x][y]);
441 printf(" GfxElement: %d\n", GfxElement[x][y]);
442 printf(" GfxAction: %d\n", GfxAction[x][y]);
443 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
444 printf(" Player x/y: %d, %d\n", local_player->jx, local_player->jy);
448 void DumpTileFromScreen(int sx, int sy)
450 int lx = getLevelFromScreenX(sx);
451 int ly = getLevelFromScreenY(sy);
456 void SetDrawtoField(int mode)
458 if (mode == DRAW_TO_FIELDBUFFER)
464 BX2 = SCR_FIELDX + 1;
465 BY2 = SCR_FIELDY + 1;
467 drawto_field = fieldbuffer;
469 else // DRAW_TO_BACKBUFFER
475 BX2 = SCR_FIELDX - 1;
476 BY2 = SCR_FIELDY - 1;
478 drawto_field = backbuffer;
482 static void RedrawPlayfield_RND(void)
484 if (game.envelope_active)
487 DrawLevel(REDRAW_ALL);
491 void RedrawPlayfield(void)
493 if (game_status != GAME_MODE_PLAYING)
496 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
497 RedrawPlayfield_EM(TRUE);
498 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
499 RedrawPlayfield_SP(TRUE);
500 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
501 RedrawPlayfield_MM();
502 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
503 RedrawPlayfield_RND();
505 BlitScreenToBitmap(backbuffer);
507 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
511 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
514 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
515 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
517 if (x == -1 && y == -1)
520 if (draw_target == DRAW_TO_SCREEN)
521 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
523 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
526 static void DrawMaskedBorderExt_FIELD(int draw_target)
528 if (global.border_status >= GAME_MODE_MAIN &&
529 global.border_status <= GAME_MODE_PLAYING &&
530 border.draw_masked[global.border_status])
531 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
535 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
537 // when drawing to backbuffer, never draw border over open doors
538 if (draw_target == DRAW_TO_BACKBUFFER &&
539 (GetDoorState() & DOOR_OPEN_1))
542 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
543 (global.border_status != GAME_MODE_EDITOR ||
544 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
545 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
548 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
550 // when drawing to backbuffer, never draw border over open doors
551 if (draw_target == DRAW_TO_BACKBUFFER &&
552 (GetDoorState() & DOOR_OPEN_2))
555 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
556 global.border_status != GAME_MODE_EDITOR)
557 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
560 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
562 // currently not available
565 static void DrawMaskedBorderExt_ALL(int draw_target)
567 DrawMaskedBorderExt_FIELD(draw_target);
568 DrawMaskedBorderExt_DOOR_1(draw_target);
569 DrawMaskedBorderExt_DOOR_2(draw_target);
570 DrawMaskedBorderExt_DOOR_3(draw_target);
573 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
575 // never draw masked screen borders on borderless screens
576 if (global.border_status == GAME_MODE_LOADING ||
577 global.border_status == GAME_MODE_TITLE)
580 if (redraw_mask & REDRAW_ALL)
581 DrawMaskedBorderExt_ALL(draw_target);
584 if (redraw_mask & REDRAW_FIELD)
585 DrawMaskedBorderExt_FIELD(draw_target);
586 if (redraw_mask & REDRAW_DOOR_1)
587 DrawMaskedBorderExt_DOOR_1(draw_target);
588 if (redraw_mask & REDRAW_DOOR_2)
589 DrawMaskedBorderExt_DOOR_2(draw_target);
590 if (redraw_mask & REDRAW_DOOR_3)
591 DrawMaskedBorderExt_DOOR_3(draw_target);
595 void DrawMaskedBorder_FIELD(void)
597 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
600 void DrawMaskedBorder(int redraw_mask)
602 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
605 void DrawMaskedBorderToTarget(int draw_target)
607 if (draw_target == DRAW_TO_BACKBUFFER ||
608 draw_target == DRAW_TO_SCREEN)
610 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
614 int last_border_status = global.border_status;
616 if (draw_target == DRAW_TO_FADE_SOURCE)
618 global.border_status = gfx.fade_border_source_status;
619 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
621 else if (draw_target == DRAW_TO_FADE_TARGET)
623 global.border_status = gfx.fade_border_target_status;
624 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
627 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
629 global.border_status = last_border_status;
630 gfx.masked_border_bitmap_ptr = backbuffer;
634 void DrawTileCursor(int draw_target)
640 int graphic = IMG_GLOBAL_TILE_CURSOR;
642 int tilesize = TILESIZE_VAR;
643 int width = tilesize;
644 int height = tilesize;
646 if (game_status != GAME_MODE_PLAYING)
649 if (!tile_cursor.enabled ||
653 if (tile_cursor.moving)
655 int step = TILESIZE_VAR / 4;
656 int dx = tile_cursor.target_x - tile_cursor.x;
657 int dy = tile_cursor.target_y - tile_cursor.y;
660 tile_cursor.x = tile_cursor.target_x;
662 tile_cursor.x += SIGN(dx) * step;
665 tile_cursor.y = tile_cursor.target_y;
667 tile_cursor.y += SIGN(dy) * step;
669 if (tile_cursor.x == tile_cursor.target_x &&
670 tile_cursor.y == tile_cursor.target_y)
671 tile_cursor.moving = FALSE;
674 dst_x = tile_cursor.x;
675 dst_y = tile_cursor.y;
677 frame = getGraphicAnimationFrame(graphic, -1);
679 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
682 (draw_target == DRAW_TO_FADE_SOURCE ? gfx.fade_bitmap_source :
683 draw_target == DRAW_TO_FADE_TARGET ? gfx.fade_bitmap_target : NULL);
685 if (draw_target == DRAW_TO_SCREEN)
686 BlitToScreenMasked(src_bitmap, src_x, src_y, width, height, dst_x, dst_y);
688 BlitBitmapMasked(src_bitmap, fade_bitmap, src_x, src_y, width, height,
692 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
694 int fx = getFieldbufferOffsetX_RND();
695 int fy = getFieldbufferOffsetY_RND();
697 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
700 void BlitScreenToBitmap(Bitmap *target_bitmap)
702 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
703 BlitScreenToBitmap_EM(target_bitmap);
704 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
705 BlitScreenToBitmap_SP(target_bitmap);
706 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
707 BlitScreenToBitmap_MM(target_bitmap);
708 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
709 BlitScreenToBitmap_RND(target_bitmap);
711 redraw_mask |= REDRAW_FIELD;
714 static void DrawFramesPerSecond(void)
717 int font_nr = FONT_TEXT_2;
718 int font_width = getFontWidth(font_nr);
719 int draw_deactivation_mask = GetDrawDeactivationMask();
720 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
722 // draw FPS with leading space (needed if field buffer deactivated)
723 sprintf(text, " %04.1f fps", global.frames_per_second);
725 // override draw deactivation mask (required for invisible warp mode)
726 SetDrawDeactivationMask(REDRAW_NONE);
728 // draw opaque FPS if field buffer deactivated, else draw masked FPS
729 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
730 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
732 // set draw deactivation mask to previous value
733 SetDrawDeactivationMask(draw_deactivation_mask);
735 // force full-screen redraw in this frame
736 redraw_mask = REDRAW_ALL;
740 static void PrintFrameTimeDebugging(void)
742 static unsigned int last_counter = 0;
743 unsigned int counter = Counter();
744 int diff_1 = counter - last_counter;
745 int diff_2 = diff_1 - GAME_FRAME_DELAY;
747 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
748 char diff_bar[2 * diff_2_max + 5];
752 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
754 for (i = 0; i < diff_2_max; i++)
755 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
756 i >= diff_2_max - diff_2_cut ? '-' : ' ');
758 diff_bar[pos++] = '|';
760 for (i = 0; i < diff_2_max; i++)
761 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
763 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
765 diff_bar[pos++] = '\0';
767 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
770 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
773 last_counter = counter;
777 static int unifiedRedrawMask(int mask)
779 if (mask & REDRAW_ALL)
782 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
788 static boolean equalRedrawMasks(int mask_1, int mask_2)
790 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
793 void BackToFront(void)
795 static int last_redraw_mask = REDRAW_NONE;
797 // force screen redraw in every frame to continue drawing global animations
798 // (but always use the last redraw mask to prevent unwanted side effects)
799 if (redraw_mask == REDRAW_NONE)
800 redraw_mask = last_redraw_mask;
802 last_redraw_mask = redraw_mask;
805 // masked border now drawn immediately when blitting backbuffer to window
807 // draw masked border to all viewports, if defined
808 DrawMaskedBorder(redraw_mask);
811 // draw frames per second (only if debug mode is enabled)
812 if (redraw_mask & REDRAW_FPS)
813 DrawFramesPerSecond();
815 // remove playfield redraw before potentially merging with doors redraw
816 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
817 redraw_mask &= ~REDRAW_FIELD;
819 // redraw complete window if both playfield and (some) doors need redraw
820 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
821 redraw_mask = REDRAW_ALL;
823 /* although redrawing the whole window would be fine for normal gameplay,
824 being able to only redraw the playfield is required for deactivating
825 certain drawing areas (mainly playfield) to work, which is needed for
826 warp-forward to be fast enough (by skipping redraw of most frames) */
828 if (redraw_mask & REDRAW_ALL)
830 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
832 else if (redraw_mask & REDRAW_FIELD)
834 BlitBitmap(backbuffer, window,
835 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
837 else if (redraw_mask & REDRAW_DOORS)
839 // merge door areas to prevent calling screen redraw more than once
845 if (redraw_mask & REDRAW_DOOR_1)
849 x2 = MAX(x2, DX + DXSIZE);
850 y2 = MAX(y2, DY + DYSIZE);
853 if (redraw_mask & REDRAW_DOOR_2)
857 x2 = MAX(x2, VX + VXSIZE);
858 y2 = MAX(y2, VY + VYSIZE);
861 if (redraw_mask & REDRAW_DOOR_3)
865 x2 = MAX(x2, EX + EXSIZE);
866 y2 = MAX(y2, EY + EYSIZE);
869 // make sure that at least one pixel is blitted, and inside the screen
870 // (else nothing is blitted, causing the animations not to be updated)
871 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
872 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
873 x2 = MIN(MAX(1, x2), WIN_XSIZE);
874 y2 = MIN(MAX(1, y2), WIN_YSIZE);
876 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
879 redraw_mask = REDRAW_NONE;
882 PrintFrameTimeDebugging();
886 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
888 unsigned int frame_delay_value_old = GetVideoFrameDelay();
890 SetVideoFrameDelay(frame_delay_value);
894 SetVideoFrameDelay(frame_delay_value_old);
897 static int fade_type_skip = FADE_TYPE_NONE;
899 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
901 void (*draw_border_function)(void) = NULL;
902 int x, y, width, height;
903 int fade_delay, post_delay;
905 if (fade_type == FADE_TYPE_FADE_OUT)
907 if (fade_type_skip != FADE_TYPE_NONE)
909 // skip all fade operations until specified fade operation
910 if (fade_type & fade_type_skip)
911 fade_type_skip = FADE_TYPE_NONE;
916 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
920 redraw_mask |= fade_mask;
922 if (fade_type == FADE_TYPE_SKIP)
924 fade_type_skip = fade_mode;
929 fade_delay = fading.fade_delay;
930 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
932 if (fade_type_skip != FADE_TYPE_NONE)
934 // skip all fade operations until specified fade operation
935 if (fade_type & fade_type_skip)
936 fade_type_skip = FADE_TYPE_NONE;
941 if (global.autoplay_leveldir)
946 if (fade_mask == REDRAW_FIELD)
951 height = FADE_SYSIZE;
953 if (border.draw_masked_when_fading)
954 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
956 DrawMaskedBorder_FIELD(); // draw once
966 if (!setup.fade_screens ||
968 fading.fade_mode == FADE_MODE_NONE)
970 if (fade_mode == FADE_MODE_FADE_OUT)
973 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
975 redraw_mask &= ~fade_mask;
980 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
981 draw_border_function);
983 redraw_mask &= ~fade_mask;
985 ClearAutoRepeatKeyEvents();
988 static void SetScreenStates_BeforeFadingIn(void)
990 // temporarily set screen mode for animations to screen after fading in
991 global.anim_status = global.anim_status_next;
993 // store backbuffer with all animations that will be started after fading in
994 if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
995 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
997 // set screen mode for animations back to fading
998 global.anim_status = GAME_MODE_PSEUDO_FADING;
1001 static void SetScreenStates_AfterFadingIn(void)
1003 // store new source screen (to use correct masked border for fading)
1004 gfx.fade_border_source_status = global.border_status;
1006 global.anim_status = global.anim_status_next;
1009 static void SetScreenStates_BeforeFadingOut(void)
1011 // store new target screen (to use correct masked border for fading)
1012 gfx.fade_border_target_status = game_status;
1014 // set screen mode for animations to fading
1015 global.anim_status = GAME_MODE_PSEUDO_FADING;
1017 // store backbuffer with all animations that will be stopped for fading out
1018 if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1019 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
1022 static void SetScreenStates_AfterFadingOut(void)
1024 global.border_status = game_status;
1027 void FadeIn(int fade_mask)
1029 SetScreenStates_BeforeFadingIn();
1032 DrawMaskedBorder(REDRAW_ALL);
1035 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1036 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
1038 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
1042 FADE_SXSIZE = FULL_SXSIZE;
1043 FADE_SYSIZE = FULL_SYSIZE;
1045 // activate virtual buttons depending on upcoming game status
1046 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
1047 game_status == GAME_MODE_PLAYING && !tape.playing)
1048 SetOverlayActive(TRUE);
1050 SetScreenStates_AfterFadingIn();
1052 // force update of global animation status in case of rapid screen changes
1053 redraw_mask = REDRAW_ALL;
1057 void FadeOut(int fade_mask)
1059 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1060 if (!equalRedrawMasks(fade_mask, redraw_mask))
1063 SetScreenStates_BeforeFadingOut();
1065 SetTileCursorActive(FALSE);
1066 SetOverlayActive(FALSE);
1069 DrawMaskedBorder(REDRAW_ALL);
1072 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1073 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1075 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1077 SetScreenStates_AfterFadingOut();
1080 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1082 static struct TitleFadingInfo fading_leave_stored;
1085 fading_leave_stored = fading_leave;
1087 fading = fading_leave_stored;
1090 void FadeSetEnterMenu(void)
1092 fading = menu.enter_menu;
1094 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1097 void FadeSetLeaveMenu(void)
1099 fading = menu.leave_menu;
1101 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1104 void FadeSetEnterScreen(void)
1106 fading = menu.enter_screen[game_status];
1108 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1111 void FadeSetNextScreen(void)
1113 fading = menu.next_screen[game_status];
1115 // (do not overwrite fade mode set by FadeSetEnterScreen)
1116 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1119 void FadeSetLeaveScreen(void)
1121 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1124 void FadeSetFromType(int type)
1126 if (type & TYPE_ENTER_SCREEN)
1127 FadeSetEnterScreen();
1128 else if (type & TYPE_ENTER)
1130 else if (type & TYPE_LEAVE)
1134 void FadeSetDisabled(void)
1136 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1138 fading = fading_none;
1141 void FadeSkipNextFadeIn(void)
1143 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1146 void FadeSkipNextFadeOut(void)
1148 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1151 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1153 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1155 return (graphic == IMG_UNDEFINED ? NULL :
1156 graphic_info[graphic].bitmap != NULL || redefined ?
1157 graphic_info[graphic].bitmap :
1158 graphic_info[default_graphic].bitmap);
1161 static Bitmap *getBackgroundBitmap(int graphic)
1163 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1166 static Bitmap *getGlobalBorderBitmap(int graphic)
1168 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1171 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1174 (status == GAME_MODE_MAIN ||
1175 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1176 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1177 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1178 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1181 return getGlobalBorderBitmap(graphic);
1184 void SetWindowBackgroundImageIfDefined(int graphic)
1186 if (graphic_info[graphic].bitmap)
1187 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1190 void SetMainBackgroundImageIfDefined(int graphic)
1192 if (graphic_info[graphic].bitmap)
1193 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1196 void SetDoorBackgroundImageIfDefined(int graphic)
1198 if (graphic_info[graphic].bitmap)
1199 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1202 void SetWindowBackgroundImage(int graphic)
1204 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1207 void SetMainBackgroundImage(int graphic)
1209 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1212 void SetDoorBackgroundImage(int graphic)
1214 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1217 void SetPanelBackground(void)
1219 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1221 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1222 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1224 SetDoorBackgroundBitmap(bitmap_db_panel);
1227 void DrawBackground(int x, int y, int width, int height)
1229 // "drawto" might still point to playfield buffer here (hall of fame)
1230 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1232 if (IN_GFX_FIELD_FULL(x, y))
1233 redraw_mask |= REDRAW_FIELD;
1234 else if (IN_GFX_DOOR_1(x, y))
1235 redraw_mask |= REDRAW_DOOR_1;
1236 else if (IN_GFX_DOOR_2(x, y))
1237 redraw_mask |= REDRAW_DOOR_2;
1238 else if (IN_GFX_DOOR_3(x, y))
1239 redraw_mask |= REDRAW_DOOR_3;
1242 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1244 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1246 if (font->bitmap == NULL)
1249 DrawBackground(x, y, width, height);
1252 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1254 struct GraphicInfo *g = &graphic_info[graphic];
1256 if (g->bitmap == NULL)
1259 DrawBackground(x, y, width, height);
1262 static int game_status_last = -1;
1263 static Bitmap *global_border_bitmap_last = NULL;
1264 static Bitmap *global_border_bitmap = NULL;
1265 static int real_sx_last = -1, real_sy_last = -1;
1266 static int full_sxsize_last = -1, full_sysize_last = -1;
1267 static int dx_last = -1, dy_last = -1;
1268 static int dxsize_last = -1, dysize_last = -1;
1269 static int vx_last = -1, vy_last = -1;
1270 static int vxsize_last = -1, vysize_last = -1;
1271 static int ex_last = -1, ey_last = -1;
1272 static int exsize_last = -1, eysize_last = -1;
1274 boolean CheckIfGlobalBorderHasChanged(void)
1276 // if game status has not changed, global border has not changed either
1277 if (game_status == game_status_last)
1280 // determine and store new global border bitmap for current game status
1281 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1283 return (global_border_bitmap_last != global_border_bitmap);
1286 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1288 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1289 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1291 // if game status has not changed, nothing has to be redrawn
1292 if (game_status == game_status_last)
1295 // redraw if last screen was title screen
1296 if (game_status_last == GAME_MODE_TITLE)
1299 // redraw if global screen border has changed
1300 if (CheckIfGlobalBorderHasChanged())
1303 // redraw if position or size of playfield area has changed
1304 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1305 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1308 // redraw if position or size of door area has changed
1309 if (dx_last != DX || dy_last != DY ||
1310 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1313 // redraw if position or size of tape area has changed
1314 if (vx_last != VX || vy_last != VY ||
1315 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1318 // redraw if position or size of editor area has changed
1319 if (ex_last != EX || ey_last != EY ||
1320 exsize_last != EXSIZE || eysize_last != EYSIZE)
1327 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1330 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1332 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1335 void RedrawGlobalBorder(void)
1337 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1339 RedrawGlobalBorderFromBitmap(bitmap);
1341 redraw_mask = REDRAW_ALL;
1344 static void RedrawGlobalBorderIfNeeded(void)
1346 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1347 if (game_status == game_status_last)
1351 // copy current draw buffer to later copy back areas that have not changed
1352 if (game_status_last != GAME_MODE_TITLE)
1353 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1355 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1356 if (CheckIfGlobalBorderRedrawIsNeeded())
1359 // redraw global screen border (or clear, if defined to be empty)
1360 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1362 if (game_status == GAME_MODE_EDITOR)
1363 DrawSpecialEditorDoor();
1365 // copy previous playfield and door areas, if they are defined on both
1366 // previous and current screen and if they still have the same size
1368 if (real_sx_last != -1 && real_sy_last != -1 &&
1369 REAL_SX != -1 && REAL_SY != -1 &&
1370 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1371 BlitBitmap(bitmap_db_store_1, backbuffer,
1372 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1375 if (dx_last != -1 && dy_last != -1 &&
1376 DX != -1 && DY != -1 &&
1377 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1378 BlitBitmap(bitmap_db_store_1, backbuffer,
1379 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1381 if (game_status != GAME_MODE_EDITOR)
1383 if (vx_last != -1 && vy_last != -1 &&
1384 VX != -1 && VY != -1 &&
1385 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1386 BlitBitmap(bitmap_db_store_1, backbuffer,
1387 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1391 if (ex_last != -1 && ey_last != -1 &&
1392 EX != -1 && EY != -1 &&
1393 exsize_last == EXSIZE && eysize_last == EYSIZE)
1394 BlitBitmap(bitmap_db_store_1, backbuffer,
1395 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1398 redraw_mask = REDRAW_ALL;
1401 game_status_last = game_status;
1403 global_border_bitmap_last = global_border_bitmap;
1405 real_sx_last = REAL_SX;
1406 real_sy_last = REAL_SY;
1407 full_sxsize_last = FULL_SXSIZE;
1408 full_sysize_last = FULL_SYSIZE;
1411 dxsize_last = DXSIZE;
1412 dysize_last = DYSIZE;
1415 vxsize_last = VXSIZE;
1416 vysize_last = VYSIZE;
1419 exsize_last = EXSIZE;
1420 eysize_last = EYSIZE;
1423 void ClearField(void)
1425 RedrawGlobalBorderIfNeeded();
1427 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1428 // (when entering hall of fame after playing)
1429 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1431 // !!! maybe this should be done before clearing the background !!!
1432 if (game_status == GAME_MODE_PLAYING)
1434 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1435 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1439 SetDrawtoField(DRAW_TO_BACKBUFFER);
1443 void MarkTileDirty(int x, int y)
1445 redraw_mask |= REDRAW_FIELD;
1448 void SetBorderElement(void)
1452 BorderElement = EL_EMPTY;
1454 // the MM game engine does not use a visible border element
1455 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1458 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1460 for (x = 0; x < lev_fieldx; x++)
1462 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1463 BorderElement = EL_STEELWALL;
1465 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1471 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1472 int max_array_fieldx, int max_array_fieldy,
1473 short field[max_array_fieldx][max_array_fieldy],
1474 int max_fieldx, int max_fieldy)
1478 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1479 static int safety = 0;
1481 // check if starting field still has the desired content
1482 if (field[from_x][from_y] == fill_element)
1487 if (safety > max_fieldx * max_fieldy)
1488 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1490 old_element = field[from_x][from_y];
1491 field[from_x][from_y] = fill_element;
1493 for (i = 0; i < 4; i++)
1495 x = from_x + check[i][0];
1496 y = from_y + check[i][1];
1498 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1499 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1500 field, max_fieldx, max_fieldy);
1506 void FloodFillLevel(int from_x, int from_y, int fill_element,
1507 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1508 int max_fieldx, int max_fieldy)
1510 FloodFillLevelExt(from_x, from_y, fill_element,
1511 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1512 max_fieldx, max_fieldy);
1515 void SetRandomAnimationValue(int x, int y)
1517 gfx.anim_random_frame = GfxRandom[x][y];
1520 int getGraphicAnimationFrame(int graphic, int sync_frame)
1522 // animation synchronized with global frame counter, not move position
1523 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1524 sync_frame = FrameCounter;
1526 return getAnimationFrame(graphic_info[graphic].anim_frames,
1527 graphic_info[graphic].anim_delay,
1528 graphic_info[graphic].anim_mode,
1529 graphic_info[graphic].anim_start_frame,
1533 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1535 struct GraphicInfo *g = &graphic_info[graphic];
1536 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1538 if (tilesize == gfx.standard_tile_size)
1539 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1540 else if (tilesize == game.tile_size)
1541 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1543 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1546 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1547 boolean get_backside)
1549 struct GraphicInfo *g = &graphic_info[graphic];
1550 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1551 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1553 if (g->offset_y == 0) // frames are ordered horizontally
1555 int max_width = g->anim_frames_per_line * g->width;
1556 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1558 *x = pos % max_width;
1559 *y = src_y % g->height + pos / max_width * g->height;
1561 else if (g->offset_x == 0) // frames are ordered vertically
1563 int max_height = g->anim_frames_per_line * g->height;
1564 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1566 *x = src_x % g->width + pos / max_height * g->width;
1567 *y = pos % max_height;
1569 else // frames are ordered diagonally
1571 *x = src_x + frame * g->offset_x;
1572 *y = src_y + frame * g->offset_y;
1576 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1577 Bitmap **bitmap, int *x, int *y,
1578 boolean get_backside)
1580 struct GraphicInfo *g = &graphic_info[graphic];
1582 // if no graphics defined at all, use fallback graphics
1583 if (g->bitmaps == NULL)
1584 *g = graphic_info[IMG_CHAR_EXCLAM];
1586 // if no in-game graphics defined, always use standard graphic size
1587 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1588 tilesize = TILESIZE;
1590 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1591 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1593 *x = *x * tilesize / g->tile_size;
1594 *y = *y * tilesize / g->tile_size;
1597 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1598 Bitmap **bitmap, int *x, int *y)
1600 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1603 void getFixedGraphicSource(int graphic, int frame,
1604 Bitmap **bitmap, int *x, int *y)
1606 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1609 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1611 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1614 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1615 int *x, int *y, boolean get_backside)
1617 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1621 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1623 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1626 void DrawGraphic(int x, int y, int graphic, int frame)
1629 if (!IN_SCR_FIELD(x, y))
1631 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1632 printf("DrawGraphic(): This should never happen!\n");
1637 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1640 MarkTileDirty(x, y);
1643 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1646 if (!IN_SCR_FIELD(x, y))
1648 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1649 printf("DrawGraphic(): This should never happen!\n");
1654 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1656 MarkTileDirty(x, y);
1659 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1665 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1667 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1670 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1676 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1677 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1680 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1683 if (!IN_SCR_FIELD(x, y))
1685 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1686 printf("DrawGraphicThruMask(): This should never happen!\n");
1691 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1694 MarkTileDirty(x, y);
1697 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1700 if (!IN_SCR_FIELD(x, y))
1702 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1703 printf("DrawGraphicThruMask(): This should never happen!\n");
1708 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1710 MarkTileDirty(x, y);
1713 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1719 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1721 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1725 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1726 int graphic, int frame)
1731 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1733 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1737 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1739 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1741 MarkTileDirty(x / tilesize, y / tilesize);
1744 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1747 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1748 graphic, frame, tilesize);
1749 MarkTileDirty(x / tilesize, y / tilesize);
1752 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1758 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1759 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1762 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1763 int frame, int tilesize)
1768 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1769 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1772 void DrawMiniGraphic(int x, int y, int graphic)
1774 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1775 MarkTileDirty(x / 2, y / 2);
1778 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1783 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1784 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1787 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1788 int graphic, int frame,
1789 int cut_mode, int mask_mode)
1794 int width = TILEX, height = TILEY;
1797 if (dx || dy) // shifted graphic
1799 if (x < BX1) // object enters playfield from the left
1806 else if (x > BX2) // object enters playfield from the right
1812 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1818 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1820 else if (dx) // general horizontal movement
1821 MarkTileDirty(x + SIGN(dx), y);
1823 if (y < BY1) // object enters playfield from the top
1825 if (cut_mode == CUT_BELOW) // object completely above top border
1833 else if (y > BY2) // object enters playfield from the bottom
1839 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1845 else if (dy > 0 && cut_mode == CUT_ABOVE)
1847 if (y == BY2) // object completely above bottom border
1853 MarkTileDirty(x, y + 1);
1854 } // object leaves playfield to the bottom
1855 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1857 else if (dy) // general vertical movement
1858 MarkTileDirty(x, y + SIGN(dy));
1862 if (!IN_SCR_FIELD(x, y))
1864 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1865 printf("DrawGraphicShifted(): This should never happen!\n");
1870 width = width * TILESIZE_VAR / TILESIZE;
1871 height = height * TILESIZE_VAR / TILESIZE;
1872 cx = cx * TILESIZE_VAR / TILESIZE;
1873 cy = cy * TILESIZE_VAR / TILESIZE;
1874 dx = dx * TILESIZE_VAR / TILESIZE;
1875 dy = dy * TILESIZE_VAR / TILESIZE;
1877 if (width > 0 && height > 0)
1879 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1884 dst_x = FX + x * TILEX_VAR + dx;
1885 dst_y = FY + y * TILEY_VAR + dy;
1887 if (mask_mode == USE_MASKING)
1888 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1891 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1894 MarkTileDirty(x, y);
1898 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1899 int graphic, int frame,
1900 int cut_mode, int mask_mode)
1905 int width = TILEX_VAR, height = TILEY_VAR;
1908 int x2 = x + SIGN(dx);
1909 int y2 = y + SIGN(dy);
1911 // movement with two-tile animations must be sync'ed with movement position,
1912 // not with current GfxFrame (which can be higher when using slow movement)
1913 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1914 int anim_frames = graphic_info[graphic].anim_frames;
1916 // (we also need anim_delay here for movement animations with less frames)
1917 int anim_delay = graphic_info[graphic].anim_delay;
1918 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1920 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1921 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1923 // re-calculate animation frame for two-tile movement animation
1924 frame = getGraphicAnimationFrame(graphic, sync_frame);
1926 // check if movement start graphic inside screen area and should be drawn
1927 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1929 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1931 dst_x = FX + x1 * TILEX_VAR;
1932 dst_y = FY + y1 * TILEY_VAR;
1934 if (mask_mode == USE_MASKING)
1935 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1938 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1941 MarkTileDirty(x1, y1);
1944 // check if movement end graphic inside screen area and should be drawn
1945 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1947 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1949 dst_x = FX + x2 * TILEX_VAR;
1950 dst_y = FY + y2 * TILEY_VAR;
1952 if (mask_mode == USE_MASKING)
1953 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1956 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1959 MarkTileDirty(x2, y2);
1963 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1964 int graphic, int frame,
1965 int cut_mode, int mask_mode)
1969 DrawGraphic(x, y, graphic, frame);
1974 if (graphic_info[graphic].double_movement) // EM style movement images
1975 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1977 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1980 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1981 int graphic, int frame, int cut_mode)
1983 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1986 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1987 int cut_mode, int mask_mode)
1989 int lx = LEVELX(x), ly = LEVELY(y);
1993 if (IN_LEV_FIELD(lx, ly))
1995 SetRandomAnimationValue(lx, ly);
1997 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1998 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2000 // do not use double (EM style) movement graphic when not moving
2001 if (graphic_info[graphic].double_movement && !dx && !dy)
2003 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
2004 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
2007 else // border element
2009 graphic = el2img(element);
2010 frame = getGraphicAnimationFrame(graphic, -1);
2013 if (element == EL_EXPANDABLE_WALL)
2015 boolean left_stopped = FALSE, right_stopped = FALSE;
2017 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
2018 left_stopped = TRUE;
2019 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
2020 right_stopped = TRUE;
2022 if (left_stopped && right_stopped)
2024 else if (left_stopped)
2026 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
2027 frame = graphic_info[graphic].anim_frames - 1;
2029 else if (right_stopped)
2031 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
2032 frame = graphic_info[graphic].anim_frames - 1;
2037 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
2038 else if (mask_mode == USE_MASKING)
2039 DrawGraphicThruMask(x, y, graphic, frame);
2041 DrawGraphic(x, y, graphic, frame);
2044 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2045 int cut_mode, int mask_mode)
2047 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2048 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2049 cut_mode, mask_mode);
2052 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2055 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2058 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2061 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2064 void DrawLevelElementThruMask(int x, int y, int element)
2066 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2069 void DrawLevelFieldThruMask(int x, int y)
2071 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
2074 // !!! implementation of quicksand is totally broken !!!
2075 #define IS_CRUMBLED_TILE(x, y, e) \
2076 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2077 !IS_MOVING(x, y) || \
2078 (e) == EL_QUICKSAND_EMPTYING || \
2079 (e) == EL_QUICKSAND_FAST_EMPTYING))
2081 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2086 int width, height, cx, cy;
2087 int sx = SCREENX(x), sy = SCREENY(y);
2088 int crumbled_border_size = graphic_info[graphic].border_size;
2089 int crumbled_tile_size = graphic_info[graphic].tile_size;
2090 int crumbled_border_size_var =
2091 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2094 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2096 for (i = 1; i < 4; i++)
2098 int dxx = (i & 1 ? dx : 0);
2099 int dyy = (i & 2 ? dy : 0);
2102 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2105 // check if neighbour field is of same crumble type
2106 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2107 graphic_info[graphic].class ==
2108 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2110 // return if check prevents inner corner
2111 if (same == (dxx == dx && dyy == dy))
2115 // if we reach this point, we have an inner corner
2117 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2119 width = crumbled_border_size_var;
2120 height = crumbled_border_size_var;
2121 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2122 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2124 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2125 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2128 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2133 int width, height, bx, by, cx, cy;
2134 int sx = SCREENX(x), sy = SCREENY(y);
2135 int crumbled_border_size = graphic_info[graphic].border_size;
2136 int crumbled_tile_size = graphic_info[graphic].tile_size;
2137 int crumbled_border_size_var =
2138 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2139 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2142 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2144 // draw simple, sloppy, non-corner-accurate crumbled border
2146 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2147 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2148 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2149 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2151 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2152 FX + sx * TILEX_VAR + cx,
2153 FY + sy * TILEY_VAR + cy);
2155 // (remaining middle border part must be at least as big as corner part)
2156 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2157 crumbled_border_size_var >= TILESIZE_VAR / 3)
2160 // correct corners of crumbled border, if needed
2162 for (i = -1; i <= 1; i += 2)
2164 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2165 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2166 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2169 // check if neighbour field is of same crumble type
2170 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2171 graphic_info[graphic].class ==
2172 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2174 // no crumbled corner, but continued crumbled border
2176 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2177 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2178 int b1 = (i == 1 ? crumbled_border_size_var :
2179 TILESIZE_VAR - 2 * crumbled_border_size_var);
2181 width = crumbled_border_size_var;
2182 height = crumbled_border_size_var;
2184 if (dir == 1 || dir == 2)
2199 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2201 FX + sx * TILEX_VAR + cx,
2202 FY + sy * TILEY_VAR + cy);
2207 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2209 int sx = SCREENX(x), sy = SCREENY(y);
2212 static int xy[4][2] =
2220 if (!IN_LEV_FIELD(x, y))
2223 element = TILE_GFX_ELEMENT(x, y);
2225 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2227 if (!IN_SCR_FIELD(sx, sy))
2230 // crumble field borders towards direct neighbour fields
2231 for (i = 0; i < 4; i++)
2233 int xx = x + xy[i][0];
2234 int yy = y + xy[i][1];
2236 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2239 // check if neighbour field is of same crumble type
2240 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2241 graphic_info[graphic].class ==
2242 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2245 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2248 // crumble inner field corners towards corner neighbour fields
2249 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2250 graphic_info[graphic].anim_frames == 2)
2252 for (i = 0; i < 4; i++)
2254 int dx = (i & 1 ? +1 : -1);
2255 int dy = (i & 2 ? +1 : -1);
2257 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2261 MarkTileDirty(sx, sy);
2263 else // center field is not crumbled -- crumble neighbour fields
2265 // crumble field borders of direct neighbour fields
2266 for (i = 0; i < 4; i++)
2268 int xx = x + xy[i][0];
2269 int yy = y + xy[i][1];
2270 int sxx = sx + xy[i][0];
2271 int syy = sy + xy[i][1];
2273 if (!IN_LEV_FIELD(xx, yy) ||
2274 !IN_SCR_FIELD(sxx, syy))
2277 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2280 element = TILE_GFX_ELEMENT(xx, yy);
2282 if (!IS_CRUMBLED_TILE(xx, yy, element))
2285 graphic = el_act2crm(element, ACTION_DEFAULT);
2287 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2289 MarkTileDirty(sxx, syy);
2292 // crumble inner field corners of corner neighbour fields
2293 for (i = 0; i < 4; i++)
2295 int dx = (i & 1 ? +1 : -1);
2296 int dy = (i & 2 ? +1 : -1);
2302 if (!IN_LEV_FIELD(xx, yy) ||
2303 !IN_SCR_FIELD(sxx, syy))
2306 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2309 element = TILE_GFX_ELEMENT(xx, yy);
2311 if (!IS_CRUMBLED_TILE(xx, yy, element))
2314 graphic = el_act2crm(element, ACTION_DEFAULT);
2316 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2317 graphic_info[graphic].anim_frames == 2)
2318 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2320 MarkTileDirty(sxx, syy);
2325 void DrawLevelFieldCrumbled(int x, int y)
2329 if (!IN_LEV_FIELD(x, y))
2332 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2333 GfxElement[x][y] != EL_UNDEFINED &&
2334 GFX_CRUMBLED(GfxElement[x][y]))
2336 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2341 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2343 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2346 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2349 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2350 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2351 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2352 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2353 int sx = SCREENX(x), sy = SCREENY(y);
2355 DrawGraphic(sx, sy, graphic1, frame1);
2356 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2359 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2361 int sx = SCREENX(x), sy = SCREENY(y);
2362 static int xy[4][2] =
2371 // crumble direct neighbour fields (required for field borders)
2372 for (i = 0; i < 4; i++)
2374 int xx = x + xy[i][0];
2375 int yy = y + xy[i][1];
2376 int sxx = sx + xy[i][0];
2377 int syy = sy + xy[i][1];
2379 if (!IN_LEV_FIELD(xx, yy) ||
2380 !IN_SCR_FIELD(sxx, syy) ||
2381 !GFX_CRUMBLED(Feld[xx][yy]) ||
2385 DrawLevelField(xx, yy);
2388 // crumble corner neighbour fields (required for inner field corners)
2389 for (i = 0; i < 4; i++)
2391 int dx = (i & 1 ? +1 : -1);
2392 int dy = (i & 2 ? +1 : -1);
2398 if (!IN_LEV_FIELD(xx, yy) ||
2399 !IN_SCR_FIELD(sxx, syy) ||
2400 !GFX_CRUMBLED(Feld[xx][yy]) ||
2404 int element = TILE_GFX_ELEMENT(xx, yy);
2405 int graphic = el_act2crm(element, ACTION_DEFAULT);
2407 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2408 graphic_info[graphic].anim_frames == 2)
2409 DrawLevelField(xx, yy);
2413 static int getBorderElement(int x, int y)
2417 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2418 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2419 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2420 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2421 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2422 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2423 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2425 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2426 int steel_position = (x == -1 && y == -1 ? 0 :
2427 x == lev_fieldx && y == -1 ? 1 :
2428 x == -1 && y == lev_fieldy ? 2 :
2429 x == lev_fieldx && y == lev_fieldy ? 3 :
2430 x == -1 || x == lev_fieldx ? 4 :
2431 y == -1 || y == lev_fieldy ? 5 : 6);
2433 return border[steel_position][steel_type];
2436 void DrawScreenElement(int x, int y, int element)
2438 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2439 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2442 void DrawLevelElement(int x, int y, int element)
2444 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2445 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2448 void DrawScreenField(int x, int y)
2450 int lx = LEVELX(x), ly = LEVELY(y);
2451 int element, content;
2453 if (!IN_LEV_FIELD(lx, ly))
2455 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2458 element = getBorderElement(lx, ly);
2460 DrawScreenElement(x, y, element);
2465 element = Feld[lx][ly];
2466 content = Store[lx][ly];
2468 if (IS_MOVING(lx, ly))
2470 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2471 boolean cut_mode = NO_CUTTING;
2473 if (element == EL_QUICKSAND_EMPTYING ||
2474 element == EL_QUICKSAND_FAST_EMPTYING ||
2475 element == EL_MAGIC_WALL_EMPTYING ||
2476 element == EL_BD_MAGIC_WALL_EMPTYING ||
2477 element == EL_DC_MAGIC_WALL_EMPTYING ||
2478 element == EL_AMOEBA_DROPPING)
2479 cut_mode = CUT_ABOVE;
2480 else if (element == EL_QUICKSAND_FILLING ||
2481 element == EL_QUICKSAND_FAST_FILLING ||
2482 element == EL_MAGIC_WALL_FILLING ||
2483 element == EL_BD_MAGIC_WALL_FILLING ||
2484 element == EL_DC_MAGIC_WALL_FILLING)
2485 cut_mode = CUT_BELOW;
2487 if (cut_mode == CUT_ABOVE)
2488 DrawScreenElement(x, y, element);
2490 DrawScreenElement(x, y, EL_EMPTY);
2493 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2494 else if (cut_mode == NO_CUTTING)
2495 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2498 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2500 if (cut_mode == CUT_BELOW &&
2501 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2502 DrawLevelElement(lx, ly + 1, element);
2505 if (content == EL_ACID)
2507 int dir = MovDir[lx][ly];
2508 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2509 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2511 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2513 // prevent target field from being drawn again (but without masking)
2514 // (this would happen if target field is scanned after moving element)
2515 Stop[newlx][newly] = TRUE;
2518 else if (IS_BLOCKED(lx, ly))
2523 boolean cut_mode = NO_CUTTING;
2524 int element_old, content_old;
2526 Blocked2Moving(lx, ly, &oldx, &oldy);
2529 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2530 MovDir[oldx][oldy] == MV_RIGHT);
2532 element_old = Feld[oldx][oldy];
2533 content_old = Store[oldx][oldy];
2535 if (element_old == EL_QUICKSAND_EMPTYING ||
2536 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2537 element_old == EL_MAGIC_WALL_EMPTYING ||
2538 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2539 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2540 element_old == EL_AMOEBA_DROPPING)
2541 cut_mode = CUT_ABOVE;
2543 DrawScreenElement(x, y, EL_EMPTY);
2546 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2548 else if (cut_mode == NO_CUTTING)
2549 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2552 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2555 else if (IS_DRAWABLE(element))
2556 DrawScreenElement(x, y, element);
2558 DrawScreenElement(x, y, EL_EMPTY);
2561 void DrawLevelField(int x, int y)
2563 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2564 DrawScreenField(SCREENX(x), SCREENY(y));
2565 else if (IS_MOVING(x, y))
2569 Moving2Blocked(x, y, &newx, &newy);
2570 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2571 DrawScreenField(SCREENX(newx), SCREENY(newy));
2573 else if (IS_BLOCKED(x, y))
2577 Blocked2Moving(x, y, &oldx, &oldy);
2578 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2579 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2583 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2584 int (*el2img_function)(int), boolean masked,
2585 int element_bits_draw)
2587 int element_base = map_mm_wall_element(element);
2588 int element_bits = (IS_DF_WALL(element) ?
2589 element - EL_DF_WALL_START :
2590 IS_MM_WALL(element) ?
2591 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2592 int graphic = el2img_function(element_base);
2593 int tilesize_draw = tilesize / 2;
2598 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2600 for (i = 0; i < 4; i++)
2602 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2603 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2605 if (!(element_bits_draw & (1 << i)))
2608 if (element_bits & (1 << i))
2611 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2612 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2614 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2615 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2620 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2621 tilesize_draw, tilesize_draw);
2626 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2627 boolean masked, int element_bits_draw)
2629 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2630 element, tilesize, el2edimg, masked, element_bits_draw);
2633 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2634 int (*el2img_function)(int))
2636 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2640 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2643 if (IS_MM_WALL(element))
2645 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2646 element, tilesize, el2edimg, masked, 0x000f);
2650 int graphic = el2edimg(element);
2653 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2655 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2659 void DrawSizedElement(int x, int y, int element, int tilesize)
2661 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2664 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2666 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2669 void DrawMiniElement(int x, int y, int element)
2673 graphic = el2edimg(element);
2674 DrawMiniGraphic(x, y, graphic);
2677 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2680 int x = sx + scroll_x, y = sy + scroll_y;
2682 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2683 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2684 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2685 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2687 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2690 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2692 int x = sx + scroll_x, y = sy + scroll_y;
2694 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2695 DrawMiniElement(sx, sy, EL_EMPTY);
2696 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2697 DrawMiniElement(sx, sy, Feld[x][y]);
2699 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2702 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2703 int x, int y, int xsize, int ysize,
2704 int tile_width, int tile_height)
2708 int dst_x = startx + x * tile_width;
2709 int dst_y = starty + y * tile_height;
2710 int width = graphic_info[graphic].width;
2711 int height = graphic_info[graphic].height;
2712 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2713 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2714 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2715 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2716 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2717 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2718 boolean draw_masked = graphic_info[graphic].draw_masked;
2720 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2722 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2724 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2728 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2729 inner_sx + (x - 1) * tile_width % inner_width);
2730 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2731 inner_sy + (y - 1) * tile_height % inner_height);
2734 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2737 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2741 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2742 int x, int y, int xsize, int ysize,
2745 int font_width = getFontWidth(font_nr);
2746 int font_height = getFontHeight(font_nr);
2748 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2749 font_width, font_height);
2752 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2754 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2755 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2756 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2757 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2758 boolean no_delay = (tape.warp_forward);
2759 unsigned int anim_delay = 0;
2760 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2761 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2762 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2763 int font_width = getFontWidth(font_nr);
2764 int font_height = getFontHeight(font_nr);
2765 int max_xsize = level.envelope[envelope_nr].xsize;
2766 int max_ysize = level.envelope[envelope_nr].ysize;
2767 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2768 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2769 int xend = max_xsize;
2770 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2771 int xstep = (xstart < xend ? 1 : 0);
2772 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2774 int end = MAX(xend - xstart, yend - ystart);
2777 for (i = start; i <= end; i++)
2779 int last_frame = end; // last frame of this "for" loop
2780 int x = xstart + i * xstep;
2781 int y = ystart + i * ystep;
2782 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2783 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2784 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2785 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2788 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2790 BlitScreenToBitmap(backbuffer);
2792 SetDrawtoField(DRAW_TO_BACKBUFFER);
2794 for (yy = 0; yy < ysize; yy++)
2795 for (xx = 0; xx < xsize; xx++)
2796 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2798 DrawTextBuffer(sx + font_width, sy + font_height,
2799 level.envelope[envelope_nr].text, font_nr, max_xsize,
2800 xsize - 2, ysize - 2, 0, mask_mode,
2801 level.envelope[envelope_nr].autowrap,
2802 level.envelope[envelope_nr].centered, FALSE);
2804 redraw_mask |= REDRAW_FIELD;
2807 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2810 ClearAutoRepeatKeyEvents();
2813 void ShowEnvelope(int envelope_nr)
2815 int element = EL_ENVELOPE_1 + envelope_nr;
2816 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2817 int sound_opening = element_info[element].sound[ACTION_OPENING];
2818 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2819 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2820 boolean no_delay = (tape.warp_forward);
2821 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2822 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2823 int anim_mode = graphic_info[graphic].anim_mode;
2824 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2825 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2827 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2829 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2831 if (anim_mode == ANIM_DEFAULT)
2832 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2834 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2837 Delay(wait_delay_value);
2839 WaitForEventToContinue();
2841 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2843 if (anim_mode != ANIM_NONE)
2844 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2846 if (anim_mode == ANIM_DEFAULT)
2847 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2849 game.envelope_active = FALSE;
2851 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2853 redraw_mask |= REDRAW_FIELD;
2857 static void setRequestBasePosition(int *x, int *y)
2859 int sx_base, sy_base;
2861 if (request.x != -1)
2862 sx_base = request.x;
2863 else if (request.align == ALIGN_LEFT)
2865 else if (request.align == ALIGN_RIGHT)
2866 sx_base = SX + SXSIZE;
2868 sx_base = SX + SXSIZE / 2;
2870 if (request.y != -1)
2871 sy_base = request.y;
2872 else if (request.valign == VALIGN_TOP)
2874 else if (request.valign == VALIGN_BOTTOM)
2875 sy_base = SY + SYSIZE;
2877 sy_base = SY + SYSIZE / 2;
2883 static void setRequestPositionExt(int *x, int *y, int width, int height,
2884 boolean add_border_size)
2886 int border_size = request.border_size;
2887 int sx_base, sy_base;
2890 setRequestBasePosition(&sx_base, &sy_base);
2892 if (request.align == ALIGN_LEFT)
2894 else if (request.align == ALIGN_RIGHT)
2895 sx = sx_base - width;
2897 sx = sx_base - width / 2;
2899 if (request.valign == VALIGN_TOP)
2901 else if (request.valign == VALIGN_BOTTOM)
2902 sy = sy_base - height;
2904 sy = sy_base - height / 2;
2906 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2907 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2909 if (add_border_size)
2919 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2921 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2924 static void DrawEnvelopeRequest(char *text)
2926 char *text_final = text;
2927 char *text_door_style = NULL;
2928 int graphic = IMG_BACKGROUND_REQUEST;
2929 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2930 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2931 int font_nr = FONT_REQUEST;
2932 int font_width = getFontWidth(font_nr);
2933 int font_height = getFontHeight(font_nr);
2934 int border_size = request.border_size;
2935 int line_spacing = request.line_spacing;
2936 int line_height = font_height + line_spacing;
2937 int max_text_width = request.width - 2 * border_size;
2938 int max_text_height = request.height - 2 * border_size;
2939 int line_length = max_text_width / font_width;
2940 int max_lines = max_text_height / line_height;
2941 int text_width = line_length * font_width;
2942 int width = request.width;
2943 int height = request.height;
2944 int tile_size = MAX(request.step_offset, 1);
2945 int x_steps = width / tile_size;
2946 int y_steps = height / tile_size;
2947 int sx_offset = border_size;
2948 int sy_offset = border_size;
2952 if (request.centered)
2953 sx_offset = (request.width - text_width) / 2;
2955 if (request.wrap_single_words && !request.autowrap)
2957 char *src_text_ptr, *dst_text_ptr;
2959 text_door_style = checked_malloc(2 * strlen(text) + 1);
2961 src_text_ptr = text;
2962 dst_text_ptr = text_door_style;
2964 while (*src_text_ptr)
2966 if (*src_text_ptr == ' ' ||
2967 *src_text_ptr == '?' ||
2968 *src_text_ptr == '!')
2969 *dst_text_ptr++ = '\n';
2971 if (*src_text_ptr != ' ')
2972 *dst_text_ptr++ = *src_text_ptr;
2977 *dst_text_ptr = '\0';
2979 text_final = text_door_style;
2982 setRequestPosition(&sx, &sy, FALSE);
2984 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2986 for (y = 0; y < y_steps; y++)
2987 for (x = 0; x < x_steps; x++)
2988 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2989 x, y, x_steps, y_steps,
2990 tile_size, tile_size);
2992 // force DOOR font inside door area
2993 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2995 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2996 line_length, -1, max_lines, line_spacing, mask_mode,
2997 request.autowrap, request.centered, FALSE);
3001 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3002 RedrawGadget(tool_gadget[i]);
3004 // store readily prepared envelope request for later use when animating
3005 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3007 if (text_door_style)
3008 free(text_door_style);
3011 static void AnimateEnvelopeRequest(int anim_mode, int action)
3013 int graphic = IMG_BACKGROUND_REQUEST;
3014 boolean draw_masked = graphic_info[graphic].draw_masked;
3015 int delay_value_normal = request.step_delay;
3016 int delay_value_fast = delay_value_normal / 2;
3017 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3018 boolean no_delay = (tape.warp_forward);
3019 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3020 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3021 unsigned int anim_delay = 0;
3023 int tile_size = MAX(request.step_offset, 1);
3024 int max_xsize = request.width / tile_size;
3025 int max_ysize = request.height / tile_size;
3026 int max_xsize_inner = max_xsize - 2;
3027 int max_ysize_inner = max_ysize - 2;
3029 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3030 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3031 int xend = max_xsize_inner;
3032 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3033 int xstep = (xstart < xend ? 1 : 0);
3034 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3036 int end = MAX(xend - xstart, yend - ystart);
3039 if (setup.quick_doors)
3046 for (i = start; i <= end; i++)
3048 int last_frame = end; // last frame of this "for" loop
3049 int x = xstart + i * xstep;
3050 int y = ystart + i * ystep;
3051 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3052 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3053 int xsize_size_left = (xsize - 1) * tile_size;
3054 int ysize_size_top = (ysize - 1) * tile_size;
3055 int max_xsize_pos = (max_xsize - 1) * tile_size;
3056 int max_ysize_pos = (max_ysize - 1) * tile_size;
3057 int width = xsize * tile_size;
3058 int height = ysize * tile_size;
3063 setRequestPosition(&src_x, &src_y, FALSE);
3064 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3066 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3068 for (yy = 0; yy < 2; yy++)
3070 for (xx = 0; xx < 2; xx++)
3072 int src_xx = src_x + xx * max_xsize_pos;
3073 int src_yy = src_y + yy * max_ysize_pos;
3074 int dst_xx = dst_x + xx * xsize_size_left;
3075 int dst_yy = dst_y + yy * ysize_size_top;
3076 int xx_size = (xx ? tile_size : xsize_size_left);
3077 int yy_size = (yy ? tile_size : ysize_size_top);
3080 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3081 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3083 BlitBitmap(bitmap_db_store_2, backbuffer,
3084 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3088 redraw_mask |= REDRAW_FIELD;
3092 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3095 ClearAutoRepeatKeyEvents();
3098 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3100 int graphic = IMG_BACKGROUND_REQUEST;
3101 int sound_opening = SND_REQUEST_OPENING;
3102 int sound_closing = SND_REQUEST_CLOSING;
3103 int anim_mode_1 = request.anim_mode; // (higher priority)
3104 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3105 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3106 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3107 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3109 if (game_status == GAME_MODE_PLAYING)
3110 BlitScreenToBitmap(backbuffer);
3112 SetDrawtoField(DRAW_TO_BACKBUFFER);
3114 // SetDrawBackgroundMask(REDRAW_NONE);
3116 if (action == ACTION_OPENING)
3118 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3120 if (req_state & REQ_ASK)
3122 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3123 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3125 else if (req_state & REQ_CONFIRM)
3127 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3129 else if (req_state & REQ_PLAYER)
3131 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3132 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3133 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3134 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3137 DrawEnvelopeRequest(text);
3140 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3142 if (action == ACTION_OPENING)
3144 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3146 if (anim_mode == ANIM_DEFAULT)
3147 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3149 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3153 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3155 if (anim_mode != ANIM_NONE)
3156 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3158 if (anim_mode == ANIM_DEFAULT)
3159 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3162 game.envelope_active = FALSE;
3164 if (action == ACTION_CLOSING)
3165 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3167 // SetDrawBackgroundMask(last_draw_background_mask);
3169 redraw_mask |= REDRAW_FIELD;
3173 if (action == ACTION_CLOSING &&
3174 game_status == GAME_MODE_PLAYING &&
3175 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3176 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3179 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3181 if (IS_MM_WALL(element))
3183 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3189 int graphic = el2preimg(element);
3191 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3192 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3197 void DrawLevel(int draw_background_mask)
3201 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3202 SetDrawBackgroundMask(draw_background_mask);
3206 for (x = BX1; x <= BX2; x++)
3207 for (y = BY1; y <= BY2; y++)
3208 DrawScreenField(x, y);
3210 redraw_mask |= REDRAW_FIELD;
3213 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3218 for (x = 0; x < size_x; x++)
3219 for (y = 0; y < size_y; y++)
3220 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3222 redraw_mask |= REDRAW_FIELD;
3225 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3229 for (x = 0; x < size_x; x++)
3230 for (y = 0; y < size_y; y++)
3231 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3233 redraw_mask |= REDRAW_FIELD;
3236 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3238 boolean show_level_border = (BorderElement != EL_EMPTY);
3239 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3240 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3241 int tile_size = preview.tile_size;
3242 int preview_width = preview.xsize * tile_size;
3243 int preview_height = preview.ysize * tile_size;
3244 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3245 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3246 int real_preview_width = real_preview_xsize * tile_size;
3247 int real_preview_height = real_preview_ysize * tile_size;
3248 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3249 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3252 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3255 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3257 dst_x += (preview_width - real_preview_width) / 2;
3258 dst_y += (preview_height - real_preview_height) / 2;
3260 for (x = 0; x < real_preview_xsize; x++)
3262 for (y = 0; y < real_preview_ysize; y++)
3264 int lx = from_x + x + (show_level_border ? -1 : 0);
3265 int ly = from_y + y + (show_level_border ? -1 : 0);
3266 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3267 getBorderElement(lx, ly));
3269 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3270 element, tile_size);
3274 redraw_mask |= REDRAW_FIELD;
3277 #define MICROLABEL_EMPTY 0
3278 #define MICROLABEL_LEVEL_NAME 1
3279 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3280 #define MICROLABEL_LEVEL_AUTHOR 3
3281 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3282 #define MICROLABEL_IMPORTED_FROM 5
3283 #define MICROLABEL_IMPORTED_BY_HEAD 6
3284 #define MICROLABEL_IMPORTED_BY 7
3286 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3288 int max_text_width = SXSIZE;
3289 int font_width = getFontWidth(font_nr);
3291 if (pos->align == ALIGN_CENTER)
3292 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3293 else if (pos->align == ALIGN_RIGHT)
3294 max_text_width = pos->x;
3296 max_text_width = SXSIZE - pos->x;
3298 return max_text_width / font_width;
3301 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3303 char label_text[MAX_OUTPUT_LINESIZE + 1];
3304 int max_len_label_text;
3305 int font_nr = pos->font;
3308 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3311 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3312 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3313 mode == MICROLABEL_IMPORTED_BY_HEAD)
3314 font_nr = pos->font_alt;
3316 max_len_label_text = getMaxTextLength(pos, font_nr);
3318 if (pos->size != -1)
3319 max_len_label_text = pos->size;
3321 for (i = 0; i < max_len_label_text; i++)
3322 label_text[i] = ' ';
3323 label_text[max_len_label_text] = '\0';
3325 if (strlen(label_text) > 0)
3326 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3329 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3330 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3331 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3332 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3333 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3334 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3335 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3336 max_len_label_text);
3337 label_text[max_len_label_text] = '\0';
3339 if (strlen(label_text) > 0)
3340 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3342 redraw_mask |= REDRAW_FIELD;
3345 static void DrawPreviewLevelLabel(int mode)
3347 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3350 static void DrawPreviewLevelInfo(int mode)
3352 if (mode == MICROLABEL_LEVEL_NAME)
3353 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3354 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3355 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3358 static void DrawPreviewLevelExt(boolean restart)
3360 static unsigned int scroll_delay = 0;
3361 static unsigned int label_delay = 0;
3362 static int from_x, from_y, scroll_direction;
3363 static int label_state, label_counter;
3364 unsigned int scroll_delay_value = preview.step_delay;
3365 boolean show_level_border = (BorderElement != EL_EMPTY);
3366 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3367 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3374 if (preview.anim_mode == ANIM_CENTERED)
3376 if (level_xsize > preview.xsize)
3377 from_x = (level_xsize - preview.xsize) / 2;
3378 if (level_ysize > preview.ysize)
3379 from_y = (level_ysize - preview.ysize) / 2;
3382 from_x += preview.xoffset;
3383 from_y += preview.yoffset;
3385 scroll_direction = MV_RIGHT;
3389 DrawPreviewLevelPlayfield(from_x, from_y);
3390 DrawPreviewLevelLabel(label_state);
3392 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3393 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3395 // initialize delay counters
3396 DelayReached(&scroll_delay, 0);
3397 DelayReached(&label_delay, 0);
3399 if (leveldir_current->name)
3401 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3402 char label_text[MAX_OUTPUT_LINESIZE + 1];
3403 int font_nr = pos->font;
3404 int max_len_label_text = getMaxTextLength(pos, font_nr);
3406 if (pos->size != -1)
3407 max_len_label_text = pos->size;
3409 strncpy(label_text, leveldir_current->name, max_len_label_text);
3410 label_text[max_len_label_text] = '\0';
3412 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3413 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3419 // scroll preview level, if needed
3420 if (preview.anim_mode != ANIM_NONE &&
3421 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3422 DelayReached(&scroll_delay, scroll_delay_value))
3424 switch (scroll_direction)
3429 from_x -= preview.step_offset;
3430 from_x = (from_x < 0 ? 0 : from_x);
3433 scroll_direction = MV_UP;
3437 if (from_x < level_xsize - preview.xsize)
3439 from_x += preview.step_offset;
3440 from_x = (from_x > level_xsize - preview.xsize ?
3441 level_xsize - preview.xsize : from_x);
3444 scroll_direction = MV_DOWN;
3450 from_y -= preview.step_offset;
3451 from_y = (from_y < 0 ? 0 : from_y);
3454 scroll_direction = MV_RIGHT;
3458 if (from_y < level_ysize - preview.ysize)
3460 from_y += preview.step_offset;
3461 from_y = (from_y > level_ysize - preview.ysize ?
3462 level_ysize - preview.ysize : from_y);
3465 scroll_direction = MV_LEFT;
3472 DrawPreviewLevelPlayfield(from_x, from_y);
3475 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3476 // redraw micro level label, if needed
3477 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3478 !strEqual(level.author, ANONYMOUS_NAME) &&
3479 !strEqual(level.author, leveldir_current->name) &&
3480 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3482 int max_label_counter = 23;
3484 if (leveldir_current->imported_from != NULL &&
3485 strlen(leveldir_current->imported_from) > 0)
3486 max_label_counter += 14;
3487 if (leveldir_current->imported_by != NULL &&
3488 strlen(leveldir_current->imported_by) > 0)
3489 max_label_counter += 14;
3491 label_counter = (label_counter + 1) % max_label_counter;
3492 label_state = (label_counter >= 0 && label_counter <= 7 ?
3493 MICROLABEL_LEVEL_NAME :
3494 label_counter >= 9 && label_counter <= 12 ?
3495 MICROLABEL_LEVEL_AUTHOR_HEAD :
3496 label_counter >= 14 && label_counter <= 21 ?
3497 MICROLABEL_LEVEL_AUTHOR :
3498 label_counter >= 23 && label_counter <= 26 ?
3499 MICROLABEL_IMPORTED_FROM_HEAD :
3500 label_counter >= 28 && label_counter <= 35 ?
3501 MICROLABEL_IMPORTED_FROM :
3502 label_counter >= 37 && label_counter <= 40 ?
3503 MICROLABEL_IMPORTED_BY_HEAD :
3504 label_counter >= 42 && label_counter <= 49 ?
3505 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3507 if (leveldir_current->imported_from == NULL &&
3508 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3509 label_state == MICROLABEL_IMPORTED_FROM))
3510 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3511 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3513 DrawPreviewLevelLabel(label_state);
3517 static void DrawPreviewPlayers(void)
3519 if (game_status != GAME_MODE_MAIN)
3522 if (!network.enabled && !setup.team_mode)
3525 boolean player_found[MAX_PLAYERS];
3526 int num_players = 0;
3529 for (i = 0; i < MAX_PLAYERS; i++)
3530 player_found[i] = FALSE;
3532 // check which players can be found in the level (simple approach)
3533 for (x = 0; x < lev_fieldx; x++)
3535 for (y = 0; y < lev_fieldy; y++)
3537 int element = level.field[x][y];
3539 if (ELEM_IS_PLAYER(element))
3541 int player_nr = GET_PLAYER_NR(element);
3543 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3545 if (!player_found[player_nr])
3548 player_found[player_nr] = TRUE;
3553 struct TextPosInfo *pos = &menu.main.preview_players;
3554 int tile_size = pos->tile_size;
3555 int border_size = pos->border_size;
3556 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3557 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3558 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3559 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3560 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3561 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3562 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3563 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3564 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3565 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3566 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3567 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3569 // clear area in which the players will be drawn
3570 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3571 max_players_width, max_players_height);
3573 // only draw players if level is suited for team mode
3574 if (num_players < 2)
3577 // draw all players that were found in the level
3578 for (i = 0; i < MAX_PLAYERS; i++)
3580 if (player_found[i])
3582 int graphic = el2img(EL_PLAYER_1 + i);
3584 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3586 xpos += player_xoffset;
3587 ypos += player_yoffset;
3592 void DrawPreviewLevelInitial(void)
3594 DrawPreviewLevelExt(TRUE);
3595 DrawPreviewPlayers();
3598 void DrawPreviewLevelAnimation(void)
3600 DrawPreviewLevelExt(FALSE);
3603 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3604 int border_size, int font_nr)
3606 int graphic = el2img(EL_PLAYER_1 + player_nr);
3607 int font_height = getFontHeight(font_nr);
3608 int player_height = MAX(tile_size, font_height);
3609 int xoffset_text = tile_size + border_size;
3610 int yoffset_text = (player_height - font_height) / 2;
3611 int yoffset_graphic = (player_height - tile_size) / 2;
3612 char *player_name = getNetworkPlayerName(player_nr + 1);
3614 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3616 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3619 static void DrawNetworkPlayersExt(boolean force)
3621 if (game_status != GAME_MODE_MAIN)
3624 if (!network.connected && !force)
3627 int num_players = 0;
3630 for (i = 0; i < MAX_PLAYERS; i++)
3631 if (stored_player[i].connected_network)
3634 struct TextPosInfo *pos = &menu.main.network_players;
3635 int tile_size = pos->tile_size;
3636 int border_size = pos->border_size;
3637 int xoffset_text = tile_size + border_size;
3638 int font_nr = pos->font;
3639 int font_width = getFontWidth(font_nr);
3640 int font_height = getFontHeight(font_nr);
3641 int player_height = MAX(tile_size, font_height);
3642 int player_yoffset = player_height + border_size;
3643 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3644 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3645 int all_players_height = num_players * player_yoffset - border_size;
3646 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3647 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3648 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3650 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3651 max_players_width, max_players_height);
3653 // first draw local network player ...
3654 for (i = 0; i < MAX_PLAYERS; i++)
3656 if (stored_player[i].connected_network &&
3657 stored_player[i].connected_locally)
3659 char *player_name = getNetworkPlayerName(i + 1);
3660 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3661 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3663 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3665 ypos += player_yoffset;
3669 // ... then draw all other network players
3670 for (i = 0; i < MAX_PLAYERS; i++)
3672 if (stored_player[i].connected_network &&
3673 !stored_player[i].connected_locally)
3675 char *player_name = getNetworkPlayerName(i + 1);
3676 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3677 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3679 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3681 ypos += player_yoffset;
3686 void DrawNetworkPlayers(void)
3688 DrawNetworkPlayersExt(FALSE);
3691 void ClearNetworkPlayers(void)
3693 DrawNetworkPlayersExt(TRUE);
3696 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3697 int graphic, int sync_frame,
3700 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3702 if (mask_mode == USE_MASKING)
3703 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3705 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3708 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3709 int graphic, int sync_frame, int mask_mode)
3711 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3713 if (mask_mode == USE_MASKING)
3714 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3716 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3719 static void DrawGraphicAnimation(int x, int y, int graphic)
3721 int lx = LEVELX(x), ly = LEVELY(y);
3723 if (!IN_SCR_FIELD(x, y))
3726 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3727 graphic, GfxFrame[lx][ly], NO_MASKING);
3729 MarkTileDirty(x, y);
3732 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3734 int lx = LEVELX(x), ly = LEVELY(y);
3736 if (!IN_SCR_FIELD(x, y))
3739 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3740 graphic, GfxFrame[lx][ly], NO_MASKING);
3741 MarkTileDirty(x, y);
3744 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3746 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3749 void DrawLevelElementAnimation(int x, int y, int element)
3751 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3753 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3756 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3758 int sx = SCREENX(x), sy = SCREENY(y);
3760 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3763 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3766 DrawGraphicAnimation(sx, sy, graphic);
3769 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3770 DrawLevelFieldCrumbled(x, y);
3772 if (GFX_CRUMBLED(Feld[x][y]))
3773 DrawLevelFieldCrumbled(x, y);
3777 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3779 int sx = SCREENX(x), sy = SCREENY(y);
3782 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3785 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3787 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3790 DrawGraphicAnimation(sx, sy, graphic);
3792 if (GFX_CRUMBLED(element))
3793 DrawLevelFieldCrumbled(x, y);
3796 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3798 if (player->use_murphy)
3800 // this works only because currently only one player can be "murphy" ...
3801 static int last_horizontal_dir = MV_LEFT;
3802 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3804 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3805 last_horizontal_dir = move_dir;
3807 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3809 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3811 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3817 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3820 static boolean equalGraphics(int graphic1, int graphic2)
3822 struct GraphicInfo *g1 = &graphic_info[graphic1];
3823 struct GraphicInfo *g2 = &graphic_info[graphic2];
3825 return (g1->bitmap == g2->bitmap &&
3826 g1->src_x == g2->src_x &&
3827 g1->src_y == g2->src_y &&
3828 g1->anim_frames == g2->anim_frames &&
3829 g1->anim_delay == g2->anim_delay &&
3830 g1->anim_mode == g2->anim_mode);
3833 void DrawAllPlayers(void)
3837 for (i = 0; i < MAX_PLAYERS; i++)
3838 if (stored_player[i].active)
3839 DrawPlayer(&stored_player[i]);
3842 void DrawPlayerField(int x, int y)
3844 if (!IS_PLAYER(x, y))
3847 DrawPlayer(PLAYERINFO(x, y));
3850 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3852 void DrawPlayer(struct PlayerInfo *player)
3854 int jx = player->jx;
3855 int jy = player->jy;
3856 int move_dir = player->MovDir;
3857 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3858 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3859 int last_jx = (player->is_moving ? jx - dx : jx);
3860 int last_jy = (player->is_moving ? jy - dy : jy);
3861 int next_jx = jx + dx;
3862 int next_jy = jy + dy;
3863 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3864 boolean player_is_opaque = FALSE;
3865 int sx = SCREENX(jx), sy = SCREENY(jy);
3866 int sxx = 0, syy = 0;
3867 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3869 int action = ACTION_DEFAULT;
3870 int last_player_graphic = getPlayerGraphic(player, move_dir);
3871 int last_player_frame = player->Frame;
3874 // GfxElement[][] is set to the element the player is digging or collecting;
3875 // remove also for off-screen player if the player is not moving anymore
3876 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3877 GfxElement[jx][jy] = EL_UNDEFINED;
3879 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3883 if (!IN_LEV_FIELD(jx, jy))
3885 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3886 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3887 printf("DrawPlayerField(): This should never happen!\n");
3892 if (element == EL_EXPLOSION)
3895 action = (player->is_pushing ? ACTION_PUSHING :
3896 player->is_digging ? ACTION_DIGGING :
3897 player->is_collecting ? ACTION_COLLECTING :
3898 player->is_moving ? ACTION_MOVING :
3899 player->is_snapping ? ACTION_SNAPPING :
3900 player->is_dropping ? ACTION_DROPPING :
3901 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3903 if (player->is_waiting)
3904 move_dir = player->dir_waiting;
3906 InitPlayerGfxAnimation(player, action, move_dir);
3908 // --------------------------------------------------------------------------
3909 // draw things in the field the player is leaving, if needed
3910 // --------------------------------------------------------------------------
3912 if (player->is_moving)
3914 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3916 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3918 if (last_element == EL_DYNAMITE_ACTIVE ||
3919 last_element == EL_EM_DYNAMITE_ACTIVE ||
3920 last_element == EL_SP_DISK_RED_ACTIVE)
3921 DrawDynamite(last_jx, last_jy);
3923 DrawLevelFieldThruMask(last_jx, last_jy);
3925 else if (last_element == EL_DYNAMITE_ACTIVE ||
3926 last_element == EL_EM_DYNAMITE_ACTIVE ||
3927 last_element == EL_SP_DISK_RED_ACTIVE)
3928 DrawDynamite(last_jx, last_jy);
3930 /* !!! this is not enough to prevent flickering of players which are
3931 moving next to each others without a free tile between them -- this
3932 can only be solved by drawing all players layer by layer (first the
3933 background, then the foreground etc.) !!! => TODO */
3934 else if (!IS_PLAYER(last_jx, last_jy))
3935 DrawLevelField(last_jx, last_jy);
3938 DrawLevelField(last_jx, last_jy);
3941 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3942 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3945 if (!IN_SCR_FIELD(sx, sy))
3948 // --------------------------------------------------------------------------
3949 // draw things behind the player, if needed
3950 // --------------------------------------------------------------------------
3953 DrawLevelElement(jx, jy, Back[jx][jy]);
3954 else if (IS_ACTIVE_BOMB(element))
3955 DrawLevelElement(jx, jy, EL_EMPTY);
3958 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3960 int old_element = GfxElement[jx][jy];
3961 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3962 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3964 if (GFX_CRUMBLED(old_element))
3965 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3967 DrawGraphic(sx, sy, old_graphic, frame);
3969 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)