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 // do not crumble fields that are being digged or snapped
2278 if (Feld[xx][yy] == EL_EMPTY ||
2279 Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2282 element = TILE_GFX_ELEMENT(xx, yy);
2284 if (!IS_CRUMBLED_TILE(xx, yy, element))
2287 graphic = el_act2crm(element, ACTION_DEFAULT);
2289 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2291 MarkTileDirty(sxx, syy);
2294 // crumble inner field corners of corner neighbour fields
2295 for (i = 0; i < 4; i++)
2297 int dx = (i & 1 ? +1 : -1);
2298 int dy = (i & 2 ? +1 : -1);
2304 if (!IN_LEV_FIELD(xx, yy) ||
2305 !IN_SCR_FIELD(sxx, syy))
2308 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2311 element = TILE_GFX_ELEMENT(xx, yy);
2313 if (!IS_CRUMBLED_TILE(xx, yy, element))
2316 graphic = el_act2crm(element, ACTION_DEFAULT);
2318 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2319 graphic_info[graphic].anim_frames == 2)
2320 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2322 MarkTileDirty(sxx, syy);
2327 void DrawLevelFieldCrumbled(int x, int y)
2331 if (!IN_LEV_FIELD(x, y))
2334 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2335 GfxElement[x][y] != EL_UNDEFINED &&
2336 GFX_CRUMBLED(GfxElement[x][y]))
2338 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2343 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2345 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2348 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2351 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2352 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2353 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2354 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2355 int sx = SCREENX(x), sy = SCREENY(y);
2357 DrawGraphic(sx, sy, graphic1, frame1);
2358 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2361 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2363 int sx = SCREENX(x), sy = SCREENY(y);
2364 static int xy[4][2] =
2373 // crumble direct neighbour fields (required for field borders)
2374 for (i = 0; i < 4; i++)
2376 int xx = x + xy[i][0];
2377 int yy = y + xy[i][1];
2378 int sxx = sx + xy[i][0];
2379 int syy = sy + xy[i][1];
2381 if (!IN_LEV_FIELD(xx, yy) ||
2382 !IN_SCR_FIELD(sxx, syy) ||
2383 !GFX_CRUMBLED(Feld[xx][yy]) ||
2387 DrawLevelField(xx, yy);
2390 // crumble corner neighbour fields (required for inner field corners)
2391 for (i = 0; i < 4; i++)
2393 int dx = (i & 1 ? +1 : -1);
2394 int dy = (i & 2 ? +1 : -1);
2400 if (!IN_LEV_FIELD(xx, yy) ||
2401 !IN_SCR_FIELD(sxx, syy) ||
2402 !GFX_CRUMBLED(Feld[xx][yy]) ||
2406 int element = TILE_GFX_ELEMENT(xx, yy);
2407 int graphic = el_act2crm(element, ACTION_DEFAULT);
2409 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2410 graphic_info[graphic].anim_frames == 2)
2411 DrawLevelField(xx, yy);
2415 static int getBorderElement(int x, int y)
2419 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2420 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2421 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2422 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2423 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2424 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2425 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2427 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2428 int steel_position = (x == -1 && y == -1 ? 0 :
2429 x == lev_fieldx && y == -1 ? 1 :
2430 x == -1 && y == lev_fieldy ? 2 :
2431 x == lev_fieldx && y == lev_fieldy ? 3 :
2432 x == -1 || x == lev_fieldx ? 4 :
2433 y == -1 || y == lev_fieldy ? 5 : 6);
2435 return border[steel_position][steel_type];
2438 void DrawScreenElement(int x, int y, int element)
2440 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2441 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2444 void DrawLevelElement(int x, int y, int element)
2446 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2447 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2450 void DrawScreenField(int x, int y)
2452 int lx = LEVELX(x), ly = LEVELY(y);
2453 int element, content;
2455 if (!IN_LEV_FIELD(lx, ly))
2457 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2460 element = getBorderElement(lx, ly);
2462 DrawScreenElement(x, y, element);
2467 element = Feld[lx][ly];
2468 content = Store[lx][ly];
2470 if (IS_MOVING(lx, ly))
2472 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2473 boolean cut_mode = NO_CUTTING;
2475 if (element == EL_QUICKSAND_EMPTYING ||
2476 element == EL_QUICKSAND_FAST_EMPTYING ||
2477 element == EL_MAGIC_WALL_EMPTYING ||
2478 element == EL_BD_MAGIC_WALL_EMPTYING ||
2479 element == EL_DC_MAGIC_WALL_EMPTYING ||
2480 element == EL_AMOEBA_DROPPING)
2481 cut_mode = CUT_ABOVE;
2482 else if (element == EL_QUICKSAND_FILLING ||
2483 element == EL_QUICKSAND_FAST_FILLING ||
2484 element == EL_MAGIC_WALL_FILLING ||
2485 element == EL_BD_MAGIC_WALL_FILLING ||
2486 element == EL_DC_MAGIC_WALL_FILLING)
2487 cut_mode = CUT_BELOW;
2489 if (cut_mode == CUT_ABOVE)
2490 DrawScreenElement(x, y, element);
2492 DrawScreenElement(x, y, EL_EMPTY);
2495 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2496 else if (cut_mode == NO_CUTTING)
2497 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2500 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2502 if (cut_mode == CUT_BELOW &&
2503 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2504 DrawLevelElement(lx, ly + 1, element);
2507 if (content == EL_ACID)
2509 int dir = MovDir[lx][ly];
2510 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2511 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2513 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2515 // prevent target field from being drawn again (but without masking)
2516 // (this would happen if target field is scanned after moving element)
2517 Stop[newlx][newly] = TRUE;
2520 else if (IS_BLOCKED(lx, ly))
2525 boolean cut_mode = NO_CUTTING;
2526 int element_old, content_old;
2528 Blocked2Moving(lx, ly, &oldx, &oldy);
2531 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2532 MovDir[oldx][oldy] == MV_RIGHT);
2534 element_old = Feld[oldx][oldy];
2535 content_old = Store[oldx][oldy];
2537 if (element_old == EL_QUICKSAND_EMPTYING ||
2538 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2539 element_old == EL_MAGIC_WALL_EMPTYING ||
2540 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2541 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2542 element_old == EL_AMOEBA_DROPPING)
2543 cut_mode = CUT_ABOVE;
2545 DrawScreenElement(x, y, EL_EMPTY);
2548 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2550 else if (cut_mode == NO_CUTTING)
2551 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2554 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2557 else if (IS_DRAWABLE(element))
2558 DrawScreenElement(x, y, element);
2560 DrawScreenElement(x, y, EL_EMPTY);
2563 void DrawLevelField(int x, int y)
2565 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2566 DrawScreenField(SCREENX(x), SCREENY(y));
2567 else if (IS_MOVING(x, y))
2571 Moving2Blocked(x, y, &newx, &newy);
2572 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2573 DrawScreenField(SCREENX(newx), SCREENY(newy));
2575 else if (IS_BLOCKED(x, y))
2579 Blocked2Moving(x, y, &oldx, &oldy);
2580 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2581 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2585 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2586 int (*el2img_function)(int), boolean masked,
2587 int element_bits_draw)
2589 int element_base = map_mm_wall_element(element);
2590 int element_bits = (IS_DF_WALL(element) ?
2591 element - EL_DF_WALL_START :
2592 IS_MM_WALL(element) ?
2593 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2594 int graphic = el2img_function(element_base);
2595 int tilesize_draw = tilesize / 2;
2600 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2602 for (i = 0; i < 4; i++)
2604 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2605 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2607 if (!(element_bits_draw & (1 << i)))
2610 if (element_bits & (1 << i))
2613 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2614 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2616 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2617 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2622 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2623 tilesize_draw, tilesize_draw);
2628 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2629 boolean masked, int element_bits_draw)
2631 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2632 element, tilesize, el2edimg, masked, element_bits_draw);
2635 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2636 int (*el2img_function)(int))
2638 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2642 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2645 if (IS_MM_WALL(element))
2647 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2648 element, tilesize, el2edimg, masked, 0x000f);
2652 int graphic = el2edimg(element);
2655 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2657 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2661 void DrawSizedElement(int x, int y, int element, int tilesize)
2663 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2666 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2668 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2671 void DrawMiniElement(int x, int y, int element)
2675 graphic = el2edimg(element);
2676 DrawMiniGraphic(x, y, graphic);
2679 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2682 int x = sx + scroll_x, y = sy + scroll_y;
2684 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2685 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2686 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2687 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2689 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2692 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2694 int x = sx + scroll_x, y = sy + scroll_y;
2696 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2697 DrawMiniElement(sx, sy, EL_EMPTY);
2698 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2699 DrawMiniElement(sx, sy, Feld[x][y]);
2701 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2704 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2705 int x, int y, int xsize, int ysize,
2706 int tile_width, int tile_height)
2710 int dst_x = startx + x * tile_width;
2711 int dst_y = starty + y * tile_height;
2712 int width = graphic_info[graphic].width;
2713 int height = graphic_info[graphic].height;
2714 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2715 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2716 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2717 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2718 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2719 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2720 boolean draw_masked = graphic_info[graphic].draw_masked;
2722 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2724 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2726 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2730 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2731 inner_sx + (x - 1) * tile_width % inner_width);
2732 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2733 inner_sy + (y - 1) * tile_height % inner_height);
2736 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2739 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2743 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2744 int x, int y, int xsize, int ysize,
2747 int font_width = getFontWidth(font_nr);
2748 int font_height = getFontHeight(font_nr);
2750 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2751 font_width, font_height);
2754 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2756 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2757 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2758 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2759 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2760 boolean no_delay = (tape.warp_forward);
2761 unsigned int anim_delay = 0;
2762 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2763 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2764 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2765 int font_width = getFontWidth(font_nr);
2766 int font_height = getFontHeight(font_nr);
2767 int max_xsize = level.envelope[envelope_nr].xsize;
2768 int max_ysize = level.envelope[envelope_nr].ysize;
2769 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2770 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2771 int xend = max_xsize;
2772 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2773 int xstep = (xstart < xend ? 1 : 0);
2774 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2776 int end = MAX(xend - xstart, yend - ystart);
2779 for (i = start; i <= end; i++)
2781 int last_frame = end; // last frame of this "for" loop
2782 int x = xstart + i * xstep;
2783 int y = ystart + i * ystep;
2784 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2785 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2786 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2787 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2790 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2792 BlitScreenToBitmap(backbuffer);
2794 SetDrawtoField(DRAW_TO_BACKBUFFER);
2796 for (yy = 0; yy < ysize; yy++)
2797 for (xx = 0; xx < xsize; xx++)
2798 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2800 DrawTextBuffer(sx + font_width, sy + font_height,
2801 level.envelope[envelope_nr].text, font_nr, max_xsize,
2802 xsize - 2, ysize - 2, 0, mask_mode,
2803 level.envelope[envelope_nr].autowrap,
2804 level.envelope[envelope_nr].centered, FALSE);
2806 redraw_mask |= REDRAW_FIELD;
2809 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2812 ClearAutoRepeatKeyEvents();
2815 void ShowEnvelope(int envelope_nr)
2817 int element = EL_ENVELOPE_1 + envelope_nr;
2818 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2819 int sound_opening = element_info[element].sound[ACTION_OPENING];
2820 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2821 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2822 boolean no_delay = (tape.warp_forward);
2823 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2824 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2825 int anim_mode = graphic_info[graphic].anim_mode;
2826 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2827 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2829 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2831 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2833 if (anim_mode == ANIM_DEFAULT)
2834 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2836 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2839 Delay(wait_delay_value);
2841 WaitForEventToContinue();
2843 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2845 if (anim_mode != ANIM_NONE)
2846 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2848 if (anim_mode == ANIM_DEFAULT)
2849 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2851 game.envelope_active = FALSE;
2853 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2855 redraw_mask |= REDRAW_FIELD;
2859 static void setRequestBasePosition(int *x, int *y)
2861 int sx_base, sy_base;
2863 if (request.x != -1)
2864 sx_base = request.x;
2865 else if (request.align == ALIGN_LEFT)
2867 else if (request.align == ALIGN_RIGHT)
2868 sx_base = SX + SXSIZE;
2870 sx_base = SX + SXSIZE / 2;
2872 if (request.y != -1)
2873 sy_base = request.y;
2874 else if (request.valign == VALIGN_TOP)
2876 else if (request.valign == VALIGN_BOTTOM)
2877 sy_base = SY + SYSIZE;
2879 sy_base = SY + SYSIZE / 2;
2885 static void setRequestPositionExt(int *x, int *y, int width, int height,
2886 boolean add_border_size)
2888 int border_size = request.border_size;
2889 int sx_base, sy_base;
2892 setRequestBasePosition(&sx_base, &sy_base);
2894 if (request.align == ALIGN_LEFT)
2896 else if (request.align == ALIGN_RIGHT)
2897 sx = sx_base - width;
2899 sx = sx_base - width / 2;
2901 if (request.valign == VALIGN_TOP)
2903 else if (request.valign == VALIGN_BOTTOM)
2904 sy = sy_base - height;
2906 sy = sy_base - height / 2;
2908 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2909 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2911 if (add_border_size)
2921 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2923 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2926 static void DrawEnvelopeRequest(char *text)
2928 char *text_final = text;
2929 char *text_door_style = NULL;
2930 int graphic = IMG_BACKGROUND_REQUEST;
2931 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2932 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2933 int font_nr = FONT_REQUEST;
2934 int font_width = getFontWidth(font_nr);
2935 int font_height = getFontHeight(font_nr);
2936 int border_size = request.border_size;
2937 int line_spacing = request.line_spacing;
2938 int line_height = font_height + line_spacing;
2939 int max_text_width = request.width - 2 * border_size;
2940 int max_text_height = request.height - 2 * border_size;
2941 int line_length = max_text_width / font_width;
2942 int max_lines = max_text_height / line_height;
2943 int text_width = line_length * font_width;
2944 int width = request.width;
2945 int height = request.height;
2946 int tile_size = MAX(request.step_offset, 1);
2947 int x_steps = width / tile_size;
2948 int y_steps = height / tile_size;
2949 int sx_offset = border_size;
2950 int sy_offset = border_size;
2954 if (request.centered)
2955 sx_offset = (request.width - text_width) / 2;
2957 if (request.wrap_single_words && !request.autowrap)
2959 char *src_text_ptr, *dst_text_ptr;
2961 text_door_style = checked_malloc(2 * strlen(text) + 1);
2963 src_text_ptr = text;
2964 dst_text_ptr = text_door_style;
2966 while (*src_text_ptr)
2968 if (*src_text_ptr == ' ' ||
2969 *src_text_ptr == '?' ||
2970 *src_text_ptr == '!')
2971 *dst_text_ptr++ = '\n';
2973 if (*src_text_ptr != ' ')
2974 *dst_text_ptr++ = *src_text_ptr;
2979 *dst_text_ptr = '\0';
2981 text_final = text_door_style;
2984 setRequestPosition(&sx, &sy, FALSE);
2986 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2988 for (y = 0; y < y_steps; y++)
2989 for (x = 0; x < x_steps; x++)
2990 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2991 x, y, x_steps, y_steps,
2992 tile_size, tile_size);
2994 // force DOOR font inside door area
2995 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2997 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2998 line_length, -1, max_lines, line_spacing, mask_mode,
2999 request.autowrap, request.centered, FALSE);
3003 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3004 RedrawGadget(tool_gadget[i]);
3006 // store readily prepared envelope request for later use when animating
3007 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3009 if (text_door_style)
3010 free(text_door_style);
3013 static void AnimateEnvelopeRequest(int anim_mode, int action)
3015 int graphic = IMG_BACKGROUND_REQUEST;
3016 boolean draw_masked = graphic_info[graphic].draw_masked;
3017 int delay_value_normal = request.step_delay;
3018 int delay_value_fast = delay_value_normal / 2;
3019 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3020 boolean no_delay = (tape.warp_forward);
3021 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3022 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3023 unsigned int anim_delay = 0;
3025 int tile_size = MAX(request.step_offset, 1);
3026 int max_xsize = request.width / tile_size;
3027 int max_ysize = request.height / tile_size;
3028 int max_xsize_inner = max_xsize - 2;
3029 int max_ysize_inner = max_ysize - 2;
3031 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3032 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3033 int xend = max_xsize_inner;
3034 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3035 int xstep = (xstart < xend ? 1 : 0);
3036 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3038 int end = MAX(xend - xstart, yend - ystart);
3041 if (setup.quick_doors)
3048 for (i = start; i <= end; i++)
3050 int last_frame = end; // last frame of this "for" loop
3051 int x = xstart + i * xstep;
3052 int y = ystart + i * ystep;
3053 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3054 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3055 int xsize_size_left = (xsize - 1) * tile_size;
3056 int ysize_size_top = (ysize - 1) * tile_size;
3057 int max_xsize_pos = (max_xsize - 1) * tile_size;
3058 int max_ysize_pos = (max_ysize - 1) * tile_size;
3059 int width = xsize * tile_size;
3060 int height = ysize * tile_size;
3065 setRequestPosition(&src_x, &src_y, FALSE);
3066 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3068 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3070 for (yy = 0; yy < 2; yy++)
3072 for (xx = 0; xx < 2; xx++)
3074 int src_xx = src_x + xx * max_xsize_pos;
3075 int src_yy = src_y + yy * max_ysize_pos;
3076 int dst_xx = dst_x + xx * xsize_size_left;
3077 int dst_yy = dst_y + yy * ysize_size_top;
3078 int xx_size = (xx ? tile_size : xsize_size_left);
3079 int yy_size = (yy ? tile_size : ysize_size_top);
3082 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3083 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3085 BlitBitmap(bitmap_db_store_2, backbuffer,
3086 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3090 redraw_mask |= REDRAW_FIELD;
3094 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3097 ClearAutoRepeatKeyEvents();
3100 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3102 int graphic = IMG_BACKGROUND_REQUEST;
3103 int sound_opening = SND_REQUEST_OPENING;
3104 int sound_closing = SND_REQUEST_CLOSING;
3105 int anim_mode_1 = request.anim_mode; // (higher priority)
3106 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3107 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3108 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3109 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3111 if (game_status == GAME_MODE_PLAYING)
3112 BlitScreenToBitmap(backbuffer);
3114 SetDrawtoField(DRAW_TO_BACKBUFFER);
3116 // SetDrawBackgroundMask(REDRAW_NONE);
3118 if (action == ACTION_OPENING)
3120 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3122 if (req_state & REQ_ASK)
3124 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3125 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3127 else if (req_state & REQ_CONFIRM)
3129 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3131 else if (req_state & REQ_PLAYER)
3133 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3134 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3135 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3136 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3139 DrawEnvelopeRequest(text);
3142 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3144 if (action == ACTION_OPENING)
3146 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3148 if (anim_mode == ANIM_DEFAULT)
3149 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3151 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3155 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3157 if (anim_mode != ANIM_NONE)
3158 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3160 if (anim_mode == ANIM_DEFAULT)
3161 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3164 game.envelope_active = FALSE;
3166 if (action == ACTION_CLOSING)
3167 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3169 // SetDrawBackgroundMask(last_draw_background_mask);
3171 redraw_mask |= REDRAW_FIELD;
3175 if (action == ACTION_CLOSING &&
3176 game_status == GAME_MODE_PLAYING &&
3177 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3178 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3181 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3183 if (IS_MM_WALL(element))
3185 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3191 int graphic = el2preimg(element);
3193 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3194 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3199 void DrawLevel(int draw_background_mask)
3203 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3204 SetDrawBackgroundMask(draw_background_mask);
3208 for (x = BX1; x <= BX2; x++)
3209 for (y = BY1; y <= BY2; y++)
3210 DrawScreenField(x, y);
3212 redraw_mask |= REDRAW_FIELD;
3215 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3220 for (x = 0; x < size_x; x++)
3221 for (y = 0; y < size_y; y++)
3222 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3224 redraw_mask |= REDRAW_FIELD;
3227 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3231 for (x = 0; x < size_x; x++)
3232 for (y = 0; y < size_y; y++)
3233 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3235 redraw_mask |= REDRAW_FIELD;
3238 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3240 boolean show_level_border = (BorderElement != EL_EMPTY);
3241 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3242 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3243 int tile_size = preview.tile_size;
3244 int preview_width = preview.xsize * tile_size;
3245 int preview_height = preview.ysize * tile_size;
3246 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3247 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3248 int real_preview_width = real_preview_xsize * tile_size;
3249 int real_preview_height = real_preview_ysize * tile_size;
3250 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3251 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3254 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3257 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3259 dst_x += (preview_width - real_preview_width) / 2;
3260 dst_y += (preview_height - real_preview_height) / 2;
3262 for (x = 0; x < real_preview_xsize; x++)
3264 for (y = 0; y < real_preview_ysize; y++)
3266 int lx = from_x + x + (show_level_border ? -1 : 0);
3267 int ly = from_y + y + (show_level_border ? -1 : 0);
3268 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3269 getBorderElement(lx, ly));
3271 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3272 element, tile_size);
3276 redraw_mask |= REDRAW_FIELD;
3279 #define MICROLABEL_EMPTY 0
3280 #define MICROLABEL_LEVEL_NAME 1
3281 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3282 #define MICROLABEL_LEVEL_AUTHOR 3
3283 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3284 #define MICROLABEL_IMPORTED_FROM 5
3285 #define MICROLABEL_IMPORTED_BY_HEAD 6
3286 #define MICROLABEL_IMPORTED_BY 7
3288 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3290 int max_text_width = SXSIZE;
3291 int font_width = getFontWidth(font_nr);
3293 if (pos->align == ALIGN_CENTER)
3294 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3295 else if (pos->align == ALIGN_RIGHT)
3296 max_text_width = pos->x;
3298 max_text_width = SXSIZE - pos->x;
3300 return max_text_width / font_width;
3303 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3305 char label_text[MAX_OUTPUT_LINESIZE + 1];
3306 int max_len_label_text;
3307 int font_nr = pos->font;
3310 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3313 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3314 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3315 mode == MICROLABEL_IMPORTED_BY_HEAD)
3316 font_nr = pos->font_alt;
3318 max_len_label_text = getMaxTextLength(pos, font_nr);
3320 if (pos->size != -1)
3321 max_len_label_text = pos->size;
3323 for (i = 0; i < max_len_label_text; i++)
3324 label_text[i] = ' ';
3325 label_text[max_len_label_text] = '\0';
3327 if (strlen(label_text) > 0)
3328 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3331 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3332 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3333 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3334 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3335 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3336 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3337 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3338 max_len_label_text);
3339 label_text[max_len_label_text] = '\0';
3341 if (strlen(label_text) > 0)
3342 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3344 redraw_mask |= REDRAW_FIELD;
3347 static void DrawPreviewLevelLabel(int mode)
3349 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3352 static void DrawPreviewLevelInfo(int mode)
3354 if (mode == MICROLABEL_LEVEL_NAME)
3355 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3356 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3357 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3360 static void DrawPreviewLevelExt(boolean restart)
3362 static unsigned int scroll_delay = 0;
3363 static unsigned int label_delay = 0;
3364 static int from_x, from_y, scroll_direction;
3365 static int label_state, label_counter;
3366 unsigned int scroll_delay_value = preview.step_delay;
3367 boolean show_level_border = (BorderElement != EL_EMPTY);
3368 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3369 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3376 if (preview.anim_mode == ANIM_CENTERED)
3378 if (level_xsize > preview.xsize)
3379 from_x = (level_xsize - preview.xsize) / 2;
3380 if (level_ysize > preview.ysize)
3381 from_y = (level_ysize - preview.ysize) / 2;
3384 from_x += preview.xoffset;
3385 from_y += preview.yoffset;
3387 scroll_direction = MV_RIGHT;
3391 DrawPreviewLevelPlayfield(from_x, from_y);
3392 DrawPreviewLevelLabel(label_state);
3394 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3395 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3397 // initialize delay counters
3398 DelayReached(&scroll_delay, 0);
3399 DelayReached(&label_delay, 0);
3401 if (leveldir_current->name)
3403 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3404 char label_text[MAX_OUTPUT_LINESIZE + 1];
3405 int font_nr = pos->font;
3406 int max_len_label_text = getMaxTextLength(pos, font_nr);
3408 if (pos->size != -1)
3409 max_len_label_text = pos->size;
3411 strncpy(label_text, leveldir_current->name, max_len_label_text);
3412 label_text[max_len_label_text] = '\0';
3414 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3415 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3421 // scroll preview level, if needed
3422 if (preview.anim_mode != ANIM_NONE &&
3423 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3424 DelayReached(&scroll_delay, scroll_delay_value))
3426 switch (scroll_direction)
3431 from_x -= preview.step_offset;
3432 from_x = (from_x < 0 ? 0 : from_x);
3435 scroll_direction = MV_UP;
3439 if (from_x < level_xsize - preview.xsize)
3441 from_x += preview.step_offset;
3442 from_x = (from_x > level_xsize - preview.xsize ?
3443 level_xsize - preview.xsize : from_x);
3446 scroll_direction = MV_DOWN;
3452 from_y -= preview.step_offset;
3453 from_y = (from_y < 0 ? 0 : from_y);
3456 scroll_direction = MV_RIGHT;
3460 if (from_y < level_ysize - preview.ysize)
3462 from_y += preview.step_offset;
3463 from_y = (from_y > level_ysize - preview.ysize ?
3464 level_ysize - preview.ysize : from_y);
3467 scroll_direction = MV_LEFT;
3474 DrawPreviewLevelPlayfield(from_x, from_y);
3477 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3478 // redraw micro level label, if needed
3479 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3480 !strEqual(level.author, ANONYMOUS_NAME) &&
3481 !strEqual(level.author, leveldir_current->name) &&
3482 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3484 int max_label_counter = 23;
3486 if (leveldir_current->imported_from != NULL &&
3487 strlen(leveldir_current->imported_from) > 0)
3488 max_label_counter += 14;
3489 if (leveldir_current->imported_by != NULL &&
3490 strlen(leveldir_current->imported_by) > 0)
3491 max_label_counter += 14;
3493 label_counter = (label_counter + 1) % max_label_counter;
3494 label_state = (label_counter >= 0 && label_counter <= 7 ?
3495 MICROLABEL_LEVEL_NAME :
3496 label_counter >= 9 && label_counter <= 12 ?
3497 MICROLABEL_LEVEL_AUTHOR_HEAD :
3498 label_counter >= 14 && label_counter <= 21 ?
3499 MICROLABEL_LEVEL_AUTHOR :
3500 label_counter >= 23 && label_counter <= 26 ?
3501 MICROLABEL_IMPORTED_FROM_HEAD :
3502 label_counter >= 28 && label_counter <= 35 ?
3503 MICROLABEL_IMPORTED_FROM :
3504 label_counter >= 37 && label_counter <= 40 ?
3505 MICROLABEL_IMPORTED_BY_HEAD :
3506 label_counter >= 42 && label_counter <= 49 ?
3507 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3509 if (leveldir_current->imported_from == NULL &&
3510 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3511 label_state == MICROLABEL_IMPORTED_FROM))
3512 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3513 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3515 DrawPreviewLevelLabel(label_state);
3519 static void DrawPreviewPlayers(void)
3521 if (game_status != GAME_MODE_MAIN)
3524 if (!network.enabled && !setup.team_mode)
3527 boolean player_found[MAX_PLAYERS];
3528 int num_players = 0;
3531 for (i = 0; i < MAX_PLAYERS; i++)
3532 player_found[i] = FALSE;
3534 // check which players can be found in the level (simple approach)
3535 for (x = 0; x < lev_fieldx; x++)
3537 for (y = 0; y < lev_fieldy; y++)
3539 int element = level.field[x][y];
3541 if (ELEM_IS_PLAYER(element))
3543 int player_nr = GET_PLAYER_NR(element);
3545 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3547 if (!player_found[player_nr])
3550 player_found[player_nr] = TRUE;
3555 struct TextPosInfo *pos = &menu.main.preview_players;
3556 int tile_size = pos->tile_size;
3557 int border_size = pos->border_size;
3558 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3559 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3560 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3561 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3562 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3563 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3564 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3565 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3566 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3567 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3568 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3569 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3571 // clear area in which the players will be drawn
3572 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3573 max_players_width, max_players_height);
3575 // only draw players if level is suited for team mode
3576 if (num_players < 2)
3579 // draw all players that were found in the level
3580 for (i = 0; i < MAX_PLAYERS; i++)
3582 if (player_found[i])
3584 int graphic = el2img(EL_PLAYER_1 + i);
3586 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3588 xpos += player_xoffset;
3589 ypos += player_yoffset;
3594 void DrawPreviewLevelInitial(void)
3596 DrawPreviewLevelExt(TRUE);
3597 DrawPreviewPlayers();
3600 void DrawPreviewLevelAnimation(void)
3602 DrawPreviewLevelExt(FALSE);
3605 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3606 int border_size, int font_nr)
3608 int graphic = el2img(EL_PLAYER_1 + player_nr);
3609 int font_height = getFontHeight(font_nr);
3610 int player_height = MAX(tile_size, font_height);
3611 int xoffset_text = tile_size + border_size;
3612 int yoffset_text = (player_height - font_height) / 2;
3613 int yoffset_graphic = (player_height - tile_size) / 2;
3614 char *player_name = getNetworkPlayerName(player_nr + 1);
3616 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3618 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3621 static void DrawNetworkPlayersExt(boolean force)
3623 if (game_status != GAME_MODE_MAIN)
3626 if (!network.connected && !force)
3629 int num_players = 0;
3632 for (i = 0; i < MAX_PLAYERS; i++)
3633 if (stored_player[i].connected_network)
3636 struct TextPosInfo *pos = &menu.main.network_players;
3637 int tile_size = pos->tile_size;
3638 int border_size = pos->border_size;
3639 int xoffset_text = tile_size + border_size;
3640 int font_nr = pos->font;
3641 int font_width = getFontWidth(font_nr);
3642 int font_height = getFontHeight(font_nr);
3643 int player_height = MAX(tile_size, font_height);
3644 int player_yoffset = player_height + border_size;
3645 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3646 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3647 int all_players_height = num_players * player_yoffset - border_size;
3648 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3649 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3650 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3652 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3653 max_players_width, max_players_height);
3655 // first draw local network player ...
3656 for (i = 0; i < MAX_PLAYERS; i++)
3658 if (stored_player[i].connected_network &&
3659 stored_player[i].connected_locally)
3661 char *player_name = getNetworkPlayerName(i + 1);
3662 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3663 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3665 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3667 ypos += player_yoffset;
3671 // ... then draw all other network players
3672 for (i = 0; i < MAX_PLAYERS; i++)
3674 if (stored_player[i].connected_network &&
3675 !stored_player[i].connected_locally)
3677 char *player_name = getNetworkPlayerName(i + 1);
3678 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3679 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3681 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3683 ypos += player_yoffset;
3688 void DrawNetworkPlayers(void)
3690 DrawNetworkPlayersExt(FALSE);
3693 void ClearNetworkPlayers(void)
3695 DrawNetworkPlayersExt(TRUE);
3698 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3699 int graphic, int sync_frame,
3702 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3704 if (mask_mode == USE_MASKING)
3705 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3707 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3710 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3711 int graphic, int sync_frame, int mask_mode)
3713 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3715 if (mask_mode == USE_MASKING)
3716 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3718 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3721 static void DrawGraphicAnimation(int x, int y, int graphic)
3723 int lx = LEVELX(x), ly = LEVELY(y);
3725 if (!IN_SCR_FIELD(x, y))
3728 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3729 graphic, GfxFrame[lx][ly], NO_MASKING);
3731 MarkTileDirty(x, y);
3734 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3736 int lx = LEVELX(x), ly = LEVELY(y);
3738 if (!IN_SCR_FIELD(x, y))
3741 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3742 graphic, GfxFrame[lx][ly], NO_MASKING);
3743 MarkTileDirty(x, y);
3746 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3748 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3751 void DrawLevelElementAnimation(int x, int y, int element)
3753 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3755 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3758 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3760 int sx = SCREENX(x), sy = SCREENY(y);
3762 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3765 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3768 DrawGraphicAnimation(sx, sy, graphic);
3771 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3772 DrawLevelFieldCrumbled(x, y);
3774 if (GFX_CRUMBLED(Feld[x][y]))
3775 DrawLevelFieldCrumbled(x, y);
3779 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3781 int sx = SCREENX(x), sy = SCREENY(y);
3784 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3787 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3789 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3792 DrawGraphicAnimation(sx, sy, graphic);
3794 if (GFX_CRUMBLED(element))
3795 DrawLevelFieldCrumbled(x, y);
3798 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3800 if (player->use_murphy)
3802 // this works only because currently only one player can be "murphy" ...
3803 static int last_horizontal_dir = MV_LEFT;
3804 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3806 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3807 last_horizontal_dir = move_dir;
3809 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3811 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3813 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3819 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3822 static boolean equalGraphics(int graphic1, int graphic2)
3824 struct GraphicInfo *g1 = &graphic_info[graphic1];
3825 struct GraphicInfo *g2 = &graphic_info[graphic2];
3827 return (g1->bitmap == g2->bitmap &&
3828 g1->src_x == g2->src_x &&
3829 g1->src_y == g2->src_y &&
3830 g1->anim_frames == g2->anim_frames &&
3831 g1->anim_delay == g2->anim_delay &&
3832 g1->anim_mode == g2->anim_mode);
3835 void DrawAllPlayers(void)
3839 for (i = 0; i < MAX_PLAYERS; i++)
3840 if (stored_player[i].active)
3841 DrawPlayer(&stored_player[i]);
3844 void DrawPlayerField(int x, int y)
3846 if (!IS_PLAYER(x, y))
3849 DrawPlayer(PLAYERINFO(x, y));
3852 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3854 void DrawPlayer(struct PlayerInfo *player)
3856 int jx = player->jx;
3857 int jy = player->jy;
3858 int move_dir = player->MovDir;
3859 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3860 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3861 int last_jx = (player->is_moving ? jx - dx : jx);
3862 int last_jy = (player->is_moving ? jy - dy : jy);
3863 int next_jx = jx + dx;
3864 int next_jy = jy + dy;
3865 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3866 boolean player_is_opaque = FALSE;
3867 int sx = SCREENX(jx), sy = SCREENY(jy);
3868 int sxx = 0, syy = 0;
3869 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3871 int action = ACTION_DEFAULT;
3872 int last_player_graphic = getPlayerGraphic(player, move_dir);
3873 int last_player_frame = player->Frame;
3876 // GfxElement[][] is set to the element the player is digging or collecting;
3877 // remove also for off-screen player if the player is not moving anymore
3878 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3879 GfxElement[jx][jy] = EL_UNDEFINED;
3881 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3885 if (!IN_LEV_FIELD(jx, jy))
3887 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3888 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3889 printf("DrawPlayerField(): This should never happen!\n");
3894 if (element == EL_EXPLOSION)
3897 action = (player->is_pushing ? ACTION_PUSHING :
3898 player->is_digging ? ACTION_DIGGING :
3899 player->is_collecting ? ACTION_COLLECTING :
3900 player->is_moving ? ACTION_MOVING :
3901 player->is_snapping ? ACTION_SNAPPING :
3902 player->is_dropping ? ACTION_DROPPING :
3903 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3905 if (player->is_waiting)
3906 move_dir = player->dir_waiting;
3908 InitPlayerGfxAnimation(player, action, move_dir);
3910 // --------------------------------------------------------------------------
3911 // draw things in the field the player is leaving, if needed
3912 // --------------------------------------------------------------------------
3914 if (player->is_moving)
3916 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3918 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3920 if (last_element == EL_DYNAMITE_ACTIVE ||
3921 last_element == EL_EM_DYNAMITE_ACTIVE ||
3922 last_element == EL_SP_DISK_RED_ACTIVE)
3923 DrawDynamite(last_jx, last_jy);
3925 DrawLevelFieldThruMask(last_jx, last_jy);
3927 else if (last_element == EL_DYNAMITE_ACTIVE ||
3928 last_element == EL_EM_DYNAMITE_ACTIVE ||
3929 last_element == EL_SP_DISK_RED_ACTIVE)
3930 DrawDynamite(last_jx, last_jy);
3932 /* !!! this is not enough to prevent flickering of players which are
3933 moving next to each others without a free tile between them -- this
3934 can only be solved by drawing all players layer by layer (first the
3935 background, then the foreground etc.) !!! => TODO */
3936 else if (!IS_PLAYER(last_jx, last_jy))
3937 DrawLevelField(last_jx, last_jy);
3940 DrawLevelField(last_jx, last_jy);
3943 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3944 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3947 if (!IN_SCR_FIELD(sx, sy))
3950 // --------------------------------------------------------------------------
3951 // draw things behind the player, if needed
3952 // --------------------------------------------------------------------------
3955 DrawLevelElement(jx, jy, Back[jx][jy]);
3956 else if (IS_ACTIVE_BOMB(element))
3957 DrawLevelElement(jx, jy, EL_EMPTY);
3960 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3962 int old_element = GfxElement[jx][jy];
3963 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3964 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3966 if (GFX_CRUMBLED(old_element))
3967 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3969 DrawGraphic(sx, sy, old_graphic, frame);
3971 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3972 player_is_opaque = TRUE;
3976 GfxElement[jx][jy] = EL_UNDEFINED;
3978 // make sure that pushed elements are drawn with correct frame rate
3979 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3981 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3982 GfxFrame[jx][jy] = player->StepFrame;
3984 DrawLevelField(jx, jy);
3988 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3989 // -----------------------------------------------------------------------
3990 // draw player himself
3991 // -----------------------------------------------------------------------
3993 graphic = getPlayerGraphic(player, move_dir);
3995 // in the case of changed player action or direction, prevent the current
3996 // animation frame from being restarted for identical animations
3997 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3998 player->Frame = last_player_frame;
4000 frame = getGraphicAnimationFrame(graphic, player->Frame);
4004 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4005 sxx = player->GfxPos;
4007 syy = player->GfxPos;
4010 if (player_is_opaque)
4011 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
4013 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4015 if (SHIELD_ON(player))
4017 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4018 IMG_SHIELD_NORMAL_ACTIVE);
4019 int frame = getGraphicAnimationFrame(graphic, -1);
4021 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4025 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4028 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4029 sxx = player->GfxPos;
4031 syy = player->GfxPos;
4035 // --------------------------------------------------------------------------
4036 // draw things the player is pushing, if needed
4037 // --------------------------------------------------------------------------
4039 if (player->is_pushing && player->is_moving)
4041 int px = SCREENX(jx), py = SCREENY(jy);
4042 int pxx = (TILEX - ABS(sxx)) * dx;
4043 int pyy = (TILEY - ABS(syy)) * dy;
4044 int gfx_frame = GfxFrame[jx][jy];
4050 if (!IS_MOVING(jx, jy)) // push movement already finished
4052 element = Feld[next_jx][next_jy];
4053 gfx_frame = GfxFrame[next_jx][next_jy];
4056 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
4058 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
4059 frame = getGraphicAnimationFrame(graphic, sync_frame);
4061 // draw background element under pushed element (like the Sokoban field)
4062 if (game.use_masked_pushing && IS_MOVING(jx, jy))
4064 // this allows transparent pushing animation over non-black background
4067 DrawLevelElement(jx, jy, Back[jx][jy]);
4069 DrawLevelElement(jx, jy, EL_EMPTY);
4071 if (Back[next_jx][next_jy])
4072 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4074 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
4076 else if (Back[next_jx][next_jy])
4077 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
4080 // do not draw (EM style) pushing animation when pushing is finished
4081 // (two-tile animations usually do not contain start and end frame)
4082 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
4083 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
4085 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4087 // masked drawing is needed for EMC style (double) movement graphics
4088 // !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!!
4089 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
4093 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
4094 // -----------------------------------------------------------------------
4095 // draw player himself
4096 // -----------------------------------------------------------------------
4098 graphic = getPlayerGraphic(player, move_dir);
4100 // in the case of changed player action or direction, prevent the current
4101 // animation frame from being restarted for identical animations
4102 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
4103 player->Frame = last_player_frame;
4105 frame = getGraphicAnimationFrame(graphic, player->Frame);
4109 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
4110 sxx = player->GfxPos;
4112 syy = player->GfxPos;
4115 if (player_is_opaque)
4116 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
4118 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4120 if (SHIELD_ON(player))
4122 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
4123 IMG_SHIELD_NORMAL_ACTIVE);
4124 int frame = getGraphicAnimationFrame(graphic, -1);
4126 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
4130 // --------------------------------------------------------------------------
4131 // draw things in front of player (active dynamite or dynabombs)
4132 // --------------------------------------------------------------------------
4134 if (IS_ACTIVE_BOMB(element))
4136 graphic = el2img(element);
4137 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
4139 if (game.emulation == EMU_SUPAPLEX)
4140 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
4142 DrawGraphicThruMask(sx, sy, graphic, frame);
4145 if (player_is_moving && last_element == EL_EXPLOSION)
4147 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
4148 GfxElement[last_jx][last_jy] : EL_EMPTY);
4149 int graphic = el_act2img(element, ACTION_EXPLODING);
4150 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4151 int phase = ExplodePhase[last_jx][last_jy] - 1;
4152 int frame = getGraphicAnimationFrame(graphic, phase - delay);
4155 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
4158 // --------------------------------------------------------------------------
4159 // draw elements the player is just walking/passing through/under
4160 // --------------------------------------------------------------------------
4162 if (player_is_moving)
4164 // handle the field the player is leaving ...
4165 if (IS_ACCESSIBLE_INSIDE(last_element))
4166 DrawLevelField(last_jx, last_jy);
4167 else if (IS_ACCESSIBLE_UNDER(last_element))
4168 DrawLevelFieldThruMask(last_jx, last_jy);
4171 // do not redraw accessible elements if the player is just pushing them
4172 if (!player_is_moving || !player->is_pushing)
4174 // ... and the field the player is entering
4175 if (IS_ACCESSIBLE_INSIDE(element))
4176 DrawLevelField(jx, jy);
4177 else if (IS_ACCESSIBLE_UNDER(element))
4178 DrawLevelFieldThruMask(jx, jy);
4181 MarkTileDirty(sx, sy);
4184 // ----------------------------------------------------------------------------
4186 void WaitForEventToContinue(void)
4188 boolean still_wait = TRUE;
4190 if (program.headless)
4193 // simulate releasing mouse button over last gadget, if still pressed
4195 HandleGadgets(-1, -1, 0);
4197 button_status = MB_RELEASED;
4205 if (NextValidEvent(&event))
4209 case EVENT_BUTTONRELEASE:
4210 case EVENT_KEYPRESS:
4211 case SDL_CONTROLLERBUTTONDOWN:
4212 case SDL_JOYBUTTONDOWN:
4216 case EVENT_KEYRELEASE:
4217 ClearPlayerAction();
4221 HandleOtherEvents(&event);
4225 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4234 #define MAX_REQUEST_LINES 13
4235 #define MAX_REQUEST_LINE_FONT1_LEN 7
4236 #define MAX_REQUEST_LINE_FONT2_LEN 10
4238 static int RequestHandleEvents(unsigned int req_state)
4240 boolean game_just_ended = (game_status == GAME_MODE_PLAYING &&
4242 int width = request.width;
4243 int height = request.height;
4247 // when showing request dialog after game ended, deactivate game panel
4248 if (game_just_ended)
4249 game.panel.active = FALSE;
4251 game.request_active = TRUE;
4253 setRequestPosition(&sx, &sy, FALSE);
4255 button_status = MB_RELEASED;
4257 request_gadget_id = -1;
4262 if (game_just_ended)
4264 // the MM game engine does not use a special (scrollable) field buffer
4265 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
4266 SetDrawtoField(DRAW_TO_FIELDBUFFER);
4268 HandleGameActions();
4270 SetDrawtoField(DRAW_TO_BACKBUFFER);
4272 if (global.use_envelope_request)
4274 // copy current state of request area to middle of playfield area
4275 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4283 while (NextValidEvent(&event))
4287 case EVENT_BUTTONPRESS:
4288 case EVENT_BUTTONRELEASE:
4289 case EVENT_MOTIONNOTIFY:
4293 if (event.type == EVENT_MOTIONNOTIFY)
4298 motion_status = TRUE;
4299 mx = ((MotionEvent *) &event)->x;
4300 my = ((MotionEvent *) &event)->y;
4304 motion_status = FALSE;
4305 mx = ((ButtonEvent *) &event)->x;
4306 my = ((ButtonEvent *) &event)->y;
4307 if (event.type == EVENT_BUTTONPRESS)
4308 button_status = ((ButtonEvent *) &event)->button;
4310 button_status = MB_RELEASED;
4313 // this sets 'request_gadget_id'
4314 HandleGadgets(mx, my, button_status);
4316 switch (request_gadget_id)
4318 case TOOL_CTRL_ID_YES:
4321 case TOOL_CTRL_ID_NO:
4324 case TOOL_CTRL_ID_CONFIRM:
4325 result = TRUE | FALSE;
4328 case TOOL_CTRL_ID_PLAYER_1:
4331 case TOOL_CTRL_ID_PLAYER_2:
4334 case TOOL_CTRL_ID_PLAYER_3:
4337 case TOOL_CTRL_ID_PLAYER_4:
4348 case SDL_WINDOWEVENT:
4349 HandleWindowEvent((WindowEvent *) &event);
4352 case SDL_APP_WILLENTERBACKGROUND:
4353 case SDL_APP_DIDENTERBACKGROUND:
4354 case SDL_APP_WILLENTERFOREGROUND:
4355 case SDL_APP_DIDENTERFOREGROUND:
4356 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4359 case EVENT_KEYPRESS:
4361 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4366 if (req_state & REQ_CONFIRM)
4375 #if defined(KSYM_Rewind)
4376 case KSYM_Rewind: // for Amazon Fire TV remote
4385 #if defined(KSYM_FastForward)
4386 case KSYM_FastForward: // for Amazon Fire TV remote
4392 HandleKeysDebug(key, KEY_PRESSED);
4396 if (req_state & REQ_PLAYER)
4398 int old_player_nr = setup.network_player_nr;
4401 result = old_player_nr + 1;
4406 result = old_player_nr + 1;
4437 case EVENT_KEYRELEASE:
4438 ClearPlayerAction();
4441 case SDL_CONTROLLERBUTTONDOWN:
4442 switch (event.cbutton.button)
4444 case SDL_CONTROLLER_BUTTON_A:
4445 case SDL_CONTROLLER_BUTTON_X:
4446 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4447 case SDL_CONTROLLER_BUTTON_LEFTSTICK:
4451 case SDL_CONTROLLER_BUTTON_B:
4452 case SDL_CONTROLLER_BUTTON_Y:
4453 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4454 case SDL_CONTROLLER_BUTTON_RIGHTSTICK:
4455 case SDL_CONTROLLER_BUTTON_BACK:
4460 if (req_state & REQ_PLAYER)
4462 int old_player_nr = setup.network_player_nr;
4465 result = old_player_nr + 1;
4467 switch (event.cbutton.button)
4469 case SDL_CONTROLLER_BUTTON_DPAD_UP:
4470 case SDL_CONTROLLER_BUTTON_Y:
4474 case SDL_CONTROLLER_BUTTON_DPAD_RIGHT:
4475 case SDL_CONTROLLER_BUTTON_B:
4479 case SDL_CONTROLLER_BUTTON_DPAD_DOWN:
4480 case SDL_CONTROLLER_BUTTON_A:
4484 case SDL_CONTROLLER_BUTTON_DPAD_LEFT:
4485 case SDL_CONTROLLER_BUTTON_X:
4496 case SDL_CONTROLLERBUTTONUP:
4497 HandleJoystickEvent(&event);
4498 ClearPlayerAction();
4502 HandleOtherEvents(&event);
4507 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4509 int joy = AnyJoystick();
4511 if (joy & JOY_BUTTON_1)
4513 else if (joy & JOY_BUTTON_2)
4516 else if (AnyJoystick())
4518 int joy = AnyJoystick();
4520 if (req_state & REQ_PLAYER)
4524 else if (joy & JOY_RIGHT)
4526 else if (joy & JOY_DOWN)
4528 else if (joy & JOY_LEFT)
4533 if (game_just_ended)
4535 if (global.use_envelope_request)
4537 // copy back current state of pressed buttons inside request area
4538 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4545 game.request_active = FALSE;
4550 static boolean RequestDoor(char *text, unsigned int req_state)
4552 unsigned int old_door_state;
4553 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4554 int font_nr = FONT_TEXT_2;
4559 if (maxWordLengthInRequestString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4561 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4562 font_nr = FONT_TEXT_1;
4565 if (game_status == GAME_MODE_PLAYING)
4566 BlitScreenToBitmap(backbuffer);
4568 // disable deactivated drawing when quick-loading level tape recording
4569 if (tape.playing && tape.deactivate_display)
4570 TapeDeactivateDisplayOff(TRUE);
4572 SetMouseCursor(CURSOR_DEFAULT);
4574 // pause network game while waiting for request to answer
4575 if (network.enabled &&
4576 game_status == GAME_MODE_PLAYING &&
4577 !game.all_players_gone &&
4578 req_state & REQUEST_WAIT_FOR_INPUT)
4579 SendToServer_PausePlaying();
4581 old_door_state = GetDoorState();
4583 // simulate releasing mouse button over last gadget, if still pressed
4585 HandleGadgets(-1, -1, 0);
4589 // draw released gadget before proceeding
4592 if (old_door_state & DOOR_OPEN_1)
4594 CloseDoor(DOOR_CLOSE_1);
4596 // save old door content
4597 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4598 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4601 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4602 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4604 // clear door drawing field
4605 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4607 // force DOOR font inside door area
4608 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4610 // write text for request
4611 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4613 char text_line[max_request_line_len + 1];
4619 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4621 tc = *(text_ptr + tx);
4622 // if (!tc || tc == ' ')
4623 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4627 if ((tc == '?' || tc == '!') && tl == 0)
4637 strncpy(text_line, text_ptr, tl);
4640 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4641 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4642 text_line, font_nr);
4644 text_ptr += tl + (tc == ' ' ? 1 : 0);
4645 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4650 if (req_state & REQ_ASK)
4652 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4653 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4655 else if (req_state & REQ_CONFIRM)
4657 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4659 else if (req_state & REQ_PLAYER)
4661 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4662 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4663 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4664 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4667 // copy request gadgets to door backbuffer
4668 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4670 OpenDoor(DOOR_OPEN_1);
4672 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4674 if (game_status == GAME_MODE_PLAYING)
4676 SetPanelBackground();
4677 SetDrawBackgroundMask(REDRAW_DOOR_1);
4681 SetDrawBackgroundMask(REDRAW_FIELD);
4687 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4689 // ---------- handle request buttons ----------
4690 result = RequestHandleEvents(req_state);
4694 if (!(req_state & REQ_STAY_OPEN))
4696 CloseDoor(DOOR_CLOSE_1);
4698 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4699 (req_state & REQ_REOPEN))
4700 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4705 if (game_status == GAME_MODE_PLAYING)
4707 SetPanelBackground();
4708 SetDrawBackgroundMask(REDRAW_DOOR_1);
4712 SetDrawBackgroundMask(REDRAW_FIELD);
4715 // continue network game after request
4716 if (network.enabled &&
4717 game_status == GAME_MODE_PLAYING &&
4718 !game.all_players_gone &&
4719 req_state & REQUEST_WAIT_FOR_INPUT)
4720 SendToServer_ContinuePlaying();
4722 // restore deactivated drawing when quick-loading level tape recording
4723 if (tape.playing && tape.deactivate_display)
4724 TapeDeactivateDisplayOn();
4729 static boolean RequestEnvelope(char *text, unsigned int req_state)
4733 if (game_status == GAME_MODE_PLAYING)
4734 BlitScreenToBitmap(backbuffer);
4736 // disable deactivated drawing when quick-loading level tape recording
4737 if (tape.playing && tape.deactivate_display)
4738 TapeDeactivateDisplayOff(TRUE);
4740 SetMouseCursor(CURSOR_DEFAULT);
4742 // pause network game while waiting for request to answer
4743 if (network.enabled &&
4744 game_status == GAME_MODE_PLAYING &&
4745 !game.all_players_gone &&
4746 req_state & REQUEST_WAIT_FOR_INPUT)
4747 SendToServer_PausePlaying();
4749 // simulate releasing mouse button over last gadget, if still pressed
4751 HandleGadgets(-1, -1, 0);