1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 #define DEBUG_FRAME_TIME FALSE
28 // tool button identifiers
29 #define TOOL_CTRL_ID_YES 0
30 #define TOOL_CTRL_ID_NO 1
31 #define TOOL_CTRL_ID_CONFIRM 2
32 #define TOOL_CTRL_ID_PLAYER_1 3
33 #define TOOL_CTRL_ID_PLAYER_2 4
34 #define TOOL_CTRL_ID_PLAYER_3 5
35 #define TOOL_CTRL_ID_PLAYER_4 6
36 #define TOOL_CTRL_ID_TOUCH_YES 7
37 #define TOOL_CTRL_ID_TOUCH_NO 8
38 #define TOOL_CTRL_ID_TOUCH_CONFIRM 9
40 #define NUM_TOOL_BUTTONS 10
42 // constants for number of doors and door parts
44 #define NUM_PANELS NUM_DOORS
45 // #define NUM_PANELS 0
46 #define MAX_PARTS_PER_DOOR 8
47 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
48 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
51 struct DoorPartOrderInfo
57 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
59 struct DoorPartControlInfo
63 struct DoorPartPosInfo *pos;
66 static struct DoorPartControlInfo door_part_controls[] =
70 IMG_GFX_DOOR_1_PART_1,
75 IMG_GFX_DOOR_1_PART_2,
80 IMG_GFX_DOOR_1_PART_3,
85 IMG_GFX_DOOR_1_PART_4,
90 IMG_GFX_DOOR_1_PART_5,
95 IMG_GFX_DOOR_1_PART_6,
100 IMG_GFX_DOOR_1_PART_7,
105 IMG_GFX_DOOR_1_PART_8,
111 IMG_GFX_DOOR_2_PART_1,
116 IMG_GFX_DOOR_2_PART_2,
121 IMG_GFX_DOOR_2_PART_3,
126 IMG_GFX_DOOR_2_PART_4,
131 IMG_GFX_DOOR_2_PART_5,
136 IMG_GFX_DOOR_2_PART_6,
141 IMG_GFX_DOOR_2_PART_7,
146 IMG_GFX_DOOR_2_PART_8,
152 IMG_BACKGROUND_PANEL,
169 // forward declaration for internal use
170 static void UnmapToolButtons(void);
171 static void HandleToolButtons(struct GadgetInfo *);
172 static int el_act_dir2crm(int, int, int);
173 static int el_act2crm(int, int);
175 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
176 static int request_gadget_id = -1;
178 static char *print_if_not_empty(int element)
180 static char *s = NULL;
181 char *token_name = element_info[element].token_name;
186 s = checked_malloc(strlen(token_name) + 10 + 1);
188 if (element != EL_EMPTY)
189 sprintf(s, "%d\t['%s']", element, token_name);
191 sprintf(s, "%d", element);
196 int getFieldbufferOffsetX_RND(int dir, int pos)
198 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
199 int dx = (dir & MV_HORIZONTAL ? pos : 0);
200 int dx_var = dx * TILESIZE_VAR / TILESIZE;
203 if (EVEN(SCR_FIELDX))
205 int sbx_right = SBX_Right + (BorderElement != EL_EMPTY ? 1 : 0);
206 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
208 if (ffx < sbx_right * TILEX_VAR + TILEX_VAR / 2)
209 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
211 fx += (dx_var > 0 ? TILEX_VAR : 0);
218 if (full_lev_fieldx <= SCR_FIELDX)
220 if (EVEN(SCR_FIELDX))
221 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
223 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
229 int getFieldbufferOffsetY_RND(int dir, int pos)
231 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
232 int dy = (dir & MV_VERTICAL ? pos : 0);
233 int dy_var = dy * TILESIZE_VAR / TILESIZE;
236 if (EVEN(SCR_FIELDY))
238 int sby_lower = SBY_Lower + (BorderElement != EL_EMPTY ? 1 : 0);
239 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
241 if (ffy < sby_lower * TILEY_VAR + TILEY_VAR / 2)
242 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
244 fy += (dy_var > 0 ? TILEY_VAR : 0);
251 if (full_lev_fieldy <= SCR_FIELDY)
253 if (EVEN(SCR_FIELDY))
254 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
256 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
262 static int getLevelFromScreenX_RND(int sx)
264 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
267 int lx = LEVELX((px + dx) / TILESIZE_VAR);
272 static int getLevelFromScreenY_RND(int sy)
274 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
277 int ly = LEVELY((py + dy) / TILESIZE_VAR);
282 static int getLevelFromScreenX_EM(int sx)
284 int level_xsize = level.native_em_level->cav->width;
285 int full_xsize = level_xsize * TILESIZE_VAR;
287 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
289 int fx = getFieldbufferOffsetX_EM();
292 int lx = LEVELX((px + dx) / TILESIZE_VAR);
297 static int getLevelFromScreenY_EM(int sy)
299 int level_ysize = level.native_em_level->cav->height;
300 int full_ysize = level_ysize * TILESIZE_VAR;
302 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
304 int fy = getFieldbufferOffsetY_EM();
307 int ly = LEVELY((py + dy) / TILESIZE_VAR);
312 static int getLevelFromScreenX_SP(int sx)
314 int menBorder = setup.sp_show_border_elements;
315 int level_xsize = level.native_sp_level->width;
316 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
318 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
320 int fx = getFieldbufferOffsetX_SP();
323 int lx = LEVELX((px + dx) / TILESIZE_VAR);
328 static int getLevelFromScreenY_SP(int sy)
330 int menBorder = setup.sp_show_border_elements;
331 int level_ysize = level.native_sp_level->height;
332 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
334 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
336 int fy = getFieldbufferOffsetY_SP();
339 int ly = LEVELY((py + dy) / TILESIZE_VAR);
344 static int getLevelFromScreenX_MM(int sx)
346 int level_xsize = level.native_mm_level->fieldx;
347 int full_xsize = level_xsize * TILESIZE_VAR;
349 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
352 int lx = (px + TILESIZE_VAR) / TILESIZE_VAR - 1;
357 static int getLevelFromScreenY_MM(int sy)
359 int level_ysize = level.native_mm_level->fieldy;
360 int full_ysize = level_ysize * TILESIZE_VAR;
362 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
365 int ly = (py + TILESIZE_VAR) / TILESIZE_VAR - 1;
370 int getLevelFromScreenX(int x)
372 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
373 return getLevelFromScreenX_EM(x);
374 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
375 return getLevelFromScreenX_SP(x);
376 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
377 return getLevelFromScreenX_MM(x);
379 return getLevelFromScreenX_RND(x);
382 int getLevelFromScreenY(int y)
384 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
385 return getLevelFromScreenY_EM(y);
386 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
387 return getLevelFromScreenY_SP(y);
388 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
389 return getLevelFromScreenY_MM(y);
391 return getLevelFromScreenY_RND(y);
394 int getScreenFieldSizeX(void)
396 return (tape.playing ? tape.scr_fieldx : SCR_FIELDX);
399 int getScreenFieldSizeY(void)
401 return (tape.playing ? tape.scr_fieldy : SCR_FIELDY);
404 void DumpTile(int x, int y)
411 Info("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)", sx, sy, x, y);
414 if (!IN_LEV_FIELD(x, y))
416 Info("(not in level field)");
422 token_name = element_info[Tile[x][y]].token_name;
424 Info("Tile: %d\t['%s']", Tile[x][y], token_name);
425 Info("Back: %s", print_if_not_empty(Back[x][y]));
426 Info("Store: %s", print_if_not_empty(Store[x][y]));
427 Info("Store2: %s", print_if_not_empty(Store2[x][y]));
428 Info("StorePlayer: %s", print_if_not_empty(StorePlayer[x][y]));
429 Info("MovPos: %d", MovPos[x][y]);
430 Info("MovDir: %d", MovDir[x][y]);
431 Info("MovDelay: %d", MovDelay[x][y]);
432 Info("ChangeDelay: %d", ChangeDelay[x][y]);
433 Info("CustomValue: %d", CustomValue[x][y]);
434 Info("GfxElement: %d", GfxElement[x][y]);
435 Info("GfxAction: %d", GfxAction[x][y]);
436 Info("GfxFrame: %d [%d]", GfxFrame[x][y], FrameCounter);
437 Info("Player x/y: %d, %d", local_player->jx, local_player->jy);
441 void DumpTileFromScreen(int sx, int sy)
443 int lx = getLevelFromScreenX(sx);
444 int ly = getLevelFromScreenY(sy);
449 void SetDrawtoField(int mode)
451 if (mode == DRAW_TO_FIELDBUFFER)
457 BX2 = SCR_FIELDX + 1;
458 BY2 = SCR_FIELDY + 1;
460 drawto_field = fieldbuffer;
462 else // DRAW_TO_BACKBUFFER
468 BX2 = SCR_FIELDX - 1;
469 BY2 = SCR_FIELDY - 1;
471 drawto_field = backbuffer;
475 int GetDrawtoField(void)
477 return (drawto_field == fieldbuffer ? DRAW_TO_FIELDBUFFER : DRAW_TO_BACKBUFFER);
480 static void RedrawPlayfield_RND(void)
482 if (game.envelope_active)
485 DrawLevel(REDRAW_ALL);
489 void RedrawPlayfield(void)
491 if (game_status != GAME_MODE_PLAYING)
494 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
495 RedrawPlayfield_EM(TRUE);
496 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
497 RedrawPlayfield_SP(TRUE);
498 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
499 RedrawPlayfield_MM();
500 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
501 RedrawPlayfield_RND();
503 BlitScreenToBitmap(backbuffer);
505 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
509 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
512 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
513 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
515 if (x == -1 && y == -1)
518 if (draw_target == DRAW_TO_SCREEN)
519 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
521 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
524 static void DrawMaskedBorderExt_FIELD(int draw_target)
526 if (global.border_status >= GAME_MODE_MAIN &&
527 global.border_status <= GAME_MODE_PLAYING &&
528 border.draw_masked[global.border_status])
529 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
533 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
535 // when drawing to backbuffer, never draw border over open doors
536 if (draw_target == DRAW_TO_BACKBUFFER &&
537 (GetDoorState() & DOOR_OPEN_1))
540 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
541 (global.border_status != GAME_MODE_EDITOR ||
542 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
543 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
546 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
548 // when drawing to backbuffer, never draw border over open doors
549 if (draw_target == DRAW_TO_BACKBUFFER &&
550 (GetDoorState() & DOOR_OPEN_2))
553 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
554 global.border_status != GAME_MODE_EDITOR)
555 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
558 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
560 // currently not available
563 static void DrawMaskedBorderExt_ALL(int draw_target)
565 DrawMaskedBorderExt_FIELD(draw_target);
566 DrawMaskedBorderExt_DOOR_1(draw_target);
567 DrawMaskedBorderExt_DOOR_2(draw_target);
568 DrawMaskedBorderExt_DOOR_3(draw_target);
571 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
573 // never draw masked screen borders on borderless screens
574 if (global.border_status == GAME_MODE_LOADING ||
575 global.border_status == GAME_MODE_TITLE)
578 if (redraw_mask & REDRAW_ALL)
579 DrawMaskedBorderExt_ALL(draw_target);
582 if (redraw_mask & REDRAW_FIELD)
583 DrawMaskedBorderExt_FIELD(draw_target);
584 if (redraw_mask & REDRAW_DOOR_1)
585 DrawMaskedBorderExt_DOOR_1(draw_target);
586 if (redraw_mask & REDRAW_DOOR_2)
587 DrawMaskedBorderExt_DOOR_2(draw_target);
588 if (redraw_mask & REDRAW_DOOR_3)
589 DrawMaskedBorderExt_DOOR_3(draw_target);
593 void DrawMaskedBorder_FIELD(void)
595 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
598 void DrawMaskedBorder(int redraw_mask)
600 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
603 void DrawMaskedBorderToTarget(int draw_target)
605 if (draw_target == DRAW_TO_BACKBUFFER ||
606 draw_target == DRAW_TO_SCREEN)
608 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
612 int last_border_status = global.border_status;
614 if (draw_target == DRAW_TO_FADE_SOURCE)
616 global.border_status = gfx.fade_border_source_status;
617 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
619 else if (draw_target == DRAW_TO_FADE_TARGET)
621 global.border_status = gfx.fade_border_target_status;
622 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
625 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
627 global.border_status = last_border_status;
628 gfx.masked_border_bitmap_ptr = backbuffer;
632 void DrawTileCursor(int draw_target)
634 DrawTileCursor_MM(draw_target, game_status == GAME_MODE_PLAYING);
637 void BlitScreenToBitmapExt_RND(Bitmap *target_bitmap, int fx, int fy)
639 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
642 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
644 int fx = getFieldbufferOffsetX_RND(ScreenMovDir, ScreenGfxPos);
645 int fy = getFieldbufferOffsetY_RND(ScreenMovDir, ScreenGfxPos);
647 BlitScreenToBitmapExt_RND(target_bitmap, fx, fy);
650 void BlitScreenToBitmap(Bitmap *target_bitmap)
652 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
653 BlitScreenToBitmap_EM(target_bitmap);
654 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
655 BlitScreenToBitmap_SP(target_bitmap);
656 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
657 BlitScreenToBitmap_MM(target_bitmap);
658 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
659 BlitScreenToBitmap_RND(target_bitmap);
661 redraw_mask |= REDRAW_FIELD;
664 static void DrawFramesPerSecond(void)
667 int font_nr = FONT_TEXT_2;
668 int font_width = getFontWidth(font_nr);
669 int draw_deactivation_mask = GetDrawDeactivationMask();
670 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
672 // draw FPS with leading space (needed if field buffer deactivated)
673 sprintf(text, " %04.1f fps", global.frames_per_second);
675 // override draw deactivation mask (required for invisible warp mode)
676 SetDrawDeactivationMask(REDRAW_NONE);
678 // draw opaque FPS if field buffer deactivated, else draw masked FPS
679 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
680 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
682 // set draw deactivation mask to previous value
683 SetDrawDeactivationMask(draw_deactivation_mask);
685 // force full-screen redraw in this frame
686 redraw_mask = REDRAW_ALL;
690 static void PrintFrameTimeDebugging(void)
692 static unsigned int last_counter = 0;
693 unsigned int counter = Counter();
694 int diff_1 = counter - last_counter;
695 int diff_2 = diff_1 - GAME_FRAME_DELAY;
697 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
698 char diff_bar[2 * diff_2_max + 5];
702 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
704 for (i = 0; i < diff_2_max; i++)
705 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
706 i >= diff_2_max - diff_2_cut ? '-' : ' ');
708 diff_bar[pos++] = '|';
710 for (i = 0; i < diff_2_max; i++)
711 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
713 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
715 diff_bar[pos++] = '\0';
717 Debug("time:frame", "%06d [%02d] [%c%02d] %s",
720 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
723 last_counter = counter;
727 static int unifiedRedrawMask(int mask)
729 if (mask & REDRAW_ALL)
732 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
738 static boolean equalRedrawMasks(int mask_1, int mask_2)
740 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
743 void BackToFront(void)
745 static int last_redraw_mask = REDRAW_NONE;
747 // force screen redraw in every frame to continue drawing global animations
748 // (but always use the last redraw mask to prevent unwanted side effects)
749 if (redraw_mask == REDRAW_NONE)
750 redraw_mask = last_redraw_mask;
752 last_redraw_mask = redraw_mask;
755 // masked border now drawn immediately when blitting backbuffer to window
757 // draw masked border to all viewports, if defined
758 DrawMaskedBorder(redraw_mask);
761 // draw frames per second (only if debug mode is enabled)
762 if (redraw_mask & REDRAW_FPS)
763 DrawFramesPerSecond();
765 // remove playfield redraw before potentially merging with doors redraw
766 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
767 redraw_mask &= ~REDRAW_FIELD;
769 // redraw complete window if both playfield and (some) doors need redraw
770 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
771 redraw_mask = REDRAW_ALL;
773 /* although redrawing the whole window would be fine for normal gameplay,
774 being able to only redraw the playfield is required for deactivating
775 certain drawing areas (mainly playfield) to work, which is needed for
776 warp-forward to be fast enough (by skipping redraw of most frames) */
778 if (redraw_mask & REDRAW_ALL)
780 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
782 else if (redraw_mask & REDRAW_FIELD)
784 BlitBitmap(backbuffer, window,
785 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
787 else if (redraw_mask & REDRAW_DOORS)
789 // merge door areas to prevent calling screen redraw more than once
795 if (redraw_mask & REDRAW_DOOR_1)
799 x2 = MAX(x2, DX + DXSIZE);
800 y2 = MAX(y2, DY + DYSIZE);
803 if (redraw_mask & REDRAW_DOOR_2)
807 x2 = MAX(x2, VX + VXSIZE);
808 y2 = MAX(y2, VY + VYSIZE);
811 if (redraw_mask & REDRAW_DOOR_3)
815 x2 = MAX(x2, EX + EXSIZE);
816 y2 = MAX(y2, EY + EYSIZE);
819 // make sure that at least one pixel is blitted, and inside the screen
820 // (else nothing is blitted, causing the animations not to be updated)
821 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
822 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
823 x2 = MIN(MAX(1, x2), WIN_XSIZE);
824 y2 = MIN(MAX(1, y2), WIN_YSIZE);
826 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
829 redraw_mask = REDRAW_NONE;
832 PrintFrameTimeDebugging();
836 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
838 unsigned int frame_delay_value_old = GetVideoFrameDelay();
840 SetVideoFrameDelay(frame_delay_value);
844 SetVideoFrameDelay(frame_delay_value_old);
847 static int fade_type_skip = FADE_TYPE_NONE;
849 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
851 void (*draw_border_function)(void) = NULL;
852 int x, y, width, height;
853 int fade_delay, post_delay;
855 if (fade_type == FADE_TYPE_FADE_OUT)
857 if (fade_type_skip != FADE_TYPE_NONE)
859 // skip all fade operations until specified fade operation
860 if (fade_type & fade_type_skip)
861 fade_type_skip = FADE_TYPE_NONE;
866 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
870 redraw_mask |= fade_mask;
872 if (fade_type == FADE_TYPE_SKIP)
874 fade_type_skip = fade_mode;
879 fade_delay = fading.fade_delay;
880 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
882 if (fade_type_skip != FADE_TYPE_NONE)
884 // skip all fade operations until specified fade operation
885 if (fade_type & fade_type_skip)
886 fade_type_skip = FADE_TYPE_NONE;
891 if (global.autoplay_leveldir)
896 if (fade_mask == REDRAW_FIELD)
901 height = FADE_SYSIZE;
903 if (border.draw_masked_when_fading)
904 draw_border_function = DrawMaskedBorder_FIELD; // update when fading
906 DrawMaskedBorder_FIELD(); // draw once
916 // when switching screens without fading, set fade delay to zero
917 if (!setup.fade_screens || fading.fade_mode == FADE_MODE_NONE)
920 // do not display black frame when fading out without fade delay
921 if (fade_mode == FADE_MODE_FADE_OUT && fade_delay == 0)
924 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
925 draw_border_function);
927 redraw_mask &= ~fade_mask;
929 ClearAutoRepeatKeyEvents();
932 static void SetScreenStates_BeforeFadingIn(void)
934 // temporarily set screen mode for animations to screen after fading in
935 global.anim_status = global.anim_status_next;
937 // store backbuffer with all animations that will be started after fading in
938 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
940 // set screen mode for animations back to fading
941 global.anim_status = GAME_MODE_PSEUDO_FADING;
944 static void SetScreenStates_AfterFadingIn(void)
946 // store new source screen (to use correct masked border for fading)
947 gfx.fade_border_source_status = global.border_status;
949 global.anim_status = global.anim_status_next;
952 static void SetScreenStates_BeforeFadingOut(void)
954 // store new target screen (to use correct masked border for fading)
955 gfx.fade_border_target_status = game_status;
957 // set screen mode for animations to fading
958 global.anim_status = GAME_MODE_PSEUDO_FADING;
960 // store backbuffer with all animations that will be stopped for fading out
961 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
964 static void SetScreenStates_AfterFadingOut(void)
966 global.border_status = game_status;
969 void FadeIn(int fade_mask)
971 SetScreenStates_BeforeFadingIn();
974 DrawMaskedBorder(REDRAW_ALL);
977 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
978 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
980 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
984 FADE_SXSIZE = FULL_SXSIZE;
985 FADE_SYSIZE = FULL_SYSIZE;
987 // activate virtual buttons depending on upcoming game status
988 if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS) &&
989 game_status == GAME_MODE_PLAYING && !tape.playing)
990 SetOverlayActive(TRUE);
992 SetScreenStates_AfterFadingIn();
994 // force update of global animation status in case of rapid screen changes
995 redraw_mask = REDRAW_ALL;
999 void FadeOut(int fade_mask)
1001 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1002 if (!equalRedrawMasks(fade_mask, redraw_mask) &&
1003 fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
1006 SetScreenStates_BeforeFadingOut();
1008 SetTileCursorActive(FALSE);
1009 SetOverlayActive(FALSE);
1012 DrawMaskedBorder(REDRAW_ALL);
1015 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1016 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1018 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1020 SetScreenStates_AfterFadingOut();
1023 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1025 static struct TitleFadingInfo fading_leave_stored;
1028 fading_leave_stored = fading_leave;
1030 fading = fading_leave_stored;
1033 void FadeSetEnterMenu(void)
1035 fading = menu.enter_menu;
1037 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1040 void FadeSetLeaveMenu(void)
1042 fading = menu.leave_menu;
1044 FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1047 void FadeSetEnterScreen(void)
1049 fading = menu.enter_screen[game_status];
1051 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); // store
1054 void FadeSetNextScreen(void)
1056 fading = menu.next_screen[game_status];
1058 // (do not overwrite fade mode set by FadeSetEnterScreen)
1059 // FadeSetLeaveNext(fading, TRUE); // (keep same fade mode)
1062 void FadeSetLeaveScreen(void)
1064 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); // recall
1067 void FadeSetFromType(int type)
1069 if (type & TYPE_ENTER_SCREEN)
1070 FadeSetEnterScreen();
1071 else if (type & TYPE_ENTER)
1073 else if (type & TYPE_LEAVE)
1077 void FadeSetDisabled(void)
1079 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1081 fading = fading_none;
1084 void FadeSkipNextFadeIn(void)
1086 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1089 void FadeSkipNextFadeOut(void)
1091 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1094 static Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1096 if (graphic == IMG_UNDEFINED)
1099 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1101 return (graphic_info[graphic].bitmap != NULL || redefined ?
1102 graphic_info[graphic].bitmap :
1103 graphic_info[default_graphic].bitmap);
1106 static Bitmap *getBackgroundBitmap(int graphic)
1108 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1111 static Bitmap *getGlobalBorderBitmap(int graphic)
1113 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1116 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1119 (status == GAME_MODE_MAIN ||
1120 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1121 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1122 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1123 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1126 return getGlobalBorderBitmap(graphic);
1129 void SetWindowBackgroundImageIfDefined(int graphic)
1131 if (graphic_info[graphic].bitmap)
1132 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1135 void SetMainBackgroundImageIfDefined(int graphic)
1137 if (graphic_info[graphic].bitmap)
1138 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1141 void SetDoorBackgroundImageIfDefined(int graphic)
1143 if (graphic_info[graphic].bitmap)
1144 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1147 void SetWindowBackgroundImage(int graphic)
1149 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1152 void SetMainBackgroundImage(int graphic)
1154 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1157 void SetDoorBackgroundImage(int graphic)
1159 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1162 void SetPanelBackground(void)
1164 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1166 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1167 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1169 SetDoorBackgroundBitmap(bitmap_db_panel);
1172 void DrawBackground(int x, int y, int width, int height)
1174 // "drawto" might still point to playfield buffer here (hall of fame)
1175 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1177 if (IN_GFX_FIELD_FULL(x, y))
1178 redraw_mask |= REDRAW_FIELD;
1179 else if (IN_GFX_DOOR_1(x, y))
1180 redraw_mask |= REDRAW_DOOR_1;
1181 else if (IN_GFX_DOOR_2(x, y))
1182 redraw_mask |= REDRAW_DOOR_2;
1183 else if (IN_GFX_DOOR_3(x, y))
1184 redraw_mask |= REDRAW_DOOR_3;
1187 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1189 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1191 if (font->bitmap == NULL)
1194 DrawBackground(x, y, width, height);
1197 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1199 struct GraphicInfo *g = &graphic_info[graphic];
1201 if (g->bitmap == NULL)
1204 DrawBackground(x, y, width, height);
1207 static int game_status_last = -1;
1208 static Bitmap *global_border_bitmap_last = NULL;
1209 static Bitmap *global_border_bitmap = NULL;
1210 static int real_sx_last = -1, real_sy_last = -1;
1211 static int full_sxsize_last = -1, full_sysize_last = -1;
1212 static int dx_last = -1, dy_last = -1;
1213 static int dxsize_last = -1, dysize_last = -1;
1214 static int vx_last = -1, vy_last = -1;
1215 static int vxsize_last = -1, vysize_last = -1;
1216 static int ex_last = -1, ey_last = -1;
1217 static int exsize_last = -1, eysize_last = -1;
1219 boolean CheckIfGlobalBorderHasChanged(void)
1221 // if game status has not changed, global border has not changed either
1222 if (game_status == game_status_last)
1225 // determine and store new global border bitmap for current game status
1226 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1228 return (global_border_bitmap_last != global_border_bitmap);
1231 #define ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED 0
1233 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1234 static boolean CheckIfGlobalBorderRedrawIsNeeded(void)
1236 // if game status has not changed, nothing has to be redrawn
1237 if (game_status == game_status_last)
1240 // redraw if last screen was title screen
1241 if (game_status_last == GAME_MODE_TITLE)
1244 // redraw if global screen border has changed
1245 if (CheckIfGlobalBorderHasChanged())
1248 // redraw if position or size of playfield area has changed
1249 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1250 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1253 // redraw if position or size of door area has changed
1254 if (dx_last != DX || dy_last != DY ||
1255 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1258 // redraw if position or size of tape area has changed
1259 if (vx_last != VX || vy_last != VY ||
1260 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1263 // redraw if position or size of editor area has changed
1264 if (ex_last != EX || ey_last != EY ||
1265 exsize_last != EXSIZE || eysize_last != EYSIZE)
1272 static void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1275 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1277 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1280 void RedrawGlobalBorder(void)
1282 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1284 RedrawGlobalBorderFromBitmap(bitmap);
1286 redraw_mask = REDRAW_ALL;
1289 static void RedrawGlobalBorderIfNeeded(void)
1291 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1292 if (game_status == game_status_last)
1296 // copy current draw buffer to later copy back areas that have not changed
1297 if (game_status_last != GAME_MODE_TITLE)
1298 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1300 #if ONLY_REDRAW_GLOBAL_BORDER_IF_NEEDED
1301 if (CheckIfGlobalBorderRedrawIsNeeded())
1303 // determine and store new global border bitmap for current game status
1304 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1307 // redraw global screen border (or clear, if defined to be empty)
1308 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1310 if (game_status == GAME_MODE_EDITOR)
1311 DrawSpecialEditorDoor();
1313 // copy previous playfield and door areas, if they are defined on both
1314 // previous and current screen and if they still have the same size
1316 if (real_sx_last != -1 && real_sy_last != -1 &&
1317 REAL_SX != -1 && REAL_SY != -1 &&
1318 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1319 BlitBitmap(bitmap_db_store_1, backbuffer,
1320 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1323 if (dx_last != -1 && dy_last != -1 &&
1324 DX != -1 && DY != -1 &&
1325 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1326 BlitBitmap(bitmap_db_store_1, backbuffer,
1327 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1329 if (game_status != GAME_MODE_EDITOR)
1331 if (vx_last != -1 && vy_last != -1 &&
1332 VX != -1 && VY != -1 &&
1333 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1334 BlitBitmap(bitmap_db_store_1, backbuffer,
1335 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1339 if (ex_last != -1 && ey_last != -1 &&
1340 EX != -1 && EY != -1 &&
1341 exsize_last == EXSIZE && eysize_last == EYSIZE)
1342 BlitBitmap(bitmap_db_store_1, backbuffer,
1343 ex_last, ey_last, EXSIZE, EYSIZE, EX, EY);
1346 redraw_mask = REDRAW_ALL;
1349 game_status_last = game_status;
1351 global_border_bitmap_last = global_border_bitmap;
1353 real_sx_last = REAL_SX;
1354 real_sy_last = REAL_SY;
1355 full_sxsize_last = FULL_SXSIZE;
1356 full_sysize_last = FULL_SYSIZE;
1359 dxsize_last = DXSIZE;
1360 dysize_last = DYSIZE;
1363 vxsize_last = VXSIZE;
1364 vysize_last = VYSIZE;
1367 exsize_last = EXSIZE;
1368 eysize_last = EYSIZE;
1371 void ClearField(void)
1373 RedrawGlobalBorderIfNeeded();
1375 // !!! "drawto" might still point to playfield buffer here (see above) !!!
1376 // (when entering hall of fame after playing)
1377 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1379 // !!! maybe this should be done before clearing the background !!!
1380 if (game_status == GAME_MODE_PLAYING)
1382 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1383 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1387 SetDrawtoField(DRAW_TO_BACKBUFFER);
1391 void MarkTileDirty(int x, int y)
1393 redraw_mask |= REDRAW_FIELD;
1396 void SetBorderElement(void)
1400 BorderElement = EL_EMPTY;
1402 // only the R'n'D game engine may use an additional steelwall border
1403 if (level.game_engine_type != GAME_ENGINE_TYPE_RND)
1406 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1408 for (x = 0; x < lev_fieldx; x++)
1410 if (!IS_INDESTRUCTIBLE(Tile[x][y]))
1411 BorderElement = EL_STEELWALL;
1413 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1419 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1420 int max_array_fieldx, int max_array_fieldy,
1421 short field[max_array_fieldx][max_array_fieldy],
1422 int max_fieldx, int max_fieldy)
1426 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1427 static int safety = 0;
1429 // check if starting field still has the desired content
1430 if (field[from_x][from_y] == fill_element)
1435 if (safety > max_fieldx * max_fieldy)
1436 Fail("Something went wrong in 'FloodFill()'. Please debug.");
1438 old_element = field[from_x][from_y];
1439 field[from_x][from_y] = fill_element;
1441 for (i = 0; i < 4; i++)
1443 x = from_x + check[i][0];
1444 y = from_y + check[i][1];
1446 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1447 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1448 field, max_fieldx, max_fieldy);
1454 void FloodFillLevel(int from_x, int from_y, int fill_element,
1455 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1456 int max_fieldx, int max_fieldy)
1458 FloodFillLevelExt(from_x, from_y, fill_element,
1459 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1460 max_fieldx, max_fieldy);
1463 void SetRandomAnimationValue(int x, int y)
1465 gfx.anim_random_frame = GfxRandom[x][y];
1468 int getGraphicAnimationFrame(int graphic, int sync_frame)
1470 // animation synchronized with global frame counter, not move position
1471 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1472 sync_frame = FrameCounter;
1474 return getAnimationFrame(graphic_info[graphic].anim_frames,
1475 graphic_info[graphic].anim_delay,
1476 graphic_info[graphic].anim_mode,
1477 graphic_info[graphic].anim_start_frame,
1481 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1483 struct GraphicInfo *g = &graphic_info[graphic];
1484 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1486 if (tilesize == gfx.standard_tile_size)
1487 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1488 else if (tilesize == game.tile_size)
1489 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1491 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1494 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1495 boolean get_backside)
1497 struct GraphicInfo *g = &graphic_info[graphic];
1498 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1499 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1501 if (g->offset_y == 0) // frames are ordered horizontally
1503 int max_width = g->anim_frames_per_line * g->width;
1504 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1506 *x = pos % max_width;
1507 *y = src_y % g->height + pos / max_width * g->height;
1509 else if (g->offset_x == 0) // frames are ordered vertically
1511 int max_height = g->anim_frames_per_line * g->height;
1512 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1514 *x = src_x % g->width + pos / max_height * g->width;
1515 *y = pos % max_height;
1517 else // frames are ordered diagonally
1519 *x = src_x + frame * g->offset_x;
1520 *y = src_y + frame * g->offset_y;
1524 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1525 Bitmap **bitmap, int *x, int *y,
1526 boolean get_backside)
1528 struct GraphicInfo *g = &graphic_info[graphic];
1530 // if no graphics defined at all, use fallback graphics
1531 if (g->bitmaps == NULL)
1532 *g = graphic_info[IMG_CHAR_EXCLAM];
1534 // if no in-game graphics defined, always use standard graphic size
1535 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1536 tilesize = TILESIZE;
1538 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1539 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1541 *x = *x * tilesize / g->tile_size;
1542 *y = *y * tilesize / g->tile_size;
1545 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1546 Bitmap **bitmap, int *x, int *y)
1548 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1551 void getFixedGraphicSource(int graphic, int frame,
1552 Bitmap **bitmap, int *x, int *y)
1554 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1557 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1559 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1562 static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1563 int *x, int *y, boolean get_backside)
1565 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1569 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1571 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1574 void DrawGraphic(int x, int y, int graphic, int frame)
1577 if (!IN_SCR_FIELD(x, y))
1579 Debug("draw:DrawGraphic", "x = %d, y = %d, graphic = %d", x, y, graphic);
1580 Debug("draw:DrawGraphic", "This should never happen!");
1586 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1589 MarkTileDirty(x, y);
1592 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1595 if (!IN_SCR_FIELD(x, y))
1597 Debug("draw:DrawFixedGraphic", "x = %d, y = %d, graphic = %d",
1599 Debug("draw:DrawFixedGraphic", "This should never happen!");
1605 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1607 MarkTileDirty(x, y);
1610 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1616 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1618 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1621 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1627 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1628 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1631 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1634 if (!IN_SCR_FIELD(x, y))
1636 Debug("draw:DrawGraphicThruMask", "x = %d,y = %d, graphic = %d",
1638 Debug("draw:DrawGraphicThruMask", "This should never happen!");
1644 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1647 MarkTileDirty(x, y);
1650 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1653 if (!IN_SCR_FIELD(x, y))
1655 Debug("draw:DrawFixedGraphicThruMask", "x = %d,y = %d, graphic = %d",
1657 Debug("draw:DrawFixedGraphicThruMask", "This should never happen!");
1663 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1665 MarkTileDirty(x, y);
1668 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1674 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1676 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1680 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1681 int graphic, int frame)
1686 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1688 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1692 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1694 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1696 MarkTileDirty(x / tilesize, y / tilesize);
1699 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1702 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1703 graphic, frame, tilesize);
1704 MarkTileDirty(x / tilesize, y / tilesize);
1707 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1713 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1714 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1717 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1718 int frame, int tilesize)
1723 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1724 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1727 void DrawMiniGraphic(int x, int y, int graphic)
1729 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1730 MarkTileDirty(x / 2, y / 2);
1733 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1738 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1739 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1742 static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1743 int graphic, int frame,
1744 int cut_mode, int mask_mode)
1749 int width = TILEX, height = TILEY;
1752 if (dx || dy) // shifted graphic
1754 if (x < BX1) // object enters playfield from the left
1761 else if (x > BX2) // object enters playfield from the right
1767 else if (x == BX1 && dx < 0) // object leaves playfield to the left
1773 else if (x == BX2 && dx > 0) // object leaves playfield to the right
1775 else if (dx) // general horizontal movement
1776 MarkTileDirty(x + SIGN(dx), y);
1778 if (y < BY1) // object enters playfield from the top
1780 if (cut_mode == CUT_BELOW) // object completely above top border
1788 else if (y > BY2) // object enters playfield from the bottom
1794 else if (y == BY1 && dy < 0) // object leaves playfield to the top
1800 else if (dy > 0 && cut_mode == CUT_ABOVE)
1802 if (y == BY2) // object completely above bottom border
1808 MarkTileDirty(x, y + 1);
1809 } // object leaves playfield to the bottom
1810 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1812 else if (dy) // general vertical movement
1813 MarkTileDirty(x, y + SIGN(dy));
1817 if (!IN_SCR_FIELD(x, y))
1819 Debug("draw:DrawGraphicShiftedNormal", "x = %d, y = %d, graphic = %d",
1821 Debug("draw:DrawGraphicShiftedNormal", "This should never happen!");
1827 width = width * TILESIZE_VAR / TILESIZE;
1828 height = height * TILESIZE_VAR / TILESIZE;
1829 cx = cx * TILESIZE_VAR / TILESIZE;
1830 cy = cy * TILESIZE_VAR / TILESIZE;
1831 dx = dx * TILESIZE_VAR / TILESIZE;
1832 dy = dy * TILESIZE_VAR / TILESIZE;
1834 if (width > 0 && height > 0)
1836 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1841 dst_x = FX + x * TILEX_VAR + dx;
1842 dst_y = FY + y * TILEY_VAR + dy;
1844 if (mask_mode == USE_MASKING)
1845 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1848 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1851 MarkTileDirty(x, y);
1855 static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1856 int graphic, int frame,
1857 int cut_mode, int mask_mode)
1862 int width = TILEX_VAR, height = TILEY_VAR;
1865 int x2 = x + SIGN(dx);
1866 int y2 = y + SIGN(dy);
1868 // movement with two-tile animations must be sync'ed with movement position,
1869 // not with current GfxFrame (which can be higher when using slow movement)
1870 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1871 int anim_frames = graphic_info[graphic].anim_frames;
1873 // (we also need anim_delay here for movement animations with less frames)
1874 int anim_delay = graphic_info[graphic].anim_delay;
1875 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1877 boolean draw_start_tile = (cut_mode != CUT_ABOVE); // only for falling!
1878 boolean draw_end_tile = (cut_mode != CUT_BELOW); // only for falling!
1880 // re-calculate animation frame for two-tile movement animation
1881 frame = getGraphicAnimationFrame(graphic, sync_frame);
1883 // check if movement start graphic inside screen area and should be drawn
1884 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1886 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1888 dst_x = FX + x1 * TILEX_VAR;
1889 dst_y = FY + y1 * TILEY_VAR;
1891 if (mask_mode == USE_MASKING)
1892 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1895 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1898 MarkTileDirty(x1, y1);
1901 // check if movement end graphic inside screen area and should be drawn
1902 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1904 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1906 dst_x = FX + x2 * TILEX_VAR;
1907 dst_y = FY + y2 * TILEY_VAR;
1909 if (mask_mode == USE_MASKING)
1910 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1913 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1916 MarkTileDirty(x2, y2);
1920 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1921 int graphic, int frame,
1922 int cut_mode, int mask_mode)
1926 DrawGraphic(x, y, graphic, frame);
1931 if (graphic_info[graphic].double_movement) // EM style movement images
1932 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1934 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1937 static void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy,
1938 int graphic, int frame, int cut_mode)
1940 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1943 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1944 int cut_mode, int mask_mode)
1946 int lx = LEVELX(x), ly = LEVELY(y);
1950 if (IN_LEV_FIELD(lx, ly))
1952 SetRandomAnimationValue(lx, ly);
1954 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1955 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1957 // do not use double (EM style) movement graphic when not moving
1958 if (graphic_info[graphic].double_movement && !dx && !dy)
1960 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1961 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1964 else // border element
1966 graphic = el2img(element);
1967 frame = getGraphicAnimationFrame(graphic, -1);
1970 if (element == EL_EXPANDABLE_WALL)
1972 boolean left_stopped = FALSE, right_stopped = FALSE;
1974 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Tile[lx - 1][ly]))
1975 left_stopped = TRUE;
1976 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Tile[lx + 1][ly]))
1977 right_stopped = TRUE;
1979 if (left_stopped && right_stopped)
1981 else if (left_stopped)
1983 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1984 frame = graphic_info[graphic].anim_frames - 1;
1986 else if (right_stopped)
1988 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1989 frame = graphic_info[graphic].anim_frames - 1;
1994 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1995 else if (mask_mode == USE_MASKING)
1996 DrawGraphicThruMask(x, y, graphic, frame);
1998 DrawGraphic(x, y, graphic, frame);
2001 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
2002 int cut_mode, int mask_mode)
2004 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2005 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
2006 cut_mode, mask_mode);
2009 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
2012 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2015 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
2018 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
2021 void DrawLevelElementThruMask(int x, int y, int element)
2023 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
2026 void DrawLevelFieldThruMask(int x, int y)
2028 DrawLevelElementExt(x, y, 0, 0, Tile[x][y], NO_CUTTING, USE_MASKING);
2031 // !!! implementation of quicksand is totally broken !!!
2032 #define IS_CRUMBLED_TILE(x, y, e) \
2033 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
2034 !IS_MOVING(x, y) || \
2035 (e) == EL_QUICKSAND_EMPTYING || \
2036 (e) == EL_QUICKSAND_FAST_EMPTYING))
2038 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
2043 int width, height, cx, cy;
2044 int sx = SCREENX(x), sy = SCREENY(y);
2045 int crumbled_border_size = graphic_info[graphic].border_size;
2046 int crumbled_tile_size = graphic_info[graphic].tile_size;
2047 int crumbled_border_size_var =
2048 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2051 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2053 for (i = 1; i < 4; i++)
2055 int dxx = (i & 1 ? dx : 0);
2056 int dyy = (i & 2 ? dy : 0);
2059 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2062 // check if neighbour field is of same crumble type
2063 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2064 graphic_info[graphic].class ==
2065 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2067 // return if check prevents inner corner
2068 if (same == (dxx == dx && dyy == dy))
2072 // if we reach this point, we have an inner corner
2074 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2076 width = crumbled_border_size_var;
2077 height = crumbled_border_size_var;
2078 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2079 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2081 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2082 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2085 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2090 int width, height, bx, by, cx, cy;
2091 int sx = SCREENX(x), sy = SCREENY(y);
2092 int crumbled_border_size = graphic_info[graphic].border_size;
2093 int crumbled_tile_size = graphic_info[graphic].tile_size;
2094 int crumbled_border_size_var =
2095 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2096 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2099 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2101 // draw simple, sloppy, non-corner-accurate crumbled border
2103 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2104 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2105 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2106 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2108 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2109 FX + sx * TILEX_VAR + cx,
2110 FY + sy * TILEY_VAR + cy);
2112 // (remaining middle border part must be at least as big as corner part)
2113 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2114 crumbled_border_size_var >= TILESIZE_VAR / 3)
2117 // correct corners of crumbled border, if needed
2119 for (i = -1; i <= 1; i += 2)
2121 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2122 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2123 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2126 // check if neighbour field is of same crumble type
2127 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2128 graphic_info[graphic].class ==
2129 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2131 // no crumbled corner, but continued crumbled border
2133 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2134 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2135 int b1 = (i == 1 ? crumbled_border_size_var :
2136 TILESIZE_VAR - 2 * crumbled_border_size_var);
2138 width = crumbled_border_size_var;
2139 height = crumbled_border_size_var;
2141 if (dir == 1 || dir == 2)
2156 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2158 FX + sx * TILEX_VAR + cx,
2159 FY + sy * TILEY_VAR + cy);
2164 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2166 int sx = SCREENX(x), sy = SCREENY(y);
2169 static int xy[4][2] =
2177 if (!IN_LEV_FIELD(x, y))
2180 element = TILE_GFX_ELEMENT(x, y);
2182 if (IS_CRUMBLED_TILE(x, y, element)) // crumble field itself
2184 if (!IN_SCR_FIELD(sx, sy))
2187 // crumble field borders towards direct neighbour fields
2188 for (i = 0; i < 4; i++)
2190 int xx = x + xy[i][0];
2191 int yy = y + xy[i][1];
2193 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2196 // check if neighbour field is of same crumble type
2197 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2198 graphic_info[graphic].class ==
2199 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2202 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2205 // crumble inner field corners towards corner neighbour fields
2206 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2207 graphic_info[graphic].anim_frames == 2)
2209 for (i = 0; i < 4; i++)
2211 int dx = (i & 1 ? +1 : -1);
2212 int dy = (i & 2 ? +1 : -1);
2214 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2218 MarkTileDirty(sx, sy);
2220 else // center field is not crumbled -- crumble neighbour fields
2222 // crumble field borders of direct neighbour fields
2223 for (i = 0; i < 4; i++)
2225 int xx = x + xy[i][0];
2226 int yy = y + xy[i][1];
2227 int sxx = sx + xy[i][0];
2228 int syy = sy + xy[i][1];
2230 if (!IN_LEV_FIELD(xx, yy) ||
2231 !IN_SCR_FIELD(sxx, syy))
2234 // do not crumble fields that are being digged or snapped
2235 if (Tile[xx][yy] == EL_EMPTY ||
2236 Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2239 element = TILE_GFX_ELEMENT(xx, yy);
2241 if (!IS_CRUMBLED_TILE(xx, yy, element))
2244 graphic = el_act2crm(element, ACTION_DEFAULT);
2246 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2248 MarkTileDirty(sxx, syy);
2251 // crumble inner field corners of corner neighbour fields
2252 for (i = 0; i < 4; i++)
2254 int dx = (i & 1 ? +1 : -1);
2255 int dy = (i & 2 ? +1 : -1);
2261 if (!IN_LEV_FIELD(xx, yy) ||
2262 !IN_SCR_FIELD(sxx, syy))
2265 if (Tile[xx][yy] == EL_ELEMENT_SNAPPING)
2268 element = TILE_GFX_ELEMENT(xx, yy);
2270 if (!IS_CRUMBLED_TILE(xx, yy, element))
2273 graphic = el_act2crm(element, ACTION_DEFAULT);
2275 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2276 graphic_info[graphic].anim_frames == 2)
2277 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2279 MarkTileDirty(sxx, syy);
2284 void DrawLevelFieldCrumbled(int x, int y)
2288 if (!IN_LEV_FIELD(x, y))
2291 if (Tile[x][y] == EL_ELEMENT_SNAPPING &&
2292 GfxElement[x][y] != EL_UNDEFINED &&
2293 GFX_CRUMBLED(GfxElement[x][y]))
2295 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2300 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2302 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2305 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2308 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2309 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2310 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2311 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2312 int sx = SCREENX(x), sy = SCREENY(y);
2314 DrawGraphic(sx, sy, graphic1, frame1);
2315 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2318 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2320 int sx = SCREENX(x), sy = SCREENY(y);
2321 static int xy[4][2] =
2330 // crumble direct neighbour fields (required for field borders)
2331 for (i = 0; i < 4; i++)
2333 int xx = x + xy[i][0];
2334 int yy = y + xy[i][1];
2335 int sxx = sx + xy[i][0];
2336 int syy = sy + xy[i][1];
2338 if (!IN_LEV_FIELD(xx, yy) ||
2339 !IN_SCR_FIELD(sxx, syy) ||
2340 !GFX_CRUMBLED(Tile[xx][yy]) ||
2344 DrawLevelField(xx, yy);
2347 // crumble corner neighbour fields (required for inner field corners)
2348 for (i = 0; i < 4; i++)
2350 int dx = (i & 1 ? +1 : -1);
2351 int dy = (i & 2 ? +1 : -1);
2357 if (!IN_LEV_FIELD(xx, yy) ||
2358 !IN_SCR_FIELD(sxx, syy) ||
2359 !GFX_CRUMBLED(Tile[xx][yy]) ||
2363 int element = TILE_GFX_ELEMENT(xx, yy);
2364 int graphic = el_act2crm(element, ACTION_DEFAULT);
2366 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2367 graphic_info[graphic].anim_frames == 2)
2368 DrawLevelField(xx, yy);
2372 static int getBorderElement(int x, int y)
2376 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2377 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2378 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2379 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2380 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2381 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2382 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2384 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2385 int steel_position = (x == -1 && y == -1 ? 0 :
2386 x == lev_fieldx && y == -1 ? 1 :
2387 x == -1 && y == lev_fieldy ? 2 :
2388 x == lev_fieldx && y == lev_fieldy ? 3 :
2389 x == -1 || x == lev_fieldx ? 4 :
2390 y == -1 || y == lev_fieldy ? 5 : 6);
2392 return border[steel_position][steel_type];
2395 void DrawScreenElement(int x, int y, int element)
2397 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2398 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2401 void DrawLevelElement(int x, int y, int element)
2403 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2404 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2407 void DrawScreenField(int x, int y)
2409 int lx = LEVELX(x), ly = LEVELY(y);
2410 int element, content;
2412 if (!IN_LEV_FIELD(lx, ly))
2414 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2417 element = getBorderElement(lx, ly);
2419 DrawScreenElement(x, y, element);
2424 element = Tile[lx][ly];
2425 content = Store[lx][ly];
2427 if (IS_MOVING(lx, ly))
2429 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2430 boolean cut_mode = NO_CUTTING;
2432 if (element == EL_QUICKSAND_EMPTYING ||
2433 element == EL_QUICKSAND_FAST_EMPTYING ||
2434 element == EL_MAGIC_WALL_EMPTYING ||
2435 element == EL_BD_MAGIC_WALL_EMPTYING ||
2436 element == EL_DC_MAGIC_WALL_EMPTYING ||
2437 element == EL_AMOEBA_DROPPING)
2438 cut_mode = CUT_ABOVE;
2439 else if (element == EL_QUICKSAND_FILLING ||
2440 element == EL_QUICKSAND_FAST_FILLING ||
2441 element == EL_MAGIC_WALL_FILLING ||
2442 element == EL_BD_MAGIC_WALL_FILLING ||
2443 element == EL_DC_MAGIC_WALL_FILLING)
2444 cut_mode = CUT_BELOW;
2446 if (cut_mode == CUT_ABOVE)
2447 DrawScreenElement(x, y, element);
2449 DrawScreenElement(x, y, EL_EMPTY);
2452 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2453 else if (cut_mode == NO_CUTTING)
2454 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2457 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2459 if (cut_mode == CUT_BELOW &&
2460 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2461 DrawLevelElement(lx, ly + 1, element);
2464 if (content == EL_ACID)
2466 int dir = MovDir[lx][ly];
2467 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2468 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2470 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2472 // prevent target field from being drawn again (but without masking)
2473 // (this would happen if target field is scanned after moving element)
2474 Stop[newlx][newly] = TRUE;
2477 else if (IS_BLOCKED(lx, ly))
2482 boolean cut_mode = NO_CUTTING;
2483 int element_old, content_old;
2485 Blocked2Moving(lx, ly, &oldx, &oldy);
2488 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2489 MovDir[oldx][oldy] == MV_RIGHT);
2491 element_old = Tile[oldx][oldy];
2492 content_old = Store[oldx][oldy];
2494 if (element_old == EL_QUICKSAND_EMPTYING ||
2495 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2496 element_old == EL_MAGIC_WALL_EMPTYING ||
2497 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2498 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2499 element_old == EL_AMOEBA_DROPPING)
2500 cut_mode = CUT_ABOVE;
2502 DrawScreenElement(x, y, EL_EMPTY);
2505 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2507 else if (cut_mode == NO_CUTTING)
2508 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2511 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2514 else if (IS_DRAWABLE(element))
2515 DrawScreenElement(x, y, element);
2517 DrawScreenElement(x, y, EL_EMPTY);
2520 void DrawLevelField(int x, int y)
2522 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2523 DrawScreenField(SCREENX(x), SCREENY(y));
2524 else if (IS_MOVING(x, y))
2528 Moving2Blocked(x, y, &newx, &newy);
2529 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2530 DrawScreenField(SCREENX(newx), SCREENY(newy));
2532 else if (IS_BLOCKED(x, y))
2536 Blocked2Moving(x, y, &oldx, &oldy);
2537 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2538 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2542 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2543 int (*el2img_function)(int), boolean masked,
2544 int element_bits_draw)
2546 int element_base = map_mm_wall_element(element);
2547 int element_bits = (IS_DF_WALL(element) ?
2548 element - EL_DF_WALL_START :
2549 IS_MM_WALL(element) ?
2550 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2551 int graphic = el2img_function(element_base);
2552 int tilesize_draw = tilesize / 2;
2557 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2559 for (i = 0; i < 4; i++)
2561 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2562 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2564 if (!(element_bits_draw & (1 << i)))
2567 if (element_bits & (1 << i))
2570 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2571 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2573 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2574 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2579 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2580 tilesize_draw, tilesize_draw);
2585 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2586 boolean masked, int element_bits_draw)
2588 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2589 element, tilesize, el2edimg, masked, element_bits_draw);
2592 static void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2593 int (*el2img_function)(int))
2595 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2599 static void DrawSizedElementExt(int x, int y, int element, int tilesize,
2602 if (IS_MM_WALL(element))
2604 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2605 element, tilesize, el2edimg, masked, 0x000f);
2609 int graphic = el2edimg(element);
2612 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2614 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2618 void DrawSizedElement(int x, int y, int element, int tilesize)
2620 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2623 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2625 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2628 void DrawMiniElement(int x, int y, int element)
2632 graphic = el2edimg(element);
2633 DrawMiniGraphic(x, y, graphic);
2636 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2639 int x = sx + scroll_x, y = sy + scroll_y;
2641 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2642 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2643 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2644 DrawSizedElement(sx, sy, Tile[x][y], tilesize);
2646 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2649 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2651 int x = sx + scroll_x, y = sy + scroll_y;
2653 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2654 DrawMiniElement(sx, sy, EL_EMPTY);
2655 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2656 DrawMiniElement(sx, sy, Tile[x][y]);
2658 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2661 static void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2662 int x, int y, int xsize, int ysize,
2663 int tile_width, int tile_height)
2667 int dst_x = startx + x * tile_width;
2668 int dst_y = starty + y * tile_height;
2669 int width = graphic_info[graphic].width;
2670 int height = graphic_info[graphic].height;
2671 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2672 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2673 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2674 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2675 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2676 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2677 boolean draw_masked = graphic_info[graphic].draw_masked;
2679 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2681 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2683 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2687 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2688 inner_sx + (x - 1) * tile_width % inner_width);
2689 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2690 inner_sy + (y - 1) * tile_height % inner_height);
2693 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2696 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2700 static void DrawEnvelopeBackground(int graphic, int startx, int starty,
2701 int x, int y, int xsize, int ysize,
2704 int font_width = getFontWidth(font_nr);
2705 int font_height = getFontHeight(font_nr);
2707 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2708 font_width, font_height);
2711 static void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2713 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2714 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2715 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2716 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2717 boolean no_delay = (tape.warp_forward);
2718 unsigned int anim_delay = 0;
2719 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2720 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2721 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2722 int font_width = getFontWidth(font_nr);
2723 int font_height = getFontHeight(font_nr);
2724 int max_xsize = level.envelope[envelope_nr].xsize;
2725 int max_ysize = level.envelope[envelope_nr].ysize;
2726 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2727 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2728 int xend = max_xsize;
2729 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2730 int xstep = (xstart < xend ? 1 : 0);
2731 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2733 int end = MAX(xend - xstart, yend - ystart);
2736 for (i = start; i <= end; i++)
2738 int last_frame = end; // last frame of this "for" loop
2739 int x = xstart + i * xstep;
2740 int y = ystart + i * ystep;
2741 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2742 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2743 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2744 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2747 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2749 BlitScreenToBitmap(backbuffer);
2751 SetDrawtoField(DRAW_TO_BACKBUFFER);
2753 for (yy = 0; yy < ysize; yy++)
2754 for (xx = 0; xx < xsize; xx++)
2755 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2757 DrawTextBuffer(sx + font_width, sy + font_height,
2758 level.envelope[envelope_nr].text, font_nr, max_xsize,
2759 xsize - 2, ysize - 2, 0, mask_mode,
2760 level.envelope[envelope_nr].autowrap,
2761 level.envelope[envelope_nr].centered, FALSE);
2763 redraw_mask |= REDRAW_FIELD;
2766 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2769 ClearAutoRepeatKeyEvents();
2772 void ShowEnvelope(int envelope_nr)
2774 int element = EL_ENVELOPE_1 + envelope_nr;
2775 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2776 int sound_opening = element_info[element].sound[ACTION_OPENING];
2777 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2778 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2779 boolean no_delay = (tape.warp_forward);
2780 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2781 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2782 int anim_mode = graphic_info[graphic].anim_mode;
2783 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2784 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2785 boolean overlay_enabled = GetOverlayEnabled();
2787 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
2789 SetOverlayEnabled(FALSE);
2792 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2794 if (anim_mode == ANIM_DEFAULT)
2795 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2797 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2800 Delay_WithScreenUpdates(wait_delay_value);
2802 WaitForEventToContinue();
2805 SetOverlayEnabled(overlay_enabled);
2807 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2809 if (anim_mode != ANIM_NONE)
2810 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2812 if (anim_mode == ANIM_DEFAULT)
2813 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2815 game.envelope_active = FALSE;
2817 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2819 redraw_mask |= REDRAW_FIELD;
2823 static void PrepareEnvelopeRequestToScreen(Bitmap *bitmap, int sx, int sy,
2824 int xsize, int ysize)
2826 if (!global.use_envelope_request ||
2827 request.sort_priority <= 0)
2830 if (request.bitmap == NULL ||
2831 xsize > request.xsize ||
2832 ysize > request.ysize)
2834 if (request.bitmap != NULL)
2835 FreeBitmap(request.bitmap);
2837 request.bitmap = CreateBitmap(xsize, ysize, DEFAULT_DEPTH);
2839 SDL_Surface *surface = request.bitmap->surface;
2841 if ((request.bitmap->surface_masked = SDLGetNativeSurface(surface)) == NULL)
2842 Fail("SDLGetNativeSurface() failed");
2845 BlitBitmap(bitmap, request.bitmap, sx, sy, xsize, ysize, 0, 0);
2847 SDLFreeBitmapTextures(request.bitmap);
2848 SDLCreateBitmapTextures(request.bitmap);
2850 // set envelope request run-time values
2853 request.xsize = xsize;
2854 request.ysize = ysize;
2857 void DrawEnvelopeRequestToScreen(int drawing_target, int drawing_stage)
2859 if (global.use_envelope_request &&
2860 game.request_active_or_moving &&
2861 request.sort_priority > 0 &&
2862 drawing_target == DRAW_TO_SCREEN &&
2863 drawing_stage == DRAW_GLOBAL_ANIM_STAGE_2)
2865 BlitToScreen(request.bitmap, 0, 0, request.xsize, request.ysize,
2866 request.sx, request.sy);
2870 static void setRequestBasePosition(int *x, int *y)
2872 int sx_base, sy_base;
2874 if (request.x != -1)
2875 sx_base = request.x;
2876 else if (request.align == ALIGN_LEFT)
2878 else if (request.align == ALIGN_RIGHT)
2879 sx_base = SX + SXSIZE;
2881 sx_base = SX + SXSIZE / 2;
2883 if (request.y != -1)
2884 sy_base = request.y;
2885 else if (request.valign == VALIGN_TOP)
2887 else if (request.valign == VALIGN_BOTTOM)
2888 sy_base = SY + SYSIZE;
2890 sy_base = SY + SYSIZE / 2;
2896 static void setRequestPositionExt(int *x, int *y, int width, int height,
2897 boolean add_border_size)
2899 int border_size = request.border_size;
2900 int sx_base, sy_base;
2903 setRequestBasePosition(&sx_base, &sy_base);
2905 if (request.align == ALIGN_LEFT)
2907 else if (request.align == ALIGN_RIGHT)
2908 sx = sx_base - width;
2910 sx = sx_base - width / 2;
2912 if (request.valign == VALIGN_TOP)
2914 else if (request.valign == VALIGN_BOTTOM)
2915 sy = sy_base - height;
2917 sy = sy_base - height / 2;
2919 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2920 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2922 if (add_border_size)
2932 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2934 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2937 static void DrawEnvelopeRequest(char *text)
2939 char *text_final = text;
2940 char *text_door_style = NULL;
2941 int graphic = IMG_BACKGROUND_REQUEST;
2942 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2943 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2944 int font_nr = FONT_REQUEST;
2945 int font_width = getFontWidth(font_nr);
2946 int font_height = getFontHeight(font_nr);
2947 int border_size = request.border_size;
2948 int line_spacing = request.line_spacing;
2949 int line_height = font_height + line_spacing;
2950 int max_text_width = request.width - 2 * border_size;
2951 int max_text_height = request.height - 2 * border_size;
2952 int line_length = max_text_width / font_width;
2953 int max_lines = max_text_height / line_height;
2954 int text_width = line_length * font_width;
2955 int width = request.width;
2956 int height = request.height;
2957 int tile_size = MAX(request.step_offset, 1);
2958 int x_steps = width / tile_size;
2959 int y_steps = height / tile_size;
2960 int sx_offset = border_size;
2961 int sy_offset = border_size;
2965 if (request.centered)
2966 sx_offset = (request.width - text_width) / 2;
2968 if (request.wrap_single_words && !request.autowrap)
2970 char *src_text_ptr, *dst_text_ptr;
2972 text_door_style = checked_malloc(2 * strlen(text) + 1);
2974 src_text_ptr = text;
2975 dst_text_ptr = text_door_style;
2977 while (*src_text_ptr)
2979 if (*src_text_ptr == ' ' ||
2980 *src_text_ptr == '?' ||
2981 *src_text_ptr == '!')
2982 *dst_text_ptr++ = '\n';
2984 if (*src_text_ptr != ' ')
2985 *dst_text_ptr++ = *src_text_ptr;
2990 *dst_text_ptr = '\0';
2992 text_final = text_door_style;
2995 setRequestPosition(&sx, &sy, FALSE);
2997 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2999 for (y = 0; y < y_steps; y++)
3000 for (x = 0; x < x_steps; x++)
3001 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
3002 x, y, x_steps, y_steps,
3003 tile_size, tile_size);
3005 // force DOOR font inside door area
3006 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3008 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
3009 line_length, -1, max_lines, line_spacing, mask_mode,
3010 request.autowrap, request.centered, FALSE);
3014 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
3015 RedrawGadget(tool_gadget[i]);
3017 // store readily prepared envelope request for later use when animating
3018 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3020 PrepareEnvelopeRequestToScreen(bitmap_db_store_2, sx, sy, width, height);
3022 if (text_door_style)
3023 free(text_door_style);
3026 static void AnimateEnvelopeRequest(int anim_mode, int action)
3028 int graphic = IMG_BACKGROUND_REQUEST;
3029 boolean draw_masked = graphic_info[graphic].draw_masked;
3030 int delay_value_normal = request.step_delay;
3031 int delay_value_fast = delay_value_normal / 2;
3032 boolean ffwd_delay = (tape.playing && tape.fast_forward);
3033 boolean no_delay = (tape.warp_forward);
3034 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
3035 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
3036 unsigned int anim_delay = 0;
3038 int tile_size = MAX(request.step_offset, 1);
3039 int max_xsize = request.width / tile_size;
3040 int max_ysize = request.height / tile_size;
3041 int max_xsize_inner = max_xsize - 2;
3042 int max_ysize_inner = max_ysize - 2;
3044 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
3045 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
3046 int xend = max_xsize_inner;
3047 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
3048 int xstep = (xstart < xend ? 1 : 0);
3049 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
3051 int end = MAX(xend - xstart, yend - ystart);
3054 if (setup.quick_doors)
3061 for (i = start; i <= end; i++)
3063 int last_frame = end; // last frame of this "for" loop
3064 int x = xstart + i * xstep;
3065 int y = ystart + i * ystep;
3066 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
3067 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
3068 int xsize_size_left = (xsize - 1) * tile_size;
3069 int ysize_size_top = (ysize - 1) * tile_size;
3070 int max_xsize_pos = (max_xsize - 1) * tile_size;
3071 int max_ysize_pos = (max_ysize - 1) * tile_size;
3072 int width = xsize * tile_size;
3073 int height = ysize * tile_size;
3078 setRequestPosition(&src_x, &src_y, FALSE);
3079 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
3081 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3083 for (yy = 0; yy < 2; yy++)
3085 for (xx = 0; xx < 2; xx++)
3087 int src_xx = src_x + xx * max_xsize_pos;
3088 int src_yy = src_y + yy * max_ysize_pos;
3089 int dst_xx = dst_x + xx * xsize_size_left;
3090 int dst_yy = dst_y + yy * ysize_size_top;
3091 int xx_size = (xx ? tile_size : xsize_size_left);
3092 int yy_size = (yy ? tile_size : ysize_size_top);
3095 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
3096 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3098 BlitBitmap(bitmap_db_store_2, backbuffer,
3099 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
3103 PrepareEnvelopeRequestToScreen(backbuffer, dst_x, dst_y, width, height);
3105 redraw_mask |= REDRAW_FIELD;
3109 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
3112 ClearAutoRepeatKeyEvents();
3115 static void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3117 int graphic = IMG_BACKGROUND_REQUEST;
3118 int sound_opening = SND_REQUEST_OPENING;
3119 int sound_closing = SND_REQUEST_CLOSING;
3120 int anim_mode_1 = request.anim_mode; // (higher priority)
3121 int anim_mode_2 = graphic_info[graphic].anim_mode; // (lower priority)
3122 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3123 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3124 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3126 if (game_status == GAME_MODE_PLAYING)
3127 BlitScreenToBitmap(backbuffer);
3129 SetDrawtoField(DRAW_TO_BACKBUFFER);
3131 // SetDrawBackgroundMask(REDRAW_NONE);
3133 if (action == ACTION_OPENING)
3135 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3137 if (req_state & REQ_ASK)
3139 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3140 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3141 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_YES]);
3142 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_NO]);
3144 else if (req_state & REQ_CONFIRM)
3146 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3147 MapGadget(tool_gadget[TOOL_CTRL_ID_TOUCH_CONFIRM]);
3149 else if (req_state & REQ_PLAYER)
3151 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3152 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3153 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3154 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3157 DrawEnvelopeRequest(text);
3160 game.envelope_active = TRUE; // needed for RedrawPlayfield() events
3162 if (action == ACTION_OPENING)
3164 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3166 if (anim_mode == ANIM_DEFAULT)
3167 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3169 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3173 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3175 if (anim_mode != ANIM_NONE)
3176 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3178 if (anim_mode == ANIM_DEFAULT)
3179 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3182 game.envelope_active = FALSE;
3184 if (action == ACTION_CLOSING)
3185 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3187 // SetDrawBackgroundMask(last_draw_background_mask);
3189 redraw_mask |= REDRAW_FIELD;
3193 if (action == ACTION_CLOSING &&
3194 game_status == GAME_MODE_PLAYING &&
3195 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3196 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3199 static void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3201 if (IS_MM_WALL(element))
3203 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3209 int graphic = el2preimg(element);
3211 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3212 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3217 void DrawLevel(int draw_background_mask)
3221 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3222 SetDrawBackgroundMask(draw_background_mask);
3226 for (x = BX1; x <= BX2; x++)
3227 for (y = BY1; y <= BY2; y++)
3228 DrawScreenField(x, y);
3230 redraw_mask |= REDRAW_FIELD;
3233 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3238 for (x = 0; x < size_x; x++)
3239 for (y = 0; y < size_y; y++)
3240 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3242 redraw_mask |= REDRAW_FIELD;
3245 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3249 for (x = 0; x < size_x; x++)
3250 for (y = 0; y < size_y; y++)
3251 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3253 redraw_mask |= REDRAW_FIELD;
3256 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3258 boolean show_level_border = (BorderElement != EL_EMPTY);
3259 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3260 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3261 int tile_size = preview.tile_size;
3262 int preview_width = preview.xsize * tile_size;
3263 int preview_height = preview.ysize * tile_size;
3264 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3265 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3266 int real_preview_width = real_preview_xsize * tile_size;
3267 int real_preview_height = real_preview_ysize * tile_size;
3268 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3269 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3272 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3275 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3277 dst_x += (preview_width - real_preview_width) / 2;
3278 dst_y += (preview_height - real_preview_height) / 2;
3280 for (x = 0; x < real_preview_xsize; x++)
3282 for (y = 0; y < real_preview_ysize; y++)
3284 int lx = from_x + x + (show_level_border ? -1 : 0);
3285 int ly = from_y + y + (show_level_border ? -1 : 0);
3286 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3287 getBorderElement(lx, ly));
3289 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3290 element, tile_size);
3294 redraw_mask |= REDRAW_FIELD;
3297 #define MICROLABEL_EMPTY 0
3298 #define MICROLABEL_LEVEL_NAME 1
3299 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3300 #define MICROLABEL_LEVEL_AUTHOR 3
3301 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3302 #define MICROLABEL_IMPORTED_FROM 5
3303 #define MICROLABEL_IMPORTED_BY_HEAD 6
3304 #define MICROLABEL_IMPORTED_BY 7
3306 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3308 int max_text_width = SXSIZE;
3309 int font_width = getFontWidth(font_nr);
3311 if (pos->align == ALIGN_CENTER)
3312 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3313 else if (pos->align == ALIGN_RIGHT)
3314 max_text_width = pos->x;
3316 max_text_width = SXSIZE - pos->x;
3318 return max_text_width / font_width;
3321 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3323 char label_text[MAX_OUTPUT_LINESIZE + 1];
3324 int max_len_label_text;
3325 int font_nr = pos->font;
3328 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3331 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3332 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3333 mode == MICROLABEL_IMPORTED_BY_HEAD)
3334 font_nr = pos->font_alt;
3336 max_len_label_text = getMaxTextLength(pos, font_nr);
3338 if (pos->size != -1)
3339 max_len_label_text = pos->size;
3341 for (i = 0; i < max_len_label_text; i++)
3342 label_text[i] = ' ';
3343 label_text[max_len_label_text] = '\0';
3345 if (strlen(label_text) > 0)
3346 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3349 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3350 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3351 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3352 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3353 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3354 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3355 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3356 max_len_label_text);
3357 label_text[max_len_label_text] = '\0';
3359 if (strlen(label_text) > 0)
3360 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3362 redraw_mask |= REDRAW_FIELD;
3365 static void DrawPreviewLevelLabel(int mode)
3367 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3370 static void DrawPreviewLevelInfo(int mode)
3372 if (mode == MICROLABEL_LEVEL_NAME)
3373 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3374 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3375 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3378 static void DrawPreviewLevelExt(boolean restart)
3380 static unsigned int scroll_delay = 0;
3381 static unsigned int label_delay = 0;
3382 static int from_x, from_y, scroll_direction;
3383 static int label_state, label_counter;
3384 unsigned int scroll_delay_value = preview.step_delay;
3385 boolean show_level_border = (BorderElement != EL_EMPTY);
3386 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3387 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3394 if (preview.anim_mode == ANIM_CENTERED)
3396 if (level_xsize > preview.xsize)
3397 from_x = (level_xsize - preview.xsize) / 2;
3398 if (level_ysize > preview.ysize)
3399 from_y = (level_ysize - preview.ysize) / 2;
3402 from_x += preview.xoffset;
3403 from_y += preview.yoffset;
3405 scroll_direction = MV_RIGHT;
3409 DrawPreviewLevelPlayfield(from_x, from_y);
3410 DrawPreviewLevelLabel(label_state);
3412 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3413 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3415 // initialize delay counters
3416 DelayReached(&scroll_delay, 0);
3417 DelayReached(&label_delay, 0);
3419 if (leveldir_current->name)
3421 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3422 char label_text[MAX_OUTPUT_LINESIZE + 1];
3423 int font_nr = pos->font;
3424 int max_len_label_text = getMaxTextLength(pos, font_nr);
3426 if (pos->size != -1)
3427 max_len_label_text = pos->size;
3429 strncpy(label_text, leveldir_current->name, max_len_label_text);
3430 label_text[max_len_label_text] = '\0';
3432 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3433 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3439 // scroll preview level, if needed
3440 if (preview.anim_mode != ANIM_NONE &&
3441 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3442 DelayReached(&scroll_delay, scroll_delay_value))
3444 switch (scroll_direction)
3449 from_x -= preview.step_offset;
3450 from_x = (from_x < 0 ? 0 : from_x);
3453 scroll_direction = MV_UP;
3457 if (from_x < level_xsize - preview.xsize)
3459 from_x += preview.step_offset;
3460 from_x = (from_x > level_xsize - preview.xsize ?
3461 level_xsize - preview.xsize : from_x);
3464 scroll_direction = MV_DOWN;
3470 from_y -= preview.step_offset;
3471 from_y = (from_y < 0 ? 0 : from_y);
3474 scroll_direction = MV_RIGHT;
3478 if (from_y < level_ysize - preview.ysize)
3480 from_y += preview.step_offset;
3481 from_y = (from_y > level_ysize - preview.ysize ?
3482 level_ysize - preview.ysize : from_y);
3485 scroll_direction = MV_LEFT;
3492 DrawPreviewLevelPlayfield(from_x, from_y);
3495 // !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!!
3496 // redraw micro level label, if needed
3497 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3498 !strEqual(level.author, ANONYMOUS_NAME) &&
3499 !strEqual(level.author, leveldir_current->name) &&
3500 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3502 int max_label_counter = 23;
3504 if (leveldir_current->imported_from != NULL &&
3505 strlen(leveldir_current->imported_from) > 0)
3506 max_label_counter += 14;
3507 if (leveldir_current->imported_by != NULL &&
3508 strlen(leveldir_current->imported_by) > 0)
3509 max_label_counter += 14;
3511 label_counter = (label_counter + 1) % max_label_counter;
3512 label_state = (label_counter >= 0 && label_counter <= 7 ?
3513 MICROLABEL_LEVEL_NAME :
3514 label_counter >= 9 && label_counter <= 12 ?
3515 MICROLABEL_LEVEL_AUTHOR_HEAD :
3516 label_counter >= 14 && label_counter <= 21 ?
3517 MICROLABEL_LEVEL_AUTHOR :
3518 label_counter >= 23 && label_counter <= 26 ?
3519 MICROLABEL_IMPORTED_FROM_HEAD :
3520 label_counter >= 28 && label_counter <= 35 ?
3521 MICROLABEL_IMPORTED_FROM :
3522 label_counter >= 37 && label_counter <= 40 ?
3523 MICROLABEL_IMPORTED_BY_HEAD :
3524 label_counter >= 42 && label_counter <= 49 ?
3525 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3527 if (leveldir_current->imported_from == NULL &&
3528 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3529 label_state == MICROLABEL_IMPORTED_FROM))
3530 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3531 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3533 DrawPreviewLevelLabel(label_state);
3537 void DrawPreviewPlayers(void)
3539 if (game_status != GAME_MODE_MAIN)
3542 // do not draw preview players if level preview redefined, but players aren't
3543 if (preview.redefined && !menu.main.preview_players.redefined)
3546 boolean player_found[MAX_PLAYERS];
3547 int num_players = 0;
3550 for (i = 0; i < MAX_PLAYERS; i++)
3551 player_found[i] = FALSE;
3553 // check which players can be found in the level (simple approach)
3554 for (x = 0; x < lev_fieldx; x++)
3556 for (y = 0; y < lev_fieldy; y++)
3558 int element = level.field[x][y];
3560 if (ELEM_IS_PLAYER(element))
3562 int player_nr = GET_PLAYER_NR(element);
3564 player_nr = MIN(MAX(0, player_nr), MAX_PLAYERS - 1);
3566 if (!player_found[player_nr])
3569 player_found[player_nr] = TRUE;
3574 struct TextPosInfo *pos = &menu.main.preview_players;
3575 int tile_size = pos->tile_size;
3576 int border_size = pos->border_size;
3577 int player_xoffset_raw = (pos->vertical ? 0 : tile_size + border_size);
3578 int player_yoffset_raw = (pos->vertical ? tile_size + border_size : 0);
3579 int player_xoffset = (pos->xoffset != -1 ? pos->xoffset : player_xoffset_raw);
3580 int player_yoffset = (pos->yoffset != -1 ? pos->yoffset : player_yoffset_raw);
3581 int max_players_width = (MAX_PLAYERS - 1) * player_xoffset + tile_size;
3582 int max_players_height = (MAX_PLAYERS - 1) * player_yoffset + tile_size;
3583 int all_players_width = (num_players - 1) * player_xoffset + tile_size;
3584 int all_players_height = (num_players - 1) * player_yoffset + tile_size;
3585 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3586 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3587 int xpos = SX + ALIGNED_XPOS(pos->x, all_players_width, pos->align);
3588 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3590 // clear area in which the players will be drawn
3591 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3592 max_players_width, max_players_height);
3594 if (!network.enabled && !setup.team_mode)
3597 // only draw players if level is suited for team mode
3598 if (num_players < 2)
3601 // draw all players that were found in the level
3602 for (i = 0; i < MAX_PLAYERS; i++)
3604 if (player_found[i])
3606 int graphic = el2img(EL_PLAYER_1 + i);
3608 DrawSizedGraphicThruMaskExt(drawto, xpos, ypos, graphic, 0, tile_size);
3610 xpos += player_xoffset;
3611 ypos += player_yoffset;
3616 void DrawPreviewLevelInitial(void)
3618 DrawPreviewLevelExt(TRUE);
3619 DrawPreviewPlayers();
3622 void DrawPreviewLevelAnimation(void)
3624 DrawPreviewLevelExt(FALSE);
3627 static void DrawNetworkPlayer(int x, int y, int player_nr, int tile_size,
3628 int border_size, int font_nr)
3630 int graphic = el2img(EL_PLAYER_1 + player_nr);
3631 int font_height = getFontHeight(font_nr);
3632 int player_height = MAX(tile_size, font_height);
3633 int xoffset_text = tile_size + border_size;
3634 int yoffset_text = (player_height - font_height) / 2;
3635 int yoffset_graphic = (player_height - tile_size) / 2;
3636 char *player_name = getNetworkPlayerName(player_nr + 1);
3638 DrawSizedGraphicThruMaskExt(drawto, x, y + yoffset_graphic, graphic, 0,
3640 DrawText(x + xoffset_text, y + yoffset_text, player_name, font_nr);
3643 static void DrawNetworkPlayersExt(boolean force)
3645 if (game_status != GAME_MODE_MAIN)
3648 if (!network.connected && !force)
3651 // do not draw network players if level preview redefined, but players aren't
3652 if (preview.redefined && !menu.main.network_players.redefined)
3655 int num_players = 0;
3658 for (i = 0; i < MAX_PLAYERS; i++)
3659 if (stored_player[i].connected_network)
3662 struct TextPosInfo *pos = &menu.main.network_players;
3663 int tile_size = pos->tile_size;
3664 int border_size = pos->border_size;
3665 int xoffset_text = tile_size + border_size;
3666 int font_nr = pos->font;
3667 int font_width = getFontWidth(font_nr);
3668 int font_height = getFontHeight(font_nr);
3669 int player_height = MAX(tile_size, font_height);
3670 int player_yoffset = player_height + border_size;
3671 int max_players_width = xoffset_text + MAX_PLAYER_NAME_LEN * font_width;
3672 int max_players_height = MAX_PLAYERS * player_yoffset - border_size;
3673 int all_players_height = num_players * player_yoffset - border_size;
3674 int max_xpos = SX + ALIGNED_XPOS(pos->x, max_players_width, pos->align);
3675 int max_ypos = SY + ALIGNED_YPOS(pos->y, max_players_height, pos->valign);
3676 int ypos = SY + ALIGNED_YPOS(pos->y, all_players_height, pos->valign);
3678 ClearRectangleOnBackground(drawto, max_xpos, max_ypos,
3679 max_players_width, max_players_height);
3681 // first draw local network player ...
3682 for (i = 0; i < MAX_PLAYERS; i++)
3684 if (stored_player[i].connected_network &&
3685 stored_player[i].connected_locally)
3687 char *player_name = getNetworkPlayerName(i + 1);
3688 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3689 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3691 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3693 ypos += player_yoffset;
3697 // ... then draw all other network players
3698 for (i = 0; i < MAX_PLAYERS; i++)
3700 if (stored_player[i].connected_network &&
3701 !stored_player[i].connected_locally)
3703 char *player_name = getNetworkPlayerName(i + 1);
3704 int player_width = xoffset_text + getTextWidth(player_name, font_nr);
3705 int xpos = SX + ALIGNED_XPOS(pos->x, player_width, pos->align);
3707 DrawNetworkPlayer(xpos, ypos, i, tile_size, border_size, font_nr);
3709 ypos += player_yoffset;
3714 void DrawNetworkPlayers(void)
3716 DrawNetworkPlayersExt(FALSE);
3719 void ClearNetworkPlayers(void)
3721 DrawNetworkPlayersExt(TRUE);
3724 static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3725 int graphic, int sync_frame,
3728 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3730 if (mask_mode == USE_MASKING)
3731 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3733 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3736 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3737 int graphic, int sync_frame, int mask_mode)
3739 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3741 if (mask_mode == USE_MASKING)
3742 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3744 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3747 static void DrawGraphicAnimation(int x, int y, int graphic)
3749 int lx = LEVELX(x), ly = LEVELY(y);
3751 if (!IN_SCR_FIELD(x, y))
3754 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3755 graphic, GfxFrame[lx][ly], NO_MASKING);
3757 MarkTileDirty(x, y);
3760 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3762 int lx = LEVELX(x), ly = LEVELY(y);
3764 if (!IN_SCR_FIELD(x, y))
3767 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3768 graphic, GfxFrame[lx][ly], NO_MASKING);
3769 MarkTileDirty(x, y);
3772 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3774 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3777 void DrawLevelElementAnimation(int x, int y, int element)
3779 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3781 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3784 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3786 int sx = SCREENX(x), sy = SCREENY(y);
3788 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3791 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3794 DrawGraphicAnimation(sx, sy, graphic);
3797 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3798 DrawLevelFieldCrumbled(x, y);
3800 if (GFX_CRUMBLED(Tile[x][y]))
3801 DrawLevelFieldCrumbled(x, y);
3805 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3807 int sx = SCREENX(x), sy = SCREENY(y);
3810 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3813 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3815 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3818 DrawGraphicAnimation(sx, sy, graphic);
3820 if (GFX_CRUMBLED(element))
3821 DrawLevelFieldCrumbled(x, y);
3824 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3826 if (player->use_murphy)
3828 // this works only because currently only one player can be "murphy" ...
3829 static int last_horizontal_dir = MV_LEFT;
3830 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3832 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3833 last_horizontal_dir = move_dir;
3835 if (graphic == IMG_SP_MURPHY) // undefined => use special graphic
3837 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3839 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3845 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3848 static boolean equalGraphics(int graphic1, int graphic2)
3850 struct GraphicInfo *g1 = &graphic_info[graphic1];
3851 struct GraphicInfo *g2 = &graphic_info[graphic2];
3853 return (g1->bitmap == g2->bitmap &&
3854 g1->src_x == g2->src_x &&
3855 g1->src_y == g2->src_y &&
3856 g1->anim_frames == g2->anim_frames &&
3857 g1->anim_delay == g2->anim_delay &&
3858 g1->anim_mode == g2->anim_mode);
3861 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3865 DRAW_PLAYER_STAGE_INIT = 0,
3866 DRAW_PLAYER_STAGE_LAST_FIELD,
3867 DRAW_PLAYER_STAGE_FIELD_UNDER_PLAYER,
3868 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3869 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3870 DRAW_PLAYER_STAGE_PLAYER,
3872 DRAW_PLAYER_STAGE_PLAYER,
3873 DRAW_PLAYER_STAGE_ELEMENT_PUSHED,
3875 DRAW_PLAYER_STAGE_ELEMENT_OVER_PLAYER,
3876 DRAW_PLAYER_STAGE_FIELD_OVER_PLAYER,
3878 NUM_DRAW_PLAYER_STAGES
3881 static void DrawPlayerExt(struct PlayerInfo *player, int drawing_stage)
3883 static int static_last_player_graphic[MAX_PLAYERS];
3884 static int static_last_player_frame[MAX_PLAYERS];
3885 static boolean static_player_is_opaque[MAX_PLAYERS];
3886 static boolean draw_player[MAX_PLAYERS];
3887 int pnr = player->index_nr;
3889 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3891 static_last_player_graphic[pnr] = getPlayerGraphic(player, player->MovDir);
3892 static_last_player_frame[pnr] = player->Frame;
3893 static_player_is_opaque[pnr] = FALSE;
3895 draw_player[pnr] = TRUE;
3898 if (!draw_player[pnr])
3902 if (!IN_LEV_FIELD(player->jx, player->jy))
3904 Debug("draw:DrawPlayerExt", "x = %d, y = %d", player->jx, player->jy);
3905 Debug("draw:DrawPlayerExt", "This should never happen!");
3907 draw_player[pnr] = FALSE;
3913 int last_player_graphic = static_last_player_graphic[pnr];
3914 int last_player_frame = static_last_player_frame[pnr];
3915 boolean player_is_opaque = static_player_is_opaque[pnr];
3917 int jx = player->jx;
3918 int jy = player->jy;
3919 int move_dir = (player->is_waiting ? player->dir_waiting : player->MovDir);
3920 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3921 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3922 int last_jx = (player->is_moving ? jx - dx : jx);
3923 int last_jy = (player->is_moving ? jy - dy : jy);
3924 int next_jx = jx + dx;
3925 int next_jy = jy + dy;
3926 boolean player_is_moving = (player->MovPos != 0 ? TRUE : FALSE);
3927 int sx = SCREENX(jx);
3928 int sy = SCREENY(jy);
3929 int sxx = (move_dir == MV_LEFT || move_dir == MV_RIGHT ? player->GfxPos : 0);
3930 int syy = (move_dir == MV_UP || move_dir == MV_DOWN ? player->GfxPos : 0);
3931 int element = Tile[jx][jy];
3932 int last_element = Tile[last_jx][last_jy];
3933 int action = (player->is_pushing ? ACTION_PUSHING :
3934 player->is_digging ? ACTION_DIGGING :
3935 player->is_collecting ? ACTION_COLLECTING :
3936 player->is_moving ? ACTION_MOVING :
3937 player->is_snapping ? ACTION_SNAPPING :
3938 player->is_dropping ? ACTION_DROPPING :
3939 player->is_waiting ? player->action_waiting :
3942 if (drawing_stage == DRAW_PLAYER_STAGE_INIT)
3944 // ------------------------------------------------------------------------
3945 // initialize drawing the player
3946 // ------------------------------------------------------------------------
3948 draw_player[pnr] = FALSE;
3950 // GfxElement[][] is set to the element the player is digging or collecting;
3951 // remove also for off-screen player if the player is not moving anymore
3952 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3953 GfxElement[jx][jy] = EL_UNDEFINED;
3955 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3958 if (element == EL_EXPLOSION)
3961 InitPlayerGfxAnimation(player, action, move_dir);
3963 draw_player[pnr] = TRUE;
3965 else if (drawing_stage == DRAW_PLAYER_STAGE_LAST_FIELD)