1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 /* select level set with EMC X11 graphics before activating EM GFX debugging */
27 #define DEBUG_EM_GFX FALSE
28 #define DEBUG_FRAME_TIME FALSE
30 /* tool button identifiers */
31 #define TOOL_CTRL_ID_YES 0
32 #define TOOL_CTRL_ID_NO 1
33 #define TOOL_CTRL_ID_CONFIRM 2
34 #define TOOL_CTRL_ID_PLAYER_1 3
35 #define TOOL_CTRL_ID_PLAYER_2 4
36 #define TOOL_CTRL_ID_PLAYER_3 5
37 #define TOOL_CTRL_ID_PLAYER_4 6
39 #define NUM_TOOL_BUTTONS 7
41 /* constants for number of doors and door parts */
43 #define NUM_PANELS NUM_DOORS
44 // #define NUM_PANELS 0
45 #define MAX_PARTS_PER_DOOR 8
46 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
47 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
50 struct DoorPartOrderInfo
56 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
58 struct DoorPartControlInfo
62 struct DoorPartPosInfo *pos;
65 static struct DoorPartControlInfo door_part_controls[] =
69 IMG_GFX_DOOR_1_PART_1,
74 IMG_GFX_DOOR_1_PART_2,
79 IMG_GFX_DOOR_1_PART_3,
84 IMG_GFX_DOOR_1_PART_4,
89 IMG_GFX_DOOR_1_PART_5,
94 IMG_GFX_DOOR_1_PART_6,
99 IMG_GFX_DOOR_1_PART_7,
104 IMG_GFX_DOOR_1_PART_8,
110 IMG_GFX_DOOR_2_PART_1,
115 IMG_GFX_DOOR_2_PART_2,
120 IMG_GFX_DOOR_2_PART_3,
125 IMG_GFX_DOOR_2_PART_4,
130 IMG_GFX_DOOR_2_PART_5,
135 IMG_GFX_DOOR_2_PART_6,
140 IMG_GFX_DOOR_2_PART_7,
145 IMG_GFX_DOOR_2_PART_8,
151 IMG_BACKGROUND_PANEL,
168 /* forward declaration for internal use */
169 static void UnmapToolButtons();
170 static void HandleToolButtons(struct GadgetInfo *);
171 static int el_act_dir2crm(int, int, int);
172 static int el_act2crm(int, int);
174 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
175 static int request_gadget_id = -1;
177 static char *print_if_not_empty(int element)
179 static char *s = NULL;
180 char *token_name = element_info[element].token_name;
185 s = checked_malloc(strlen(token_name) + 10 + 1);
187 if (element != EL_EMPTY)
188 sprintf(s, "%d\t['%s']", element, token_name);
190 sprintf(s, "%d", element);
195 int correctLevelPosX_EM(int lx)
198 lx -= (BorderElement != EL_EMPTY ? 1 : 0);
203 int correctLevelPosY_EM(int ly)
206 ly -= (BorderElement != EL_EMPTY ? 1 : 0);
211 static int getFieldbufferOffsetX_RND()
213 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
214 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
215 int dx_var = dx * TILESIZE_VAR / TILESIZE;
218 if (EVEN(SCR_FIELDX))
220 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
222 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
223 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
225 fx += (dx_var > 0 ? TILEX_VAR : 0);
232 if (full_lev_fieldx <= SCR_FIELDX)
234 if (EVEN(SCR_FIELDX))
235 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
237 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
243 static int getFieldbufferOffsetY_RND()
245 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
246 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
247 int dy_var = dy * TILESIZE_VAR / TILESIZE;
250 if (EVEN(SCR_FIELDY))
252 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
254 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
255 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
257 fy += (dy_var > 0 ? TILEY_VAR : 0);
264 if (full_lev_fieldy <= SCR_FIELDY)
266 if (EVEN(SCR_FIELDY))
267 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
269 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
275 static int getLevelFromScreenX_RND(int sx)
277 int fx = getFieldbufferOffsetX_RND();
280 int lx = LEVELX((px + dx) / TILESIZE_VAR);
285 static int getLevelFromScreenY_RND(int sy)
287 int fy = getFieldbufferOffsetY_RND();
290 int ly = LEVELY((py + dy) / TILESIZE_VAR);
295 static int getLevelFromScreenX_EM(int sx)
297 int level_xsize = level.native_em_level->lev->width;
298 int full_xsize = level_xsize * TILESIZE_VAR;
300 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
302 int fx = getFieldbufferOffsetX_EM();
305 int lx = LEVELX((px + dx) / TILESIZE_VAR);
307 lx = correctLevelPosX_EM(lx);
312 static int getLevelFromScreenY_EM(int sy)
314 int level_ysize = level.native_em_level->lev->height;
315 int full_ysize = level_ysize * TILESIZE_VAR;
317 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
319 int fy = getFieldbufferOffsetY_EM();
322 int ly = LEVELY((py + dy) / TILESIZE_VAR);
324 ly = correctLevelPosY_EM(ly);
329 static int getLevelFromScreenX_SP(int sx)
331 int menBorder = setup.sp_show_border_elements;
332 int level_xsize = level.native_sp_level->width;
333 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
335 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
337 int fx = getFieldbufferOffsetX_SP();
340 int lx = LEVELX((px + dx) / TILESIZE_VAR);
345 static int getLevelFromScreenY_SP(int sy)
347 int menBorder = setup.sp_show_border_elements;
348 int level_ysize = level.native_sp_level->height;
349 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
351 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
353 int fy = getFieldbufferOffsetY_SP();
356 int ly = LEVELY((py + dy) / TILESIZE_VAR);
361 static int getLevelFromScreenX_MM(int sx)
364 int level_xsize = level.native_mm_level->fieldx;
365 int full_xsize = level_xsize * TILESIZE_VAR;
367 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
371 int lx = px / TILESIZE_VAR;
376 static int getLevelFromScreenY_MM(int sy)
379 int level_ysize = level.native_mm_level->fieldy;
380 int full_ysize = level_ysize * TILESIZE_VAR;
382 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
386 int ly = py / TILESIZE_VAR;
391 int getLevelFromScreenX(int x)
393 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
394 return getLevelFromScreenX_EM(x);
395 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
396 return getLevelFromScreenX_SP(x);
397 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
398 return getLevelFromScreenX_MM(x);
400 return getLevelFromScreenX_RND(x);
403 int getLevelFromScreenY(int y)
405 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
406 return getLevelFromScreenY_EM(y);
407 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
408 return getLevelFromScreenY_SP(y);
409 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
410 return getLevelFromScreenY_MM(y);
412 return getLevelFromScreenY_RND(y);
415 void DumpTile(int x, int y)
421 printf_line("-", 79);
422 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
423 printf_line("-", 79);
425 if (!IN_LEV_FIELD(x, y))
427 printf("(not in level field)\n");
433 token_name = element_info[Feld[x][y]].token_name;
435 printf(" Feld: %d\t['%s']\n", Feld[x][y], token_name);
436 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
437 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
438 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
439 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
440 printf(" MovPos: %d\n", MovPos[x][y]);
441 printf(" MovDir: %d\n", MovDir[x][y]);
442 printf(" MovDelay: %d\n", MovDelay[x][y]);
443 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
444 printf(" CustomValue: %d\n", CustomValue[x][y]);
445 printf(" GfxElement: %d\n", GfxElement[x][y]);
446 printf(" GfxAction: %d\n", GfxAction[x][y]);
447 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
448 printf(" Player x/y: %d, %d\n", local_player->jx, local_player->jy);
452 void DumpTileFromScreen(int sx, int sy)
454 int lx = getLevelFromScreenX(sx);
455 int ly = getLevelFromScreenY(sy);
460 void SetDrawtoField(int mode)
462 if (mode == DRAW_TO_FIELDBUFFER)
468 BX2 = SCR_FIELDX + 1;
469 BY2 = SCR_FIELDY + 1;
471 drawto_field = fieldbuffer;
473 else /* DRAW_TO_BACKBUFFER */
479 BX2 = SCR_FIELDX - 1;
480 BY2 = SCR_FIELDY - 1;
482 drawto_field = backbuffer;
486 static void RedrawPlayfield_RND()
488 if (game.envelope_active)
491 DrawLevel(REDRAW_ALL);
495 void RedrawPlayfield()
497 if (game_status != GAME_MODE_PLAYING)
500 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
501 RedrawPlayfield_EM(TRUE);
502 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
503 RedrawPlayfield_SP(TRUE);
504 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
505 RedrawPlayfield_MM();
506 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
507 RedrawPlayfield_RND();
509 BlitScreenToBitmap(backbuffer);
511 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
515 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
518 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
519 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
521 if (x == -1 && y == -1)
524 if (draw_target == DRAW_TO_SCREEN)
525 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
527 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
530 static void DrawMaskedBorderExt_FIELD(int draw_target)
532 if (global.border_status >= GAME_MODE_MAIN &&
533 global.border_status <= GAME_MODE_PLAYING &&
534 border.draw_masked[global.border_status])
535 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
539 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
541 // when drawing to backbuffer, never draw border over open doors
542 if (draw_target == DRAW_TO_BACKBUFFER &&
543 (GetDoorState() & DOOR_OPEN_1))
546 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
547 (global.border_status != GAME_MODE_EDITOR ||
548 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
549 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
552 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
554 // when drawing to backbuffer, never draw border over open doors
555 if (draw_target == DRAW_TO_BACKBUFFER &&
556 (GetDoorState() & DOOR_OPEN_2))
559 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
560 global.border_status != GAME_MODE_EDITOR)
561 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
564 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
566 /* currently not available */
569 static void DrawMaskedBorderExt_ALL(int draw_target)
571 DrawMaskedBorderExt_FIELD(draw_target);
572 DrawMaskedBorderExt_DOOR_1(draw_target);
573 DrawMaskedBorderExt_DOOR_2(draw_target);
574 DrawMaskedBorderExt_DOOR_3(draw_target);
577 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
579 /* never draw masked screen borders on borderless screens */
580 if (global.border_status == GAME_MODE_LOADING ||
581 global.border_status == GAME_MODE_TITLE)
584 if (redraw_mask & REDRAW_ALL)
585 DrawMaskedBorderExt_ALL(draw_target);
588 if (redraw_mask & REDRAW_FIELD)
589 DrawMaskedBorderExt_FIELD(draw_target);
590 if (redraw_mask & REDRAW_DOOR_1)
591 DrawMaskedBorderExt_DOOR_1(draw_target);
592 if (redraw_mask & REDRAW_DOOR_2)
593 DrawMaskedBorderExt_DOOR_2(draw_target);
594 if (redraw_mask & REDRAW_DOOR_3)
595 DrawMaskedBorderExt_DOOR_3(draw_target);
599 void DrawMaskedBorder_FIELD()
601 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
604 void DrawMaskedBorder(int redraw_mask)
606 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
609 void DrawMaskedBorderToTarget(int draw_target)
611 if (draw_target == DRAW_TO_BACKBUFFER ||
612 draw_target == DRAW_TO_SCREEN)
614 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
618 int last_border_status = global.border_status;
620 if (draw_target == DRAW_TO_FADE_SOURCE)
622 global.border_status = gfx.fade_border_source_status;
623 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
625 else if (draw_target == DRAW_TO_FADE_TARGET)
627 global.border_status = gfx.fade_border_target_status;
628 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
631 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
633 global.border_status = last_border_status;
634 gfx.masked_border_bitmap_ptr = backbuffer;
638 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
640 int fx = getFieldbufferOffsetX_RND();
641 int fy = getFieldbufferOffsetY_RND();
643 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
646 void BlitScreenToBitmap(Bitmap *target_bitmap)
648 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
649 BlitScreenToBitmap_EM(target_bitmap);
650 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
651 BlitScreenToBitmap_SP(target_bitmap);
652 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
653 BlitScreenToBitmap_MM(target_bitmap);
654 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
655 BlitScreenToBitmap_RND(target_bitmap);
657 redraw_mask |= REDRAW_FIELD;
660 void DrawFramesPerSecond()
663 int font_nr = FONT_TEXT_2;
664 int font_width = getFontWidth(font_nr);
665 int draw_deactivation_mask = GetDrawDeactivationMask();
666 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
668 /* draw FPS with leading space (needed if field buffer deactivated) */
669 sprintf(text, " %04.1f fps", global.frames_per_second);
671 /* override draw deactivation mask (required for invisible warp mode) */
672 SetDrawDeactivationMask(REDRAW_NONE);
674 /* draw opaque FPS if field buffer deactivated, else draw masked FPS */
675 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
676 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
678 /* set draw deactivation mask to previous value */
679 SetDrawDeactivationMask(draw_deactivation_mask);
681 /* force full-screen redraw in this frame */
682 redraw_mask = REDRAW_ALL;
686 static void PrintFrameTimeDebugging()
688 static unsigned int last_counter = 0;
689 unsigned int counter = Counter();
690 int diff_1 = counter - last_counter;
691 int diff_2 = diff_1 - GAME_FRAME_DELAY;
693 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
694 char diff_bar[2 * diff_2_max + 5];
698 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
700 for (i = 0; i < diff_2_max; i++)
701 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
702 i >= diff_2_max - diff_2_cut ? '-' : ' ');
704 diff_bar[pos++] = '|';
706 for (i = 0; i < diff_2_max; i++)
707 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
709 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
711 diff_bar[pos++] = '\0';
713 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
716 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
719 last_counter = counter;
723 static int unifiedRedrawMask(int mask)
725 if (mask & REDRAW_ALL)
728 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
734 static boolean equalRedrawMasks(int mask_1, int mask_2)
736 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
741 static int last_redraw_mask = REDRAW_NONE;
743 // force screen redraw in every frame to continue drawing global animations
744 // (but always use the last redraw mask to prevent unwanted side effects)
745 if (redraw_mask == REDRAW_NONE)
746 redraw_mask = last_redraw_mask;
748 last_redraw_mask = redraw_mask;
751 // masked border now drawn immediately when blitting backbuffer to window
753 // draw masked border to all viewports, if defined
754 DrawMaskedBorder(redraw_mask);
757 // draw frames per second (only if debug mode is enabled)
758 if (redraw_mask & REDRAW_FPS)
759 DrawFramesPerSecond();
761 // remove playfield redraw before potentially merging with doors redraw
762 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
763 redraw_mask &= ~REDRAW_FIELD;
765 // redraw complete window if both playfield and (some) doors need redraw
766 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
767 redraw_mask = REDRAW_ALL;
769 /* although redrawing the whole window would be fine for normal gameplay,
770 being able to only redraw the playfield is required for deactivating
771 certain drawing areas (mainly playfield) to work, which is needed for
772 warp-forward to be fast enough (by skipping redraw of most frames) */
774 if (redraw_mask & REDRAW_ALL)
776 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
778 else if (redraw_mask & REDRAW_FIELD)
780 BlitBitmap(backbuffer, window,
781 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
783 else if (redraw_mask & REDRAW_DOORS)
785 // merge door areas to prevent calling screen redraw more than once
791 if (redraw_mask & REDRAW_DOOR_1)
795 x2 = MAX(x2, DX + DXSIZE);
796 y2 = MAX(y2, DY + DYSIZE);
799 if (redraw_mask & REDRAW_DOOR_2)
803 x2 = MAX(x2, VX + VXSIZE);
804 y2 = MAX(y2, VY + VYSIZE);
807 if (redraw_mask & REDRAW_DOOR_3)
811 x2 = MAX(x2, EX + EXSIZE);
812 y2 = MAX(y2, EY + EYSIZE);
815 // make sure that at least one pixel is blitted, and inside the screen
816 // (else nothing is blitted, causing the animations not to be updated)
817 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
818 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
819 x2 = MIN(MAX(1, x2), WIN_XSIZE);
820 y2 = MIN(MAX(1, y2), WIN_YSIZE);
822 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
825 redraw_mask = REDRAW_NONE;
828 PrintFrameTimeDebugging();
832 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
834 unsigned int frame_delay_value_old = GetVideoFrameDelay();
836 SetVideoFrameDelay(frame_delay_value);
840 SetVideoFrameDelay(frame_delay_value_old);
843 static int fade_type_skip = FADE_TYPE_NONE;
845 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
847 void (*draw_border_function)(void) = NULL;
848 int x, y, width, height;
849 int fade_delay, post_delay;
851 if (fade_type == FADE_TYPE_FADE_OUT)
853 if (fade_type_skip != FADE_TYPE_NONE)
855 /* skip all fade operations until specified fade operation */
856 if (fade_type & fade_type_skip)
857 fade_type_skip = FADE_TYPE_NONE;
862 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
866 redraw_mask |= fade_mask;
868 if (fade_type == FADE_TYPE_SKIP)
870 fade_type_skip = fade_mode;
875 fade_delay = fading.fade_delay;
876 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
878 if (fade_type_skip != FADE_TYPE_NONE)
880 /* skip all fade operations until specified fade operation */
881 if (fade_type & fade_type_skip)
882 fade_type_skip = FADE_TYPE_NONE;
887 if (global.autoplay_leveldir)
892 if (fade_mask == REDRAW_FIELD)
897 height = FADE_SYSIZE;
899 if (border.draw_masked_when_fading)
900 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
902 DrawMaskedBorder_FIELD(); /* draw once */
904 else /* REDRAW_ALL */
912 if (!setup.fade_screens ||
914 fading.fade_mode == FADE_MODE_NONE)
916 if (fade_mode == FADE_MODE_FADE_OUT)
919 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
921 redraw_mask &= ~fade_mask;
926 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
927 draw_border_function);
929 redraw_mask &= ~fade_mask;
932 static void SetScreenStates_BeforeFadingIn()
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 if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
939 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
941 // set screen mode for animations back to fading
942 global.anim_status = GAME_MODE_PSEUDO_FADING;
945 static void SetScreenStates_AfterFadingIn()
947 // store new source screen (to use correct masked border for fading)
948 gfx.fade_border_source_status = global.border_status;
950 global.anim_status = global.anim_status_next;
953 static void SetScreenStates_BeforeFadingOut()
955 // store new target screen (to use correct masked border for fading)
956 gfx.fade_border_target_status = game_status;
958 // set screen mode for animations to fading
959 global.anim_status = GAME_MODE_PSEUDO_FADING;
961 // store backbuffer with all animations that will be stopped for fading out
962 if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
963 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
966 static void SetScreenStates_AfterFadingOut()
968 global.border_status = game_status;
971 void FadeIn(int fade_mask)
973 SetScreenStates_BeforeFadingIn();
976 DrawMaskedBorder(REDRAW_ALL);
979 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
980 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
982 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
986 FADE_SXSIZE = FULL_SXSIZE;
987 FADE_SYSIZE = FULL_SYSIZE;
989 if (game_status == GAME_MODE_PLAYING &&
990 strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
991 SetOverlayActive(TRUE);
993 SetScreenStates_AfterFadingIn();
995 // force update of global animation status in case of rapid screen changes
996 redraw_mask = REDRAW_ALL;
1000 void FadeOut(int fade_mask)
1002 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1003 if (!equalRedrawMasks(fade_mask, redraw_mask))
1006 SetScreenStates_BeforeFadingOut();
1008 SetOverlayActive(FALSE);
1011 DrawMaskedBorder(REDRAW_ALL);
1014 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1015 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1017 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1019 SetScreenStates_AfterFadingOut();
1022 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1024 static struct TitleFadingInfo fading_leave_stored;
1027 fading_leave_stored = fading_leave;
1029 fading = fading_leave_stored;
1032 void FadeSetEnterMenu()
1034 fading = menu.enter_menu;
1036 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1039 void FadeSetLeaveMenu()
1041 fading = menu.leave_menu;
1043 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1046 void FadeSetEnterScreen()
1048 fading = menu.enter_screen[game_status];
1050 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
1053 void FadeSetNextScreen()
1055 fading = menu.next_screen[game_status];
1057 // (do not overwrite fade mode set by FadeSetEnterScreen)
1058 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1061 void FadeSetLeaveScreen()
1063 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
1066 void FadeSetFromType(int type)
1068 if (type & TYPE_ENTER_SCREEN)
1069 FadeSetEnterScreen();
1070 else if (type & TYPE_ENTER)
1072 else if (type & TYPE_LEAVE)
1076 void FadeSetDisabled()
1078 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1080 fading = fading_none;
1083 void FadeSkipNextFadeIn()
1085 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1088 void FadeSkipNextFadeOut()
1090 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1093 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1095 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1097 return (graphic == IMG_UNDEFINED ? NULL :
1098 graphic_info[graphic].bitmap != NULL || redefined ?
1099 graphic_info[graphic].bitmap :
1100 graphic_info[default_graphic].bitmap);
1103 Bitmap *getBackgroundBitmap(int graphic)
1105 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1108 Bitmap *getGlobalBorderBitmap(int graphic)
1110 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1113 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1116 (status == GAME_MODE_MAIN ||
1117 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1118 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1119 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1120 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1123 return getGlobalBorderBitmap(graphic);
1126 void SetWindowBackgroundImageIfDefined(int graphic)
1128 if (graphic_info[graphic].bitmap)
1129 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1132 void SetMainBackgroundImageIfDefined(int graphic)
1134 if (graphic_info[graphic].bitmap)
1135 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1138 void SetDoorBackgroundImageIfDefined(int graphic)
1140 if (graphic_info[graphic].bitmap)
1141 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1144 void SetWindowBackgroundImage(int graphic)
1146 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1149 void SetMainBackgroundImage(int graphic)
1151 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1154 void SetDoorBackgroundImage(int graphic)
1156 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1159 void SetPanelBackground()
1161 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1163 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1164 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1166 SetDoorBackgroundBitmap(bitmap_db_panel);
1169 void DrawBackground(int x, int y, int width, int height)
1171 /* "drawto" might still point to playfield buffer here (hall of fame) */
1172 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1174 if (IN_GFX_FIELD_FULL(x, y))
1175 redraw_mask |= REDRAW_FIELD;
1176 else if (IN_GFX_DOOR_1(x, y))
1177 redraw_mask |= REDRAW_DOOR_1;
1178 else if (IN_GFX_DOOR_2(x, y))
1179 redraw_mask |= REDRAW_DOOR_2;
1180 else if (IN_GFX_DOOR_3(x, y))
1181 redraw_mask |= REDRAW_DOOR_3;
1184 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1186 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1188 if (font->bitmap == NULL)
1191 DrawBackground(x, y, width, height);
1194 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1196 struct GraphicInfo *g = &graphic_info[graphic];
1198 if (g->bitmap == NULL)
1201 DrawBackground(x, y, width, height);
1204 static int game_status_last = -1;
1205 static Bitmap *global_border_bitmap_last = NULL;
1206 static Bitmap *global_border_bitmap = NULL;
1207 static int real_sx_last = -1, real_sy_last = -1;
1208 static int full_sxsize_last = -1, full_sysize_last = -1;
1209 static int dx_last = -1, dy_last = -1;
1210 static int dxsize_last = -1, dysize_last = -1;
1211 static int vx_last = -1, vy_last = -1;
1212 static int vxsize_last = -1, vysize_last = -1;
1214 boolean CheckIfGlobalBorderHasChanged()
1216 // if game status has not changed, global border has not changed either
1217 if (game_status == game_status_last)
1220 // determine and store new global border bitmap for current game status
1221 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1223 return (global_border_bitmap_last != global_border_bitmap);
1226 boolean CheckIfGlobalBorderRedrawIsNeeded()
1228 // if game status has not changed, nothing has to be redrawn
1229 if (game_status == game_status_last)
1232 // redraw if last screen was title screen
1233 if (game_status_last == GAME_MODE_TITLE)
1236 // redraw if global screen border has changed
1237 if (CheckIfGlobalBorderHasChanged())
1240 // redraw if position or size of playfield area has changed
1241 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1242 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1245 // redraw if position or size of door area has changed
1246 if (dx_last != DX || dy_last != DY ||
1247 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1250 // redraw if position or size of tape area has changed
1251 if (vx_last != VX || vy_last != VY ||
1252 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1258 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1261 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1263 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1266 void RedrawGlobalBorder()
1268 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1270 RedrawGlobalBorderFromBitmap(bitmap);
1272 redraw_mask = REDRAW_ALL;
1275 static void RedrawGlobalBorderIfNeeded()
1277 if (game_status == game_status_last)
1280 // copy current draw buffer to later copy back areas that have not changed
1281 if (game_status_last != GAME_MODE_TITLE)
1282 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1284 if (CheckIfGlobalBorderRedrawIsNeeded())
1286 // redraw global screen border (or clear, if defined to be empty)
1287 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1289 // copy previous playfield and door areas, if they are defined on both
1290 // previous and current screen and if they still have the same size
1292 if (real_sx_last != -1 && real_sy_last != -1 &&
1293 REAL_SX != -1 && REAL_SY != -1 &&
1294 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1295 BlitBitmap(bitmap_db_store_1, backbuffer,
1296 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1299 if (dx_last != -1 && dy_last != -1 &&
1300 DX != -1 && DY != -1 &&
1301 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1302 BlitBitmap(bitmap_db_store_1, backbuffer,
1303 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1305 if (vx_last != -1 && vy_last != -1 &&
1306 VX != -1 && VY != -1 &&
1307 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1308 BlitBitmap(bitmap_db_store_1, backbuffer,
1309 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1311 redraw_mask = REDRAW_ALL;
1314 game_status_last = game_status;
1316 global_border_bitmap_last = global_border_bitmap;
1318 real_sx_last = REAL_SX;
1319 real_sy_last = REAL_SY;
1320 full_sxsize_last = FULL_SXSIZE;
1321 full_sysize_last = FULL_SYSIZE;
1324 dxsize_last = DXSIZE;
1325 dysize_last = DYSIZE;
1328 vxsize_last = VXSIZE;
1329 vysize_last = VYSIZE;
1334 RedrawGlobalBorderIfNeeded();
1336 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1337 /* (when entering hall of fame after playing) */
1338 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1340 /* !!! maybe this should be done before clearing the background !!! */
1341 if (game_status == GAME_MODE_PLAYING)
1343 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1344 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1348 SetDrawtoField(DRAW_TO_BACKBUFFER);
1352 void MarkTileDirty(int x, int y)
1354 redraw_mask |= REDRAW_FIELD;
1357 void SetBorderElement()
1361 BorderElement = EL_EMPTY;
1363 /* the MM game engine does not use a visible border element */
1364 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1367 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1369 for (x = 0; x < lev_fieldx; x++)
1371 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1372 BorderElement = EL_STEELWALL;
1374 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1380 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1381 int max_array_fieldx, int max_array_fieldy,
1382 short field[max_array_fieldx][max_array_fieldy],
1383 int max_fieldx, int max_fieldy)
1387 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1388 static int safety = 0;
1390 /* check if starting field still has the desired content */
1391 if (field[from_x][from_y] == fill_element)
1396 if (safety > max_fieldx * max_fieldy)
1397 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1399 old_element = field[from_x][from_y];
1400 field[from_x][from_y] = fill_element;
1402 for (i = 0; i < 4; i++)
1404 x = from_x + check[i][0];
1405 y = from_y + check[i][1];
1407 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1408 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1409 field, max_fieldx, max_fieldy);
1415 void FloodFillLevel(int from_x, int from_y, int fill_element,
1416 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1417 int max_fieldx, int max_fieldy)
1419 FloodFillLevelExt(from_x, from_y, fill_element,
1420 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1421 max_fieldx, max_fieldy);
1424 void SetRandomAnimationValue(int x, int y)
1426 gfx.anim_random_frame = GfxRandom[x][y];
1429 int getGraphicAnimationFrame(int graphic, int sync_frame)
1431 /* animation synchronized with global frame counter, not move position */
1432 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1433 sync_frame = FrameCounter;
1435 return getAnimationFrame(graphic_info[graphic].anim_frames,
1436 graphic_info[graphic].anim_delay,
1437 graphic_info[graphic].anim_mode,
1438 graphic_info[graphic].anim_start_frame,
1442 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1444 struct GraphicInfo *g = &graphic_info[graphic];
1445 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1447 if (tilesize == gfx.standard_tile_size)
1448 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1449 else if (tilesize == game.tile_size)
1450 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1452 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1455 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1456 boolean get_backside)
1458 struct GraphicInfo *g = &graphic_info[graphic];
1459 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1460 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1462 if (g->offset_y == 0) /* frames are ordered horizontally */
1464 int max_width = g->anim_frames_per_line * g->width;
1465 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1467 *x = pos % max_width;
1468 *y = src_y % g->height + pos / max_width * g->height;
1470 else if (g->offset_x == 0) /* frames are ordered vertically */
1472 int max_height = g->anim_frames_per_line * g->height;
1473 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1475 *x = src_x % g->width + pos / max_height * g->width;
1476 *y = pos % max_height;
1478 else /* frames are ordered diagonally */
1480 *x = src_x + frame * g->offset_x;
1481 *y = src_y + frame * g->offset_y;
1485 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1486 Bitmap **bitmap, int *x, int *y,
1487 boolean get_backside)
1489 struct GraphicInfo *g = &graphic_info[graphic];
1491 // if no in-game graphics defined, always use standard graphic size
1492 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1493 tilesize = TILESIZE;
1495 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1496 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1498 *x = *x * tilesize / g->tile_size;
1499 *y = *y * tilesize / g->tile_size;
1502 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1503 Bitmap **bitmap, int *x, int *y)
1505 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1508 void getFixedGraphicSource(int graphic, int frame,
1509 Bitmap **bitmap, int *x, int *y)
1511 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1514 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1516 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1519 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1520 int *x, int *y, boolean get_backside)
1522 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1526 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1528 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1531 void DrawGraphic(int x, int y, int graphic, int frame)
1534 if (!IN_SCR_FIELD(x, y))
1536 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1537 printf("DrawGraphic(): This should never happen!\n");
1542 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1545 MarkTileDirty(x, y);
1548 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1551 if (!IN_SCR_FIELD(x, y))
1553 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1554 printf("DrawGraphic(): This should never happen!\n");
1559 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1561 MarkTileDirty(x, y);
1564 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1570 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1572 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1575 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1581 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1582 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1585 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1588 if (!IN_SCR_FIELD(x, y))
1590 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1591 printf("DrawGraphicThruMask(): This should never happen!\n");
1596 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1599 MarkTileDirty(x, y);
1602 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1605 if (!IN_SCR_FIELD(x, y))
1607 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1608 printf("DrawGraphicThruMask(): This should never happen!\n");
1613 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1615 MarkTileDirty(x, y);
1618 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1624 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1626 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1630 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1631 int graphic, int frame)
1636 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1638 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1642 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1644 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1646 MarkTileDirty(x / tilesize, y / tilesize);
1649 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1652 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1653 graphic, frame, tilesize);
1654 MarkTileDirty(x / tilesize, y / tilesize);
1657 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1663 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1664 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1667 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1668 int frame, int tilesize)
1673 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1674 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1677 void DrawMiniGraphic(int x, int y, int graphic)
1679 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1680 MarkTileDirty(x / 2, y / 2);
1683 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1688 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1689 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1692 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1693 int graphic, int frame,
1694 int cut_mode, int mask_mode)
1699 int width = TILEX, height = TILEY;
1702 if (dx || dy) /* shifted graphic */
1704 if (x < BX1) /* object enters playfield from the left */
1711 else if (x > BX2) /* object enters playfield from the right */
1717 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1723 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1725 else if (dx) /* general horizontal movement */
1726 MarkTileDirty(x + SIGN(dx), y);
1728 if (y < BY1) /* object enters playfield from the top */
1730 if (cut_mode == CUT_BELOW) /* object completely above top border */
1738 else if (y > BY2) /* object enters playfield from the bottom */
1744 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1750 else if (dy > 0 && cut_mode == CUT_ABOVE)
1752 if (y == BY2) /* object completely above bottom border */
1758 MarkTileDirty(x, y + 1);
1759 } /* object leaves playfield to the bottom */
1760 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1762 else if (dy) /* general vertical movement */
1763 MarkTileDirty(x, y + SIGN(dy));
1767 if (!IN_SCR_FIELD(x, y))
1769 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1770 printf("DrawGraphicShifted(): This should never happen!\n");
1775 width = width * TILESIZE_VAR / TILESIZE;
1776 height = height * TILESIZE_VAR / TILESIZE;
1777 cx = cx * TILESIZE_VAR / TILESIZE;
1778 cy = cy * TILESIZE_VAR / TILESIZE;
1779 dx = dx * TILESIZE_VAR / TILESIZE;
1780 dy = dy * TILESIZE_VAR / TILESIZE;
1782 if (width > 0 && height > 0)
1784 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1789 dst_x = FX + x * TILEX_VAR + dx;
1790 dst_y = FY + y * TILEY_VAR + dy;
1792 if (mask_mode == USE_MASKING)
1793 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1796 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1799 MarkTileDirty(x, y);
1803 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1804 int graphic, int frame,
1805 int cut_mode, int mask_mode)
1810 int width = TILEX_VAR, height = TILEY_VAR;
1813 int x2 = x + SIGN(dx);
1814 int y2 = y + SIGN(dy);
1816 /* movement with two-tile animations must be sync'ed with movement position,
1817 not with current GfxFrame (which can be higher when using slow movement) */
1818 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1819 int anim_frames = graphic_info[graphic].anim_frames;
1821 /* (we also need anim_delay here for movement animations with less frames) */
1822 int anim_delay = graphic_info[graphic].anim_delay;
1823 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1825 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1826 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1828 /* re-calculate animation frame for two-tile movement animation */
1829 frame = getGraphicAnimationFrame(graphic, sync_frame);
1831 /* check if movement start graphic inside screen area and should be drawn */
1832 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1834 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1836 dst_x = FX + x1 * TILEX_VAR;
1837 dst_y = FY + y1 * TILEY_VAR;
1839 if (mask_mode == USE_MASKING)
1840 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1843 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1846 MarkTileDirty(x1, y1);
1849 /* check if movement end graphic inside screen area and should be drawn */
1850 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1852 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1854 dst_x = FX + x2 * TILEX_VAR;
1855 dst_y = FY + y2 * TILEY_VAR;
1857 if (mask_mode == USE_MASKING)
1858 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1861 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1864 MarkTileDirty(x2, y2);
1868 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1869 int graphic, int frame,
1870 int cut_mode, int mask_mode)
1874 DrawGraphic(x, y, graphic, frame);
1879 if (graphic_info[graphic].double_movement) /* EM style movement images */
1880 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1882 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1885 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1886 int frame, int cut_mode)
1888 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1891 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1892 int cut_mode, int mask_mode)
1894 int lx = LEVELX(x), ly = LEVELY(y);
1898 if (IN_LEV_FIELD(lx, ly))
1900 SetRandomAnimationValue(lx, ly);
1902 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1903 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1905 /* do not use double (EM style) movement graphic when not moving */
1906 if (graphic_info[graphic].double_movement && !dx && !dy)
1908 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1909 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1912 else /* border element */
1914 graphic = el2img(element);
1915 frame = getGraphicAnimationFrame(graphic, -1);
1918 if (element == EL_EXPANDABLE_WALL)
1920 boolean left_stopped = FALSE, right_stopped = FALSE;
1922 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1923 left_stopped = TRUE;
1924 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1925 right_stopped = TRUE;
1927 if (left_stopped && right_stopped)
1929 else if (left_stopped)
1931 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1932 frame = graphic_info[graphic].anim_frames - 1;
1934 else if (right_stopped)
1936 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1937 frame = graphic_info[graphic].anim_frames - 1;
1942 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1943 else if (mask_mode == USE_MASKING)
1944 DrawGraphicThruMask(x, y, graphic, frame);
1946 DrawGraphic(x, y, graphic, frame);
1949 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1950 int cut_mode, int mask_mode)
1952 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1953 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1954 cut_mode, mask_mode);
1957 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1960 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1963 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1966 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1969 void DrawLevelElementThruMask(int x, int y, int element)
1971 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1974 void DrawLevelFieldThruMask(int x, int y)
1976 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1979 /* !!! implementation of quicksand is totally broken !!! */
1980 #define IS_CRUMBLED_TILE(x, y, e) \
1981 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1982 !IS_MOVING(x, y) || \
1983 (e) == EL_QUICKSAND_EMPTYING || \
1984 (e) == EL_QUICKSAND_FAST_EMPTYING))
1986 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1991 int width, height, cx, cy;
1992 int sx = SCREENX(x), sy = SCREENY(y);
1993 int crumbled_border_size = graphic_info[graphic].border_size;
1994 int crumbled_tile_size = graphic_info[graphic].tile_size;
1995 int crumbled_border_size_var =
1996 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
1999 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2001 for (i = 1; i < 4; i++)
2003 int dxx = (i & 1 ? dx : 0);
2004 int dyy = (i & 2 ? dy : 0);
2007 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2010 /* check if neighbour field is of same crumble type */
2011 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2012 graphic_info[graphic].class ==
2013 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2015 /* return if check prevents inner corner */
2016 if (same == (dxx == dx && dyy == dy))
2020 /* if we reach this point, we have an inner corner */
2022 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2024 width = crumbled_border_size_var;
2025 height = crumbled_border_size_var;
2026 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2027 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2029 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2030 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2033 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2038 int width, height, bx, by, cx, cy;
2039 int sx = SCREENX(x), sy = SCREENY(y);
2040 int crumbled_border_size = graphic_info[graphic].border_size;
2041 int crumbled_tile_size = graphic_info[graphic].tile_size;
2042 int crumbled_border_size_var =
2043 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2044 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2047 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2049 /* draw simple, sloppy, non-corner-accurate crumbled border */
2051 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2052 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2053 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2054 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2056 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2057 FX + sx * TILEX_VAR + cx,
2058 FY + sy * TILEY_VAR + cy);
2060 /* (remaining middle border part must be at least as big as corner part) */
2061 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2062 crumbled_border_size_var >= TILESIZE_VAR / 3)
2065 /* correct corners of crumbled border, if needed */
2067 for (i = -1; i <= 1; i += 2)
2069 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2070 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2071 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2074 /* check if neighbour field is of same crumble type */
2075 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2076 graphic_info[graphic].class ==
2077 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2079 /* no crumbled corner, but continued crumbled border */
2081 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2082 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2083 int b1 = (i == 1 ? crumbled_border_size_var :
2084 TILESIZE_VAR - 2 * crumbled_border_size_var);
2086 width = crumbled_border_size_var;
2087 height = crumbled_border_size_var;
2089 if (dir == 1 || dir == 2)
2104 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2106 FX + sx * TILEX_VAR + cx,
2107 FY + sy * TILEY_VAR + cy);
2112 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2114 int sx = SCREENX(x), sy = SCREENY(y);
2117 static int xy[4][2] =
2125 if (!IN_LEV_FIELD(x, y))
2128 element = TILE_GFX_ELEMENT(x, y);
2130 if (IS_CRUMBLED_TILE(x, y, element)) /* crumble field itself */
2132 if (!IN_SCR_FIELD(sx, sy))
2135 /* crumble field borders towards direct neighbour fields */
2136 for (i = 0; i < 4; i++)
2138 int xx = x + xy[i][0];
2139 int yy = y + xy[i][1];
2141 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2144 /* check if neighbour field is of same crumble type */
2145 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2146 graphic_info[graphic].class ==
2147 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2150 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2153 /* crumble inner field corners towards corner neighbour fields */
2154 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2155 graphic_info[graphic].anim_frames == 2)
2157 for (i = 0; i < 4; i++)
2159 int dx = (i & 1 ? +1 : -1);
2160 int dy = (i & 2 ? +1 : -1);
2162 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2166 MarkTileDirty(sx, sy);
2168 else /* center field is not crumbled -- crumble neighbour fields */
2170 /* crumble field borders of direct neighbour fields */
2171 for (i = 0; i < 4; i++)
2173 int xx = x + xy[i][0];
2174 int yy = y + xy[i][1];
2175 int sxx = sx + xy[i][0];
2176 int syy = sy + xy[i][1];
2178 if (!IN_LEV_FIELD(xx, yy) ||
2179 !IN_SCR_FIELD(sxx, syy))
2182 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2185 element = TILE_GFX_ELEMENT(xx, yy);
2187 if (!IS_CRUMBLED_TILE(xx, yy, element))
2190 graphic = el_act2crm(element, ACTION_DEFAULT);
2192 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2194 MarkTileDirty(sxx, syy);
2197 /* crumble inner field corners of corner neighbour fields */
2198 for (i = 0; i < 4; i++)
2200 int dx = (i & 1 ? +1 : -1);
2201 int dy = (i & 2 ? +1 : -1);
2207 if (!IN_LEV_FIELD(xx, yy) ||
2208 !IN_SCR_FIELD(sxx, syy))
2211 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2214 element = TILE_GFX_ELEMENT(xx, yy);
2216 if (!IS_CRUMBLED_TILE(xx, yy, element))
2219 graphic = el_act2crm(element, ACTION_DEFAULT);
2221 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2222 graphic_info[graphic].anim_frames == 2)
2223 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2225 MarkTileDirty(sxx, syy);
2230 void DrawLevelFieldCrumbled(int x, int y)
2234 if (!IN_LEV_FIELD(x, y))
2237 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2238 GfxElement[x][y] != EL_UNDEFINED &&
2239 GFX_CRUMBLED(GfxElement[x][y]))
2241 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2246 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2248 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2251 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2254 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2255 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2256 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2257 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2258 int sx = SCREENX(x), sy = SCREENY(y);
2260 DrawGraphic(sx, sy, graphic1, frame1);
2261 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2264 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2266 int sx = SCREENX(x), sy = SCREENY(y);
2267 static int xy[4][2] =
2276 /* crumble direct neighbour fields (required for field borders) */
2277 for (i = 0; i < 4; i++)
2279 int xx = x + xy[i][0];
2280 int yy = y + xy[i][1];
2281 int sxx = sx + xy[i][0];
2282 int syy = sy + xy[i][1];
2284 if (!IN_LEV_FIELD(xx, yy) ||
2285 !IN_SCR_FIELD(sxx, syy) ||
2286 !GFX_CRUMBLED(Feld[xx][yy]) ||
2290 DrawLevelField(xx, yy);
2293 /* crumble corner neighbour fields (required for inner field corners) */
2294 for (i = 0; i < 4; i++)
2296 int dx = (i & 1 ? +1 : -1);
2297 int dy = (i & 2 ? +1 : -1);
2303 if (!IN_LEV_FIELD(xx, yy) ||
2304 !IN_SCR_FIELD(sxx, syy) ||
2305 !GFX_CRUMBLED(Feld[xx][yy]) ||
2309 int element = TILE_GFX_ELEMENT(xx, yy);
2310 int graphic = el_act2crm(element, ACTION_DEFAULT);
2312 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2313 graphic_info[graphic].anim_frames == 2)
2314 DrawLevelField(xx, yy);
2318 static int getBorderElement(int x, int y)
2322 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2323 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2324 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2325 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2326 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2327 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2328 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2330 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2331 int steel_position = (x == -1 && y == -1 ? 0 :
2332 x == lev_fieldx && y == -1 ? 1 :
2333 x == -1 && y == lev_fieldy ? 2 :
2334 x == lev_fieldx && y == lev_fieldy ? 3 :
2335 x == -1 || x == lev_fieldx ? 4 :
2336 y == -1 || y == lev_fieldy ? 5 : 6);
2338 return border[steel_position][steel_type];
2341 void DrawScreenElement(int x, int y, int element)
2343 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2344 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2347 void DrawLevelElement(int x, int y, int element)
2349 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2350 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2353 void DrawScreenField(int x, int y)
2355 int lx = LEVELX(x), ly = LEVELY(y);
2356 int element, content;
2358 if (!IN_LEV_FIELD(lx, ly))
2360 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2363 element = getBorderElement(lx, ly);
2365 DrawScreenElement(x, y, element);
2370 element = Feld[lx][ly];
2371 content = Store[lx][ly];
2373 if (IS_MOVING(lx, ly))
2375 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2376 boolean cut_mode = NO_CUTTING;
2378 if (element == EL_QUICKSAND_EMPTYING ||
2379 element == EL_QUICKSAND_FAST_EMPTYING ||
2380 element == EL_MAGIC_WALL_EMPTYING ||
2381 element == EL_BD_MAGIC_WALL_EMPTYING ||
2382 element == EL_DC_MAGIC_WALL_EMPTYING ||
2383 element == EL_AMOEBA_DROPPING)
2384 cut_mode = CUT_ABOVE;
2385 else if (element == EL_QUICKSAND_FILLING ||
2386 element == EL_QUICKSAND_FAST_FILLING ||
2387 element == EL_MAGIC_WALL_FILLING ||
2388 element == EL_BD_MAGIC_WALL_FILLING ||
2389 element == EL_DC_MAGIC_WALL_FILLING)
2390 cut_mode = CUT_BELOW;
2392 if (cut_mode == CUT_ABOVE)
2393 DrawScreenElement(x, y, element);
2395 DrawScreenElement(x, y, EL_EMPTY);
2398 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2399 else if (cut_mode == NO_CUTTING)
2400 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2403 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2405 if (cut_mode == CUT_BELOW &&
2406 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2407 DrawLevelElement(lx, ly + 1, element);
2410 if (content == EL_ACID)
2412 int dir = MovDir[lx][ly];
2413 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2414 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2416 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2418 // prevent target field from being drawn again (but without masking)
2419 // (this would happen if target field is scanned after moving element)
2420 Stop[newlx][newly] = TRUE;
2423 else if (IS_BLOCKED(lx, ly))
2428 boolean cut_mode = NO_CUTTING;
2429 int element_old, content_old;
2431 Blocked2Moving(lx, ly, &oldx, &oldy);
2434 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2435 MovDir[oldx][oldy] == MV_RIGHT);
2437 element_old = Feld[oldx][oldy];
2438 content_old = Store[oldx][oldy];
2440 if (element_old == EL_QUICKSAND_EMPTYING ||
2441 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2442 element_old == EL_MAGIC_WALL_EMPTYING ||
2443 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2444 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2445 element_old == EL_AMOEBA_DROPPING)
2446 cut_mode = CUT_ABOVE;
2448 DrawScreenElement(x, y, EL_EMPTY);
2451 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2453 else if (cut_mode == NO_CUTTING)
2454 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2457 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2460 else if (IS_DRAWABLE(element))
2461 DrawScreenElement(x, y, element);
2463 DrawScreenElement(x, y, EL_EMPTY);
2466 void DrawLevelField(int x, int y)
2468 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2469 DrawScreenField(SCREENX(x), SCREENY(y));
2470 else if (IS_MOVING(x, y))
2474 Moving2Blocked(x, y, &newx, &newy);
2475 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2476 DrawScreenField(SCREENX(newx), SCREENY(newy));
2478 else if (IS_BLOCKED(x, y))
2482 Blocked2Moving(x, y, &oldx, &oldy);
2483 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2484 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2488 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2489 int (*el2img_function)(int), boolean masked,
2490 int element_bits_draw)
2492 int element_base = map_mm_wall_element(element);
2493 int element_bits = (IS_DF_WALL(element) ?
2494 element - EL_DF_WALL_START :
2495 IS_MM_WALL(element) ?
2496 element - EL_MM_WALL_START : EL_EMPTY) & 0x000f;
2497 int graphic = el2img_function(element_base);
2498 int tilesize_draw = tilesize / 2;
2503 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2505 for (i = 0; i < 4; i++)
2507 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2508 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2510 if (!(element_bits_draw & (1 << i)))
2513 if (element_bits & (1 << i))
2516 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2517 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2519 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2520 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2525 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2526 tilesize_draw, tilesize_draw);
2531 void DrawSizedWallParts_MM(int x, int y, int element, int tilesize,
2532 boolean masked, int element_bits_draw)
2534 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2535 element, tilesize, el2edimg, masked, element_bits_draw);
2538 void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2539 int (*el2img_function)(int))
2541 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE,
2545 void DrawSizedElementExt(int x, int y, int element, int tilesize,
2548 if (IS_MM_WALL(element))
2550 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2551 element, tilesize, el2edimg, masked, 0x000f);
2555 int graphic = el2edimg(element);
2558 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2560 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2564 void DrawSizedElement(int x, int y, int element, int tilesize)
2566 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2569 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2571 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2574 void DrawMiniElement(int x, int y, int element)
2578 graphic = el2edimg(element);
2579 DrawMiniGraphic(x, y, graphic);
2582 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2585 int x = sx + scroll_x, y = sy + scroll_y;
2587 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2588 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2589 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2590 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2592 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2595 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2597 int x = sx + scroll_x, y = sy + scroll_y;
2599 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2600 DrawMiniElement(sx, sy, EL_EMPTY);
2601 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2602 DrawMiniElement(sx, sy, Feld[x][y]);
2604 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2607 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2608 int x, int y, int xsize, int ysize,
2609 int tile_width, int tile_height)
2613 int dst_x = startx + x * tile_width;
2614 int dst_y = starty + y * tile_height;
2615 int width = graphic_info[graphic].width;
2616 int height = graphic_info[graphic].height;
2617 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2618 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2619 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2620 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2621 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2622 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2623 boolean draw_masked = graphic_info[graphic].draw_masked;
2625 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2627 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2629 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2633 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2634 inner_sx + (x - 1) * tile_width % inner_width);
2635 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2636 inner_sy + (y - 1) * tile_height % inner_height);
2639 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2642 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2646 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2647 int x, int y, int xsize, int ysize, int font_nr)
2649 int font_width = getFontWidth(font_nr);
2650 int font_height = getFontHeight(font_nr);
2652 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2653 font_width, font_height);
2656 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2658 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2659 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2660 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2661 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2662 boolean no_delay = (tape.warp_forward);
2663 unsigned int anim_delay = 0;
2664 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2665 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2666 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2667 int font_width = getFontWidth(font_nr);
2668 int font_height = getFontHeight(font_nr);
2669 int max_xsize = level.envelope[envelope_nr].xsize;
2670 int max_ysize = level.envelope[envelope_nr].ysize;
2671 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2672 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2673 int xend = max_xsize;
2674 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2675 int xstep = (xstart < xend ? 1 : 0);
2676 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2678 int end = MAX(xend - xstart, yend - ystart);
2681 for (i = start; i <= end; i++)
2683 int last_frame = end; // last frame of this "for" loop
2684 int x = xstart + i * xstep;
2685 int y = ystart + i * ystep;
2686 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2687 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2688 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2689 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2692 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2694 BlitScreenToBitmap(backbuffer);
2696 SetDrawtoField(DRAW_TO_BACKBUFFER);
2698 for (yy = 0; yy < ysize; yy++)
2699 for (xx = 0; xx < xsize; xx++)
2700 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2702 DrawTextBuffer(sx + font_width, sy + font_height,
2703 level.envelope[envelope_nr].text, font_nr, max_xsize,
2704 xsize - 2, ysize - 2, 0, mask_mode,
2705 level.envelope[envelope_nr].autowrap,
2706 level.envelope[envelope_nr].centered, FALSE);
2708 redraw_mask |= REDRAW_FIELD;
2711 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2715 void ShowEnvelope(int envelope_nr)
2717 int element = EL_ENVELOPE_1 + envelope_nr;
2718 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2719 int sound_opening = element_info[element].sound[ACTION_OPENING];
2720 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2721 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2722 boolean no_delay = (tape.warp_forward);
2723 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2724 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2725 int anim_mode = graphic_info[graphic].anim_mode;
2726 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2727 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2729 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2731 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2733 if (anim_mode == ANIM_DEFAULT)
2734 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2736 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2739 Delay(wait_delay_value);
2741 WaitForEventToContinue();
2743 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2745 if (anim_mode != ANIM_NONE)
2746 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2748 if (anim_mode == ANIM_DEFAULT)
2749 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2751 game.envelope_active = FALSE;
2753 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2755 redraw_mask |= REDRAW_FIELD;
2759 static void setRequestBasePosition(int *x, int *y)
2761 int sx_base, sy_base;
2763 if (request.x != -1)
2764 sx_base = request.x;
2765 else if (request.align == ALIGN_LEFT)
2767 else if (request.align == ALIGN_RIGHT)
2768 sx_base = SX + SXSIZE;
2770 sx_base = SX + SXSIZE / 2;
2772 if (request.y != -1)
2773 sy_base = request.y;
2774 else if (request.valign == VALIGN_TOP)
2776 else if (request.valign == VALIGN_BOTTOM)
2777 sy_base = SY + SYSIZE;
2779 sy_base = SY + SYSIZE / 2;
2785 static void setRequestPositionExt(int *x, int *y, int width, int height,
2786 boolean add_border_size)
2788 int border_size = request.border_size;
2789 int sx_base, sy_base;
2792 setRequestBasePosition(&sx_base, &sy_base);
2794 if (request.align == ALIGN_LEFT)
2796 else if (request.align == ALIGN_RIGHT)
2797 sx = sx_base - width;
2799 sx = sx_base - width / 2;
2801 if (request.valign == VALIGN_TOP)
2803 else if (request.valign == VALIGN_BOTTOM)
2804 sy = sy_base - height;
2806 sy = sy_base - height / 2;
2808 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2809 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2811 if (add_border_size)
2821 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2823 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2826 void DrawEnvelopeRequest(char *text)
2828 char *text_final = text;
2829 char *text_door_style = NULL;
2830 int graphic = IMG_BACKGROUND_REQUEST;
2831 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2832 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2833 int font_nr = FONT_REQUEST;
2834 int font_width = getFontWidth(font_nr);
2835 int font_height = getFontHeight(font_nr);
2836 int border_size = request.border_size;
2837 int line_spacing = request.line_spacing;
2838 int line_height = font_height + line_spacing;
2839 int max_text_width = request.width - 2 * border_size;
2840 int max_text_height = request.height - 2 * border_size;
2841 int line_length = max_text_width / font_width;
2842 int max_lines = max_text_height / line_height;
2843 int text_width = line_length * font_width;
2844 int width = request.width;
2845 int height = request.height;
2846 int tile_size = MAX(request.step_offset, 1);
2847 int x_steps = width / tile_size;
2848 int y_steps = height / tile_size;
2849 int sx_offset = border_size;
2850 int sy_offset = border_size;
2854 if (request.centered)
2855 sx_offset = (request.width - text_width) / 2;
2857 if (request.wrap_single_words && !request.autowrap)
2859 char *src_text_ptr, *dst_text_ptr;
2861 text_door_style = checked_malloc(2 * strlen(text) + 1);
2863 src_text_ptr = text;
2864 dst_text_ptr = text_door_style;
2866 while (*src_text_ptr)
2868 if (*src_text_ptr == ' ' ||
2869 *src_text_ptr == '?' ||
2870 *src_text_ptr == '!')
2871 *dst_text_ptr++ = '\n';
2873 if (*src_text_ptr != ' ')
2874 *dst_text_ptr++ = *src_text_ptr;
2879 *dst_text_ptr = '\0';
2881 text_final = text_door_style;
2884 setRequestPosition(&sx, &sy, FALSE);
2886 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2888 for (y = 0; y < y_steps; y++)
2889 for (x = 0; x < x_steps; x++)
2890 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2891 x, y, x_steps, y_steps,
2892 tile_size, tile_size);
2894 /* force DOOR font inside door area */
2895 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2897 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2898 line_length, -1, max_lines, line_spacing, mask_mode,
2899 request.autowrap, request.centered, FALSE);
2903 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2904 RedrawGadget(tool_gadget[i]);
2906 // store readily prepared envelope request for later use when animating
2907 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2909 if (text_door_style)
2910 free(text_door_style);
2913 void AnimateEnvelopeRequest(int anim_mode, int action)
2915 int graphic = IMG_BACKGROUND_REQUEST;
2916 boolean draw_masked = graphic_info[graphic].draw_masked;
2917 int delay_value_normal = request.step_delay;
2918 int delay_value_fast = delay_value_normal / 2;
2919 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2920 boolean no_delay = (tape.warp_forward);
2921 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2922 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
2923 unsigned int anim_delay = 0;
2925 int tile_size = MAX(request.step_offset, 1);
2926 int max_xsize = request.width / tile_size;
2927 int max_ysize = request.height / tile_size;
2928 int max_xsize_inner = max_xsize - 2;
2929 int max_ysize_inner = max_ysize - 2;
2931 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2932 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2933 int xend = max_xsize_inner;
2934 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2935 int xstep = (xstart < xend ? 1 : 0);
2936 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2938 int end = MAX(xend - xstart, yend - ystart);
2941 if (setup.quick_doors)
2948 for (i = start; i <= end; i++)
2950 int last_frame = end; // last frame of this "for" loop
2951 int x = xstart + i * xstep;
2952 int y = ystart + i * ystep;
2953 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2954 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2955 int xsize_size_left = (xsize - 1) * tile_size;
2956 int ysize_size_top = (ysize - 1) * tile_size;
2957 int max_xsize_pos = (max_xsize - 1) * tile_size;
2958 int max_ysize_pos = (max_ysize - 1) * tile_size;
2959 int width = xsize * tile_size;
2960 int height = ysize * tile_size;
2965 setRequestPosition(&src_x, &src_y, FALSE);
2966 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2968 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2970 for (yy = 0; yy < 2; yy++)
2972 for (xx = 0; xx < 2; xx++)
2974 int src_xx = src_x + xx * max_xsize_pos;
2975 int src_yy = src_y + yy * max_ysize_pos;
2976 int dst_xx = dst_x + xx * xsize_size_left;
2977 int dst_yy = dst_y + yy * ysize_size_top;
2978 int xx_size = (xx ? tile_size : xsize_size_left);
2979 int yy_size = (yy ? tile_size : ysize_size_top);
2982 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
2983 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2985 BlitBitmap(bitmap_db_store_2, backbuffer,
2986 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2990 redraw_mask |= REDRAW_FIELD;
2994 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2998 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
3000 int graphic = IMG_BACKGROUND_REQUEST;
3001 int sound_opening = SND_REQUEST_OPENING;
3002 int sound_closing = SND_REQUEST_CLOSING;
3003 int anim_mode_1 = request.anim_mode; /* (higher priority) */
3004 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
3005 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
3006 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
3007 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
3009 if (game_status == GAME_MODE_PLAYING)
3010 BlitScreenToBitmap(backbuffer);
3012 SetDrawtoField(DRAW_TO_BACKBUFFER);
3014 // SetDrawBackgroundMask(REDRAW_NONE);
3016 if (action == ACTION_OPENING)
3018 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3020 if (req_state & REQ_ASK)
3022 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3023 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3025 else if (req_state & REQ_CONFIRM)
3027 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3029 else if (req_state & REQ_PLAYER)
3031 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3032 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3033 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3034 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3037 DrawEnvelopeRequest(text);
3040 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
3042 if (action == ACTION_OPENING)
3044 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3046 if (anim_mode == ANIM_DEFAULT)
3047 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3049 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3053 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3055 if (anim_mode != ANIM_NONE)
3056 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3058 if (anim_mode == ANIM_DEFAULT)
3059 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3062 game.envelope_active = FALSE;
3064 if (action == ACTION_CLOSING)
3065 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3067 // SetDrawBackgroundMask(last_draw_background_mask);
3069 redraw_mask |= REDRAW_FIELD;
3073 if (action == ACTION_CLOSING &&
3074 game_status == GAME_MODE_PLAYING &&
3075 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3076 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3079 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3081 if (IS_MM_WALL(element))
3083 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3089 int graphic = el2preimg(element);
3091 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3092 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3097 void DrawLevel(int draw_background_mask)
3101 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3102 SetDrawBackgroundMask(draw_background_mask);
3106 for (x = BX1; x <= BX2; x++)
3107 for (y = BY1; y <= BY2; y++)
3108 DrawScreenField(x, y);
3110 redraw_mask |= REDRAW_FIELD;
3113 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3118 for (x = 0; x < size_x; x++)
3119 for (y = 0; y < size_y; y++)
3120 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3122 redraw_mask |= REDRAW_FIELD;
3125 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3129 for (x = 0; x < size_x; x++)
3130 for (y = 0; y < size_y; y++)
3131 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3133 redraw_mask |= REDRAW_FIELD;
3136 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3138 boolean show_level_border = (BorderElement != EL_EMPTY);
3139 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3140 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3141 int tile_size = preview.tile_size;
3142 int preview_width = preview.xsize * tile_size;
3143 int preview_height = preview.ysize * tile_size;
3144 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3145 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3146 int real_preview_width = real_preview_xsize * tile_size;
3147 int real_preview_height = real_preview_ysize * tile_size;
3148 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3149 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3152 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3155 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3157 dst_x += (preview_width - real_preview_width) / 2;
3158 dst_y += (preview_height - real_preview_height) / 2;
3160 for (x = 0; x < real_preview_xsize; x++)
3162 for (y = 0; y < real_preview_ysize; y++)
3164 int lx = from_x + x + (show_level_border ? -1 : 0);
3165 int ly = from_y + y + (show_level_border ? -1 : 0);
3166 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3167 getBorderElement(lx, ly));
3169 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3170 element, tile_size);
3174 redraw_mask |= REDRAW_FIELD;
3177 #define MICROLABEL_EMPTY 0
3178 #define MICROLABEL_LEVEL_NAME 1
3179 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3180 #define MICROLABEL_LEVEL_AUTHOR 3
3181 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3182 #define MICROLABEL_IMPORTED_FROM 5
3183 #define MICROLABEL_IMPORTED_BY_HEAD 6
3184 #define MICROLABEL_IMPORTED_BY 7
3186 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3188 int max_text_width = SXSIZE;
3189 int font_width = getFontWidth(font_nr);
3191 if (pos->align == ALIGN_CENTER)
3192 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3193 else if (pos->align == ALIGN_RIGHT)
3194 max_text_width = pos->x;
3196 max_text_width = SXSIZE - pos->x;
3198 return max_text_width / font_width;
3201 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3203 char label_text[MAX_OUTPUT_LINESIZE + 1];
3204 int max_len_label_text;
3205 int font_nr = pos->font;
3208 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3211 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3212 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3213 mode == MICROLABEL_IMPORTED_BY_HEAD)
3214 font_nr = pos->font_alt;
3216 max_len_label_text = getMaxTextLength(pos, font_nr);
3218 if (pos->size != -1)
3219 max_len_label_text = pos->size;
3221 for (i = 0; i < max_len_label_text; i++)
3222 label_text[i] = ' ';
3223 label_text[max_len_label_text] = '\0';
3225 if (strlen(label_text) > 0)
3226 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3229 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3230 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3231 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3232 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3233 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3234 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3235 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3236 max_len_label_text);
3237 label_text[max_len_label_text] = '\0';
3239 if (strlen(label_text) > 0)
3240 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3242 redraw_mask |= REDRAW_FIELD;
3245 static void DrawPreviewLevelLabel(int mode)
3247 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3250 static void DrawPreviewLevelInfo(int mode)
3252 if (mode == MICROLABEL_LEVEL_NAME)
3253 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3254 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3255 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3258 static void DrawPreviewLevelExt(boolean restart)
3260 static unsigned int scroll_delay = 0;
3261 static unsigned int label_delay = 0;
3262 static int from_x, from_y, scroll_direction;
3263 static int label_state, label_counter;
3264 unsigned int scroll_delay_value = preview.step_delay;
3265 boolean show_level_border = (BorderElement != EL_EMPTY);
3266 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3267 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3274 if (preview.anim_mode == ANIM_CENTERED)
3276 if (level_xsize > preview.xsize)
3277 from_x = (level_xsize - preview.xsize) / 2;
3278 if (level_ysize > preview.ysize)
3279 from_y = (level_ysize - preview.ysize) / 2;
3282 from_x += preview.xoffset;
3283 from_y += preview.yoffset;
3285 scroll_direction = MV_RIGHT;
3289 DrawPreviewLevelPlayfield(from_x, from_y);
3290 DrawPreviewLevelLabel(label_state);
3292 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3293 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3295 /* initialize delay counters */
3296 DelayReached(&scroll_delay, 0);
3297 DelayReached(&label_delay, 0);
3299 if (leveldir_current->name)
3301 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3302 char label_text[MAX_OUTPUT_LINESIZE + 1];
3303 int font_nr = pos->font;
3304 int max_len_label_text = getMaxTextLength(pos, font_nr);
3306 if (pos->size != -1)
3307 max_len_label_text = pos->size;
3309 strncpy(label_text, leveldir_current->name, max_len_label_text);
3310 label_text[max_len_label_text] = '\0';
3312 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3313 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3319 /* scroll preview level, if needed */
3320 if (preview.anim_mode != ANIM_NONE &&
3321 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3322 DelayReached(&scroll_delay, scroll_delay_value))
3324 switch (scroll_direction)
3329 from_x -= preview.step_offset;
3330 from_x = (from_x < 0 ? 0 : from_x);
3333 scroll_direction = MV_UP;
3337 if (from_x < level_xsize - preview.xsize)
3339 from_x += preview.step_offset;
3340 from_x = (from_x > level_xsize - preview.xsize ?
3341 level_xsize - preview.xsize : from_x);
3344 scroll_direction = MV_DOWN;
3350 from_y -= preview.step_offset;
3351 from_y = (from_y < 0 ? 0 : from_y);
3354 scroll_direction = MV_RIGHT;
3358 if (from_y < level_ysize - preview.ysize)
3360 from_y += preview.step_offset;
3361 from_y = (from_y > level_ysize - preview.ysize ?
3362 level_ysize - preview.ysize : from_y);
3365 scroll_direction = MV_LEFT;
3372 DrawPreviewLevelPlayfield(from_x, from_y);
3375 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
3376 /* redraw micro level label, if needed */
3377 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3378 !strEqual(level.author, ANONYMOUS_NAME) &&
3379 !strEqual(level.author, leveldir_current->name) &&
3380 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3382 int max_label_counter = 23;
3384 if (leveldir_current->imported_from != NULL &&
3385 strlen(leveldir_current->imported_from) > 0)
3386 max_label_counter += 14;
3387 if (leveldir_current->imported_by != NULL &&
3388 strlen(leveldir_current->imported_by) > 0)
3389 max_label_counter += 14;
3391 label_counter = (label_counter + 1) % max_label_counter;
3392 label_state = (label_counter >= 0 && label_counter <= 7 ?
3393 MICROLABEL_LEVEL_NAME :
3394 label_counter >= 9 && label_counter <= 12 ?
3395 MICROLABEL_LEVEL_AUTHOR_HEAD :
3396 label_counter >= 14 && label_counter <= 21 ?
3397 MICROLABEL_LEVEL_AUTHOR :
3398 label_counter >= 23 && label_counter <= 26 ?
3399 MICROLABEL_IMPORTED_FROM_HEAD :
3400 label_counter >= 28 && label_counter <= 35 ?
3401 MICROLABEL_IMPORTED_FROM :
3402 label_counter >= 37 && label_counter <= 40 ?
3403 MICROLABEL_IMPORTED_BY_HEAD :
3404 label_counter >= 42 && label_counter <= 49 ?
3405 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3407 if (leveldir_current->imported_from == NULL &&
3408 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3409 label_state == MICROLABEL_IMPORTED_FROM))
3410 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3411 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3413 DrawPreviewLevelLabel(label_state);
3417 void DrawPreviewLevelInitial()
3419 DrawPreviewLevelExt(TRUE);
3422 void DrawPreviewLevelAnimation()
3424 DrawPreviewLevelExt(FALSE);
3427 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3428 int graphic, int sync_frame,
3431 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3433 if (mask_mode == USE_MASKING)
3434 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3436 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3439 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3440 int graphic, int sync_frame, int mask_mode)
3442 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3444 if (mask_mode == USE_MASKING)
3445 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3447 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3450 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3452 int lx = LEVELX(x), ly = LEVELY(y);
3454 if (!IN_SCR_FIELD(x, y))
3457 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3458 graphic, GfxFrame[lx][ly], NO_MASKING);
3460 MarkTileDirty(x, y);
3463 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3465 int lx = LEVELX(x), ly = LEVELY(y);
3467 if (!IN_SCR_FIELD(x, y))
3470 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3471 graphic, GfxFrame[lx][ly], NO_MASKING);
3472 MarkTileDirty(x, y);
3475 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3477 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3480 void DrawLevelElementAnimation(int x, int y, int element)
3482 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3484 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3487 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3489 int sx = SCREENX(x), sy = SCREENY(y);
3491 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3494 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3497 DrawGraphicAnimation(sx, sy, graphic);
3500 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3501 DrawLevelFieldCrumbled(x, y);
3503 if (GFX_CRUMBLED(Feld[x][y]))
3504 DrawLevelFieldCrumbled(x, y);
3508 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3510 int sx = SCREENX(x), sy = SCREENY(y);
3513 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3516 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3518 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3521 DrawGraphicAnimation(sx, sy, graphic);
3523 if (GFX_CRUMBLED(element))
3524 DrawLevelFieldCrumbled(x, y);
3527 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3529 if (player->use_murphy)
3531 /* this works only because currently only one player can be "murphy" ... */
3532 static int last_horizontal_dir = MV_LEFT;
3533 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3535 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3536 last_horizontal_dir = move_dir;
3538 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3540 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3542 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3548 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3551 static boolean equalGraphics(int graphic1, int graphic2)
3553 struct GraphicInfo *g1 = &graphic_info[graphic1];
3554 struct GraphicInfo *g2 = &graphic_info[graphic2];
3556 return (g1->bitmap == g2->bitmap &&
3557 g1->src_x == g2->src_x &&
3558 g1->src_y == g2->src_y &&
3559 g1->anim_frames == g2->anim_frames &&
3560 g1->anim_delay == g2->anim_delay &&
3561 g1->anim_mode == g2->anim_mode);
3564 void DrawAllPlayers()
3568 for (i = 0; i < MAX_PLAYERS; i++)
3569 if (stored_player[i].active)
3570 DrawPlayer(&stored_player[i]);
3573 void DrawPlayerField(int x, int y)
3575 if (!IS_PLAYER(x, y))
3578 DrawPlayer(PLAYERINFO(x, y));
3581 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3583 void DrawPlayer(struct PlayerInfo *player)
3585 int jx = player->jx;
3586 int jy = player->jy;
3587 int move_dir = player->MovDir;
3588 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3589 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3590 int last_jx = (player->is_moving ? jx - dx : jx);
3591 int last_jy = (player->is_moving ? jy - dy : jy);
3592 int next_jx = jx + dx;
3593 int next_jy = jy + dy;
3594 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3595 boolean player_is_opaque = FALSE;
3596 int sx = SCREENX(jx), sy = SCREENY(jy);
3597 int sxx = 0, syy = 0;
3598 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3600 int action = ACTION_DEFAULT;
3601 int last_player_graphic = getPlayerGraphic(player, move_dir);
3602 int last_player_frame = player->Frame;
3605 /* GfxElement[][] is set to the element the player is digging or collecting;
3606 remove also for off-screen player if the player is not moving anymore */
3607 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3608 GfxElement[jx][jy] = EL_UNDEFINED;
3610 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3614 if (!IN_LEV_FIELD(jx, jy))
3616 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3617 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3618 printf("DrawPlayerField(): This should never happen!\n");
3623 if (element == EL_EXPLOSION)
3626 action = (player->is_pushing ? ACTION_PUSHING :
3627 player->is_digging ? ACTION_DIGGING :
3628 player->is_collecting ? ACTION_COLLECTING :
3629 player->is_moving ? ACTION_MOVING :
3630 player->is_snapping ? ACTION_SNAPPING :
3631 player->is_dropping ? ACTION_DROPPING :
3632 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3634 if (player->is_waiting)
3635 move_dir = player->dir_waiting;
3637 InitPlayerGfxAnimation(player, action, move_dir);
3639 /* ----------------------------------------------------------------------- */
3640 /* draw things in the field the player is leaving, if needed */
3641 /* ----------------------------------------------------------------------- */
3643 if (player->is_moving)
3645 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3647 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3649 if (last_element == EL_DYNAMITE_ACTIVE ||
3650 last_element == EL_EM_DYNAMITE_ACTIVE ||
3651 last_element == EL_SP_DISK_RED_ACTIVE)
3652 DrawDynamite(last_jx, last_jy);
3654 DrawLevelFieldThruMask(last_jx, last_jy);
3656 else if (last_element == EL_DYNAMITE_ACTIVE ||
3657 last_element == EL_EM_DYNAMITE_ACTIVE ||
3658 last_element == EL_SP_DISK_RED_ACTIVE)
3659 DrawDynamite(last_jx, last_jy);
3661 /* !!! this is not enough to prevent flickering of players which are
3662 moving next to each others without a free tile between them -- this
3663 can only be solved by drawing all players layer by layer (first the
3664 background, then the foreground etc.) !!! => TODO */
3665 else if (!IS_PLAYER(last_jx, last_jy))
3666 DrawLevelField(last_jx, last_jy);
3669 DrawLevelField(last_jx, last_jy);
3672 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3673 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3676 if (!IN_SCR_FIELD(sx, sy))
3679 /* ----------------------------------------------------------------------- */
3680 /* draw things behind the player, if needed */
3681 /* ----------------------------------------------------------------------- */
3684 DrawLevelElement(jx, jy, Back[jx][jy]);
3685 else if (IS_ACTIVE_BOMB(element))
3686 DrawLevelElement(jx, jy, EL_EMPTY);
3689 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3691 int old_element = GfxElement[jx][jy];
3692 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3693 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3695 if (GFX_CRUMBLED(old_element))
3696 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3698 DrawGraphic(sx, sy, old_graphic, frame);
3700 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3701 player_is_opaque = TRUE;
3705 GfxElement[jx][jy] = EL_UNDEFINED;
3707 /* make sure that pushed elements are drawn with correct frame rate */
3708 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3710 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3711 GfxFrame[jx][jy] = player->StepFrame;
3713 DrawLevelField(jx, jy);
3717 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3718 /* ----------------------------------------------------------------------- */
3719 /* draw player himself */
3720 /* ----------------------------------------------------------------------- */
3722 graphic = getPlayerGraphic(player, move_dir);
3724 /* in the case of changed player action or direction, prevent the current
3725 animation frame from being restarted for identical animations */
3726 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3727 player->Frame = last_player_frame;
3729 frame = getGraphicAnimationFrame(graphic, player->Frame);
3733 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3734 sxx = player->GfxPos;
3736 syy = player->GfxPos;
3739 if (player_is_opaque)
3740 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3742 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3744 if (SHIELD_ON(player))
3746 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3747 IMG_SHIELD_NORMAL_ACTIVE);
3748 int frame = getGraphicAnimationFrame(graphic, -1);
3750 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3754 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3757 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3758 sxx = player->GfxPos;
3760 syy = player->GfxPos;
3764 /* ----------------------------------------------------------------------- */
3765 /* draw things the player is pushing, if needed */
3766 /* ----------------------------------------------------------------------- */
3768 if (player->is_pushing && player->is_moving)
3770 int px = SCREENX(jx), py = SCREENY(jy);
3771 int pxx = (TILEX - ABS(sxx)) * dx;
3772 int pyy = (TILEY - ABS(syy)) * dy;
3773 int gfx_frame = GfxFrame[jx][jy];
3779 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3781 element = Feld[next_jx][next_jy];
3782 gfx_frame = GfxFrame[next_jx][next_jy];
3785 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3787 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3788 frame = getGraphicAnimationFrame(graphic, sync_frame);
3790 /* draw background element under pushed element (like the Sokoban field) */
3791 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3793 /* this allows transparent pushing animation over non-black background */
3796 DrawLevelElement(jx, jy, Back[jx][jy]);
3798 DrawLevelElement(jx, jy, EL_EMPTY);
3800 if (Back[next_jx][next_jy])
3801 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3803 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3805 else if (Back[next_jx][next_jy])
3806 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3809 /* do not draw (EM style) pushing animation when pushing is finished */
3810 /* (two-tile animations usually do not contain start and end frame) */
3811 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3812 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3814 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3816 /* masked drawing is needed for EMC style (double) movement graphics */
3817 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3818 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3822 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3823 /* ----------------------------------------------------------------------- */
3824 /* draw player himself */
3825 /* ----------------------------------------------------------------------- */
3827 graphic = getPlayerGraphic(player, move_dir);
3829 /* in the case of changed player action or direction, prevent the current
3830 animation frame from being restarted for identical animations */
3831 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3832 player->Frame = last_player_frame;
3834 frame = getGraphicAnimationFrame(graphic, player->Frame);
3838 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3839 sxx = player->GfxPos;
3841 syy = player->GfxPos;
3844 if (player_is_opaque)
3845 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3847 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3849 if (SHIELD_ON(player))
3851 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3852 IMG_SHIELD_NORMAL_ACTIVE);
3853 int frame = getGraphicAnimationFrame(graphic, -1);
3855 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3859 /* ----------------------------------------------------------------------- */
3860 /* draw things in front of player (active dynamite or dynabombs) */
3861 /* ----------------------------------------------------------------------- */
3863 if (IS_ACTIVE_BOMB(element))
3865 graphic = el2img(element);
3866 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3868 if (game.emulation == EMU_SUPAPLEX)
3869 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3871 DrawGraphicThruMask(sx, sy, graphic, frame);
3874 if (player_is_moving && last_element == EL_EXPLOSION)
3876 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3877 GfxElement[last_jx][last_jy] : EL_EMPTY);
3878 int graphic = el_act2img(element, ACTION_EXPLODING);
3879 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3880 int phase = ExplodePhase[last_jx][last_jy] - 1;
3881 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3884 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3887 /* ----------------------------------------------------------------------- */
3888 /* draw elements the player is just walking/passing through/under */
3889 /* ----------------------------------------------------------------------- */
3891 if (player_is_moving)
3893 /* handle the field the player is leaving ... */
3894 if (IS_ACCESSIBLE_INSIDE(last_element))
3895 DrawLevelField(last_jx, last_jy);
3896 else if (IS_ACCESSIBLE_UNDER(last_element))
3897 DrawLevelFieldThruMask(last_jx, last_jy);
3900 /* do not redraw accessible elements if the player is just pushing them */
3901 if (!player_is_moving || !player->is_pushing)
3903 /* ... and the field the player is entering */
3904 if (IS_ACCESSIBLE_INSIDE(element))
3905 DrawLevelField(jx, jy);
3906 else if (IS_ACCESSIBLE_UNDER(element))
3907 DrawLevelFieldThruMask(jx, jy);
3910 MarkTileDirty(sx, sy);
3913 /* ------------------------------------------------------------------------- */
3915 void WaitForEventToContinue()
3917 boolean still_wait = TRUE;
3919 if (program.headless)
3922 /* simulate releasing mouse button over last gadget, if still pressed */
3924 HandleGadgets(-1, -1, 0);
3926 button_status = MB_RELEASED;
3934 if (NextValidEvent(&event))
3938 case EVENT_BUTTONPRESS:
3939 case EVENT_KEYPRESS:
3940 #if defined(TARGET_SDL2)
3941 case SDL_CONTROLLERBUTTONDOWN:
3943 case SDL_JOYBUTTONDOWN:
3947 case EVENT_KEYRELEASE:
3948 ClearPlayerAction();
3952 HandleOtherEvents(&event);
3956 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3965 #define MAX_REQUEST_LINES 13
3966 #define MAX_REQUEST_LINE_FONT1_LEN 7
3967 #define MAX_REQUEST_LINE_FONT2_LEN 10
3969 static int RequestHandleEvents(unsigned int req_state)
3971 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3972 local_player->LevelSolved_GameEnd);
3973 int width = request.width;
3974 int height = request.height;
3978 setRequestPosition(&sx, &sy, FALSE);
3980 button_status = MB_RELEASED;
3982 request_gadget_id = -1;
3989 /* the MM game engine does not use a special (scrollable) field buffer */
3990 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
3991 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3993 HandleGameActions();
3995 SetDrawtoField(DRAW_TO_BACKBUFFER);
3997 if (global.use_envelope_request)
3999 /* copy current state of request area to middle of playfield area */
4000 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
4008 while (NextValidEvent(&event))
4012 case EVENT_BUTTONPRESS:
4013 case EVENT_BUTTONRELEASE:
4014 case EVENT_MOTIONNOTIFY:
4018 if (event.type == EVENT_MOTIONNOTIFY)
4023 motion_status = TRUE;
4024 mx = ((MotionEvent *) &event)->x;
4025 my = ((MotionEvent *) &event)->y;
4029 motion_status = FALSE;
4030 mx = ((ButtonEvent *) &event)->x;
4031 my = ((ButtonEvent *) &event)->y;
4032 if (event.type == EVENT_BUTTONPRESS)
4033 button_status = ((ButtonEvent *) &event)->button;
4035 button_status = MB_RELEASED;
4038 /* this sets 'request_gadget_id' */
4039 HandleGadgets(mx, my, button_status);
4041 switch (request_gadget_id)
4043 case TOOL_CTRL_ID_YES:
4046 case TOOL_CTRL_ID_NO:
4049 case TOOL_CTRL_ID_CONFIRM:
4050 result = TRUE | FALSE;
4053 case TOOL_CTRL_ID_PLAYER_1:
4056 case TOOL_CTRL_ID_PLAYER_2:
4059 case TOOL_CTRL_ID_PLAYER_3:
4062 case TOOL_CTRL_ID_PLAYER_4:
4073 #if defined(TARGET_SDL2)
4074 case SDL_WINDOWEVENT:
4075 HandleWindowEvent((WindowEvent *) &event);
4078 case SDL_APP_WILLENTERBACKGROUND:
4079 case SDL_APP_DIDENTERBACKGROUND:
4080 case SDL_APP_WILLENTERFOREGROUND:
4081 case SDL_APP_DIDENTERFOREGROUND:
4082 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4086 case EVENT_KEYPRESS:
4088 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4093 if (req_state & REQ_CONFIRM)
4098 #if defined(TARGET_SDL2)
4101 #if defined(KSYM_Rewind)
4102 case KSYM_Rewind: /* for Amazon Fire TV remote */
4109 #if defined(TARGET_SDL2)
4111 #if defined(KSYM_FastForward)
4112 case KSYM_FastForward: /* for Amazon Fire TV remote */
4119 HandleKeysDebug(key);
4123 if (req_state & REQ_PLAYER)
4129 case EVENT_KEYRELEASE:
4130 ClearPlayerAction();
4133 #if defined(TARGET_SDL2)
4134 case SDL_CONTROLLERBUTTONDOWN:
4135 switch (event.cbutton.button)
4137 case SDL_CONTROLLER_BUTTON_A:
4138 case SDL_CONTROLLER_BUTTON_X:
4139 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4143 case SDL_CONTROLLER_BUTTON_B:
4144 case SDL_CONTROLLER_BUTTON_Y:
4145 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4146 case SDL_CONTROLLER_BUTTON_BACK:
4151 if (req_state & REQ_PLAYER)
4156 case SDL_CONTROLLERBUTTONUP:
4157 HandleJoystickEvent(&event);
4158 ClearPlayerAction();
4163 HandleOtherEvents(&event);
4168 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4170 int joy = AnyJoystick();
4172 if (joy & JOY_BUTTON_1)
4174 else if (joy & JOY_BUTTON_2)
4180 if (global.use_envelope_request)
4182 /* copy back current state of pressed buttons inside request area */
4183 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4193 static boolean RequestDoor(char *text, unsigned int req_state)
4195 unsigned int old_door_state;
4196 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4197 int font_nr = FONT_TEXT_2;
4202 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4204 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4205 font_nr = FONT_TEXT_1;
4208 if (game_status == GAME_MODE_PLAYING)
4209 BlitScreenToBitmap(backbuffer);
4211 /* disable deactivated drawing when quick-loading level tape recording */
4212 if (tape.playing && tape.deactivate_display)
4213 TapeDeactivateDisplayOff(TRUE);
4215 SetMouseCursor(CURSOR_DEFAULT);
4217 #if defined(NETWORK_AVALIABLE)
4218 /* pause network game while waiting for request to answer */
4219 if (options.network &&
4220 game_status == GAME_MODE_PLAYING &&
4221 req_state & REQUEST_WAIT_FOR_INPUT)
4222 SendToServer_PausePlaying();
4225 old_door_state = GetDoorState();
4227 /* simulate releasing mouse button over last gadget, if still pressed */
4229 HandleGadgets(-1, -1, 0);
4233 /* draw released gadget before proceeding */
4236 if (old_door_state & DOOR_OPEN_1)
4238 CloseDoor(DOOR_CLOSE_1);
4240 /* save old door content */
4241 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4242 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4245 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4246 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4248 /* clear door drawing field */
4249 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4251 /* force DOOR font inside door area */
4252 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4254 /* write text for request */
4255 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4257 char text_line[max_request_line_len + 1];
4263 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4265 tc = *(text_ptr + tx);
4266 // if (!tc || tc == ' ')
4267 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4271 if ((tc == '?' || tc == '!') && tl == 0)
4281 strncpy(text_line, text_ptr, tl);
4284 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4285 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4286 text_line, font_nr);
4288 text_ptr += tl + (tc == ' ' ? 1 : 0);
4289 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4294 if (req_state & REQ_ASK)
4296 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4297 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4299 else if (req_state & REQ_CONFIRM)
4301 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4303 else if (req_state & REQ_PLAYER)
4305 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4306 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4307 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4308 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4311 /* copy request gadgets to door backbuffer */
4312 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4314 OpenDoor(DOOR_OPEN_1);
4316 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4318 if (game_status == GAME_MODE_PLAYING)
4320 SetPanelBackground();
4321 SetDrawBackgroundMask(REDRAW_DOOR_1);
4325 SetDrawBackgroundMask(REDRAW_FIELD);
4331 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4333 // ---------- handle request buttons ----------
4334 result = RequestHandleEvents(req_state);
4338 if (!(req_state & REQ_STAY_OPEN))
4340 CloseDoor(DOOR_CLOSE_1);
4342 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4343 (req_state & REQ_REOPEN))
4344 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4349 if (game_status == GAME_MODE_PLAYING)
4351 SetPanelBackground();
4352 SetDrawBackgroundMask(REDRAW_DOOR_1);
4356 SetDrawBackgroundMask(REDRAW_FIELD);
4359 #if defined(NETWORK_AVALIABLE)
4360 /* continue network game after request */
4361 if (options.network &&
4362 game_status == GAME_MODE_PLAYING &&
4363 req_state & REQUEST_WAIT_FOR_INPUT)
4364 SendToServer_ContinuePlaying();
4367 /* restore deactivated drawing when quick-loading level tape recording */
4368 if (tape.playing && tape.deactivate_display)
4369 TapeDeactivateDisplayOn();
4374 static boolean RequestEnvelope(char *text, unsigned int req_state)
4378 if (game_status == GAME_MODE_PLAYING)
4379 BlitScreenToBitmap(backbuffer);
4381 /* disable deactivated drawing when quick-loading level tape recording */
4382 if (tape.playing && tape.deactivate_display)
4383 TapeDeactivateDisplayOff(TRUE);
4385 SetMouseCursor(CURSOR_DEFAULT);
4387 #if defined(NETWORK_AVALIABLE)
4388 /* pause network game while waiting for request to answer */
4389 if (options.network &&
4390 game_status == GAME_MODE_PLAYING &&
4391 req_state & REQUEST_WAIT_FOR_INPUT)
4392 SendToServer_PausePlaying();
4395 /* simulate releasing mouse button over last gadget, if still pressed */
4397 HandleGadgets(-1, -1, 0);
4401 // (replace with setting corresponding request background)
4402 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4403 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4405 /* clear door drawing field */
4406 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4408 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4410 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4412 if (game_status == GAME_MODE_PLAYING)
4414 SetPanelBackground();
4415 SetDrawBackgroundMask(REDRAW_DOOR_1);
4419 SetDrawBackgroundMask(REDRAW_FIELD);
4425 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4427 // ---------- handle request buttons ----------
4428 result = RequestHandleEvents(req_state);
4432 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4436 if (game_status == GAME_MODE_PLAYING)
4438 SetPanelBackground();
4439 SetDrawBackgroundMask(REDRAW_DOOR_1);
4443 SetDrawBackgroundMask(REDRAW_FIELD);
4446 #if defined(NETWORK_AVALIABLE)
4447 /* continue network game after request */
4448 if (options.network &&
4449 game_status == GAME_MODE_PLAYING &&
4450 req_state & REQUEST_WAIT_FOR_INPUT)
4451 SendToServer_ContinuePlaying();
4454 /* restore deactivated drawing when quick-loading level tape recording */
4455 if (tape.playing && tape.deactivate_display)
4456 TapeDeactivateDisplayOn();
4461 boolean Request(char *text, unsigned int req_state)
4463 boolean overlay_active = GetOverlayActive();
4466 SetOverlayActive(FALSE);
4468 if (global.use_envelope_request)
4469 result = RequestEnvelope(text, req_state);
4471 result = RequestDoor(text, req_state);
4473 SetOverlayActive(overlay_active);
4478 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4480 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4481 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4484 if (dpo1->sort_priority != dpo2->sort_priority)
4485 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4487 compare_result = dpo1->nr - dpo2->nr;
4489 return compare_result;
4492 void InitGraphicCompatibilityInfo_Doors()
4498 struct DoorInfo *door;
4502 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4503 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4505 { -1, -1, -1, NULL }
4507 struct Rect door_rect_list[] =
4509 { DX, DY, DXSIZE, DYSIZE },
4510 { VX, VY, VXSIZE, VYSIZE }
4514 for (i = 0; doors[i].door_token != -1; i++)
4516 int door_token = doors[i].door_token;
4517 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4518 int part_1 = doors[i].part_1;
4519 int part_8 = doors[i].part_8;
4520 int part_2 = part_1 + 1;
4521 int part_3 = part_1 + 2;
4522 struct DoorInfo *door = doors[i].door;
4523 struct Rect *door_rect = &door_rect_list[door_index];
4524 boolean door_gfx_redefined = FALSE;
4526 /* check if any door part graphic definitions have been redefined */
4528 for (j = 0; door_part_controls[j].door_token != -1; j++)
4530 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4531 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4533 if (dpc->door_token == door_token && fi->redefined)
4534 door_gfx_redefined = TRUE;
4537 /* check for old-style door graphic/animation modifications */
4539 if (!door_gfx_redefined)
4541 if (door->anim_mode & ANIM_STATIC_PANEL)
4543 door->panel.step_xoffset = 0;
4544 door->panel.step_yoffset = 0;
4547 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4549 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4550 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4551 int num_door_steps, num_panel_steps;
4553 /* remove door part graphics other than the two default wings */
4555 for (j = 0; door_part_controls[j].door_token != -1; j++)
4557 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4558 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4560 if (dpc->graphic >= part_3 &&
4561 dpc->graphic <= part_8)
4565 /* set graphics and screen positions of the default wings */
4567 g_part_1->width = door_rect->width;
4568 g_part_1->height = door_rect->height;
4569 g_part_2->width = door_rect->width;
4570 g_part_2->height = door_rect->height;
4571 g_part_2->src_x = door_rect->width;
4572 g_part_2->src_y = g_part_1->src_y;
4574 door->part_2.x = door->part_1.x;
4575 door->part_2.y = door->part_1.y;
4577 if (door->width != -1)
4579 g_part_1->width = door->width;
4580 g_part_2->width = door->width;
4582 // special treatment for graphics and screen position of right wing
4583 g_part_2->src_x += door_rect->width - door->width;
4584 door->part_2.x += door_rect->width - door->width;
4587 if (door->height != -1)
4589 g_part_1->height = door->height;
4590 g_part_2->height = door->height;
4592 // special treatment for graphics and screen position of bottom wing
4593 g_part_2->src_y += door_rect->height - door->height;
4594 door->part_2.y += door_rect->height - door->height;
4597 /* set animation delays for the default wings and panels */
4599 door->part_1.step_delay = door->step_delay;
4600 door->part_2.step_delay = door->step_delay;
4601 door->panel.step_delay = door->step_delay;
4603 /* set animation draw order for the default wings */
4605 door->part_1.sort_priority = 2; /* draw left wing over ... */
4606 door->part_2.sort_priority = 1; /* ... right wing */
4608 /* set animation draw offset for the default wings */
4610 if (door->anim_mode & ANIM_HORIZONTAL)
4612 door->part_1.step_xoffset = door->step_offset;
4613 door->part_1.step_yoffset = 0;
4614 door->part_2.step_xoffset = door->step_offset * -1;
4615 door->part_2.step_yoffset = 0;
4617 num_door_steps = g_part_1->width / door->step_offset;
4619 else // ANIM_VERTICAL
4621 door->part_1.step_xoffset = 0;
4622 door->part_1.step_yoffset = door->step_offset;
4623 door->part_2.step_xoffset = 0;
4624 door->part_2.step_yoffset = door->step_offset * -1;
4626 num_door_steps = g_part_1->height / door->step_offset;
4629 /* set animation draw offset for the default panels */
4631 if (door->step_offset > 1)
4633 num_panel_steps = 2 * door_rect->height / door->step_offset;
4634 door->panel.start_step = num_panel_steps - num_door_steps;
4635 door->panel.start_step_closing = door->panel.start_step;
4639 num_panel_steps = door_rect->height / door->step_offset;
4640 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4641 door->panel.start_step_closing = door->panel.start_step;
4642 door->panel.step_delay *= 2;
4653 for (i = 0; door_part_controls[i].door_token != -1; i++)
4655 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4656 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4658 /* initialize "start_step_opening" and "start_step_closing", if needed */
4659 if (dpc->pos->start_step_opening == 0 &&
4660 dpc->pos->start_step_closing == 0)
4662 // dpc->pos->start_step_opening = dpc->pos->start_step;
4663 dpc->pos->start_step_closing = dpc->pos->start_step;
4666 /* fill structure for door part draw order (sorted below) */
4668 dpo->sort_priority = dpc->pos->sort_priority;
4671 /* sort door part controls according to sort_priority and graphic number */
4672 qsort(door_part_order, MAX_DOOR_PARTS,
4673 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4676 unsigned int OpenDoor(unsigned int door_state)
4678 if (door_state & DOOR_COPY_BACK)
4680 if (door_state & DOOR_OPEN_1)
4681 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4682 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4684 if (door_state & DOOR_OPEN_2)
4685 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4686 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4688 door_state &= ~DOOR_COPY_BACK;
4691 return MoveDoor(door_state);
4694 unsigned int CloseDoor(unsigned int door_state)
4696 unsigned int old_door_state = GetDoorState();
4698 if (!(door_state & DOOR_NO_COPY_BACK))
4700 if (old_door_state & DOOR_OPEN_1)
4701 BlitBitmap(backbuffer, bitmap_db_door_1,
4702 DX, DY, DXSIZE, DYSIZE, 0, 0);
4704 if (old_door_state & DOOR_OPEN_2)
4705 BlitBitmap(backbuffer, bitmap_db_door_2,
4706 VX, VY, VXSIZE, VYSIZE, 0, 0);
4708 door_state &= ~DOOR_NO_COPY_BACK;
4711 return MoveDoor(door_state);
4714 unsigned int GetDoorState()
4716 return MoveDoor(DOOR_GET_STATE);
4719 unsigned int SetDoorState(unsigned int door_state)
4721 return MoveDoor(door_state | DOOR_SET_STATE);
4724 int euclid(int a, int b)
4726 return (b ? euclid(b, a % b) : a);
4729 unsigned int MoveDoor(unsigned int door_state)
4731 struct Rect door_rect_list[] =
4733 { DX, DY, DXSIZE, DYSIZE },
4734 { VX, VY, VXSIZE, VYSIZE }
4736 static int door1 = DOOR_CLOSE_1;
4737 static int door2 = DOOR_CLOSE_2;
4738 unsigned int door_delay = 0;
4739 unsigned int door_delay_value;
4742 if (door_state == DOOR_GET_STATE)
4743 return (door1 | door2);
4745 if (door_state & DOOR_SET_STATE)
4747 if (door_state & DOOR_ACTION_1)
4748 door1 = door_state & DOOR_ACTION_1;
4749 if (door_state & DOOR_ACTION_2)
4750 door2 = door_state & DOOR_ACTION_2;
4752 return (door1 | door2);
4755 if (!(door_state & DOOR_FORCE_REDRAW))
4757 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4758 door_state &= ~DOOR_OPEN_1;
4759 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4760 door_state &= ~DOOR_CLOSE_1;
4761 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4762 door_state &= ~DOOR_OPEN_2;
4763 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4764 door_state &= ~DOOR_CLOSE_2;
4767 if (global.autoplay_leveldir)
4769 door_state |= DOOR_NO_DELAY;
4770 door_state &= ~DOOR_CLOSE_ALL;
4773 if (game_status == GAME_MODE_EDITOR && !(door_state & DOOR_FORCE_ANIM))
4774 door_state |= DOOR_NO_DELAY;
4776 if (door_state & DOOR_ACTION)
4778 boolean door_panel_drawn[NUM_DOORS];
4779 boolean panel_has_doors[NUM_DOORS];
4780 boolean door_part_skip[MAX_DOOR_PARTS];
4781 boolean door_part_done[MAX_DOOR_PARTS];
4782 boolean door_part_done_all;
4783 int num_steps[MAX_DOOR_PARTS];
4784 int max_move_delay = 0; // delay for complete animations of all doors
4785 int max_step_delay = 0; // delay (ms) between two animation frames
4786 int num_move_steps = 0; // number of animation steps for all doors
4787 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4788 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4789 int current_move_delay = 0;
4793 for (i = 0; i < NUM_DOORS; i++)
4794 panel_has_doors[i] = FALSE;
4796 for (i = 0; i < MAX_DOOR_PARTS; i++)
4798 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4799 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4800 int door_token = dpc->door_token;
4802 door_part_done[i] = FALSE;
4803 door_part_skip[i] = (!(door_state & door_token) ||
4807 for (i = 0; i < MAX_DOOR_PARTS; i++)
4809 int nr = door_part_order[i].nr;
4810 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4811 struct DoorPartPosInfo *pos = dpc->pos;
4812 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4813 int door_token = dpc->door_token;
4814 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4815 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4816 int step_xoffset = ABS(pos->step_xoffset);
4817 int step_yoffset = ABS(pos->step_yoffset);
4818 int step_delay = pos->step_delay;
4819 int current_door_state = door_state & door_token;
4820 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4821 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4822 boolean part_opening = (is_panel ? door_closing : door_opening);
4823 int start_step = (part_opening ? pos->start_step_opening :
4824 pos->start_step_closing);
4825 float move_xsize = (step_xoffset ? g->width : 0);
4826 float move_ysize = (step_yoffset ? g->height : 0);
4827 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4828 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4829 int move_steps = (move_xsteps && move_ysteps ?
4830 MIN(move_xsteps, move_ysteps) :
4831 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4832 int move_delay = move_steps * step_delay;
4834 if (door_part_skip[nr])
4837 max_move_delay = MAX(max_move_delay, move_delay);
4838 max_step_delay = (max_step_delay == 0 ? step_delay :
4839 euclid(max_step_delay, step_delay));
4840 num_steps[nr] = move_steps;
4844 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4846 panel_has_doors[door_index] = TRUE;
4850 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4852 num_move_steps = max_move_delay / max_step_delay;
4853 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4855 door_delay_value = max_step_delay;
4857 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4859 start = num_move_steps - 1;
4863 /* opening door sound has priority over simultaneously closing door */
4864 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4866 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4868 if (door_state & DOOR_OPEN_1)
4869 PlayMenuSoundStereo(SND_DOOR_1_OPENING, SOUND_MIDDLE);
4870 if (door_state & DOOR_OPEN_2)
4871 PlayMenuSoundStereo(SND_DOOR_2_OPENING, SOUND_MIDDLE);
4873 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4875 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4877 if (door_state & DOOR_CLOSE_1)
4878 PlayMenuSoundStereo(SND_DOOR_1_CLOSING, SOUND_MIDDLE);
4879 if (door_state & DOOR_CLOSE_2)
4880 PlayMenuSoundStereo(SND_DOOR_2_CLOSING, SOUND_MIDDLE);
4884 for (k = start; k < num_move_steps; k++)
4886 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4888 door_part_done_all = TRUE;
4890 for (i = 0; i < NUM_DOORS; i++)
4891 door_panel_drawn[i] = FALSE;
4893 for (i = 0; i < MAX_DOOR_PARTS; i++)
4895 int nr = door_part_order[i].nr;
4896 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4897 struct DoorPartPosInfo *pos = dpc->pos;
4898 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4899 int door_token = dpc->door_token;
4900 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4901 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4902 boolean is_panel_and_door_has_closed = FALSE;
4903 struct Rect *door_rect = &door_rect_list[door_index];
4904 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4906 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4907 int current_door_state = door_state & door_token;
4908 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4909 boolean door_closing = !door_opening;
4910 boolean part_opening = (is_panel ? door_closing : door_opening);
4911 boolean part_closing = !part_opening;
4912 int start_step = (part_opening ? pos->start_step_opening :
4913 pos->start_step_closing);
4914 int step_delay = pos->step_delay;
4915 int step_factor = step_delay / max_step_delay;
4916 int k1 = (step_factor ? k / step_factor + 1 : k);
4917 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4918 int kk = MAX(0, k2);
4921 int src_x, src_y, src_xx, src_yy;
4922 int dst_x, dst_y, dst_xx, dst_yy;
4925 if (door_part_skip[nr])
4928 if (!(door_state & door_token))
4936 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4937 int kk_door = MAX(0, k2_door);
4938 int sync_frame = kk_door * door_delay_value;
4939 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4941 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
4942 &g_src_x, &g_src_y);
4947 if (!door_panel_drawn[door_index])
4949 ClearRectangle(drawto, door_rect->x, door_rect->y,
4950 door_rect->width, door_rect->height);
4952 door_panel_drawn[door_index] = TRUE;
4955 // draw opening or closing door parts
4957 if (pos->step_xoffset < 0) // door part on right side
4960 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4963 if (dst_xx + width > door_rect->width)
4964 width = door_rect->width - dst_xx;
4966 else // door part on left side
4969 dst_xx = pos->x - kk * pos->step_xoffset;
4973 src_xx = ABS(dst_xx);
4977 width = g->width - src_xx;
4979 if (width > door_rect->width)
4980 width = door_rect->width;
4982 // printf("::: k == %d [%d] \n", k, start_step);
4985 if (pos->step_yoffset < 0) // door part on bottom side
4988 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4991 if (dst_yy + height > door_rect->height)
4992 height = door_rect->height - dst_yy;
4994 else // door part on top side
4997 dst_yy = pos->y - kk * pos->step_yoffset;
5001 src_yy = ABS(dst_yy);
5005 height = g->height - src_yy;
5008 src_x = g_src_x + src_xx;
5009 src_y = g_src_y + src_yy;
5011 dst_x = door_rect->x + dst_xx;
5012 dst_y = door_rect->y + dst_yy;
5014 is_panel_and_door_has_closed =
5017 panel_has_doors[door_index] &&
5018 k >= num_move_steps_doors_only - 1);
5020 if (width >= 0 && width <= g->width &&
5021 height >= 0 && height <= g->height &&
5022 !is_panel_and_door_has_closed)
5024 if (is_panel || !pos->draw_masked)
5025 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5028 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5032 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5034 if ((part_opening && (width < 0 || height < 0)) ||
5035 (part_closing && (width >= g->width && height >= g->height)))
5036 door_part_done[nr] = TRUE;
5038 // continue door part animations, but not panel after door has closed
5039 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5040 door_part_done_all = FALSE;
5043 if (!(door_state & DOOR_NO_DELAY))
5047 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5049 current_move_delay += max_step_delay;
5051 /* prevent OS (Windows) from complaining about program not responding */
5055 if (door_part_done_all)
5059 if (!(door_state & DOOR_NO_DELAY))
5061 /* wait for specified door action post delay */
5062 if (door_state & DOOR_ACTION_1 && door_state & DOOR_ACTION_2)
5063 Delay(MAX(door_1.post_delay, door_2.post_delay));
5064 else if (door_state & DOOR_ACTION_1)
5065 Delay(door_1.post_delay);
5066 else if (door_state & DOOR_ACTION_2)
5067 Delay(door_2.post_delay);
5071 if (door_state & DOOR_ACTION_1)
5072 door1 = door_state & DOOR_ACTION_1;
5073 if (door_state & DOOR_ACTION_2)
5074 door2 = door_state & DOOR_ACTION_2;
5076 // draw masked border over door area
5077 DrawMaskedBorder(REDRAW_DOOR_1);
5078 DrawMaskedBorder(REDRAW_DOOR_2);
5080 return (door1 | door2);
5083 static boolean useSpecialEditorDoor()
5085 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5086 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5088 // do not draw special editor door if editor border defined or redefined
5089 if (graphic_info[graphic].bitmap != NULL || redefined)
5092 // do not draw special editor door if global border defined to be empty
5093 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5096 // do not draw special editor door if viewport definitions do not match
5100 EY + EYSIZE != VY + VYSIZE)
5106 void DrawSpecialEditorDoor()
5108 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5109 int top_border_width = gfx1->width;
5110 int top_border_height = gfx1->height;
5111 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5112 int ex = EX - outer_border;
5113 int ey = EY - outer_border;
5114 int vy = VY - outer_border;
5115 int exsize = EXSIZE + 2 * outer_border;
5117 if (!useSpecialEditorDoor())
5120 /* draw bigger level editor toolbox window */
5121 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5122 top_border_width, top_border_height, ex, ey - top_border_height);
5123 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5124 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5126 redraw_mask |= REDRAW_ALL;
5129 void UndrawSpecialEditorDoor()
5131 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5132 int top_border_width = gfx1->width;
5133 int top_border_height = gfx1->height;
5134 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5135 int ex = EX - outer_border;
5136 int ey = EY - outer_border;
5137 int ey_top = ey - top_border_height;
5138 int exsize = EXSIZE + 2 * outer_border;
5139 int eysize = EYSIZE + 2 * outer_border;
5141 if (!useSpecialEditorDoor())
5144 /* draw normal tape recorder window */
5145 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5147 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5148 ex, ey_top, top_border_width, top_border_height,
5150 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5151 ex, ey, exsize, eysize, ex, ey);
5155 // if screen background is set to "[NONE]", clear editor toolbox window
5156 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5157 ClearRectangle(drawto, ex, ey, exsize, eysize);
5160 redraw_mask |= REDRAW_ALL;
5164 /* ---------- new tool button stuff ---------------------------------------- */
5169 struct TextPosInfo *pos;
5172 } toolbutton_info[NUM_TOOL_BUTTONS] =
5175 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5176 TOOL_CTRL_ID_YES, "yes"
5179 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5180 TOOL_CTRL_ID_NO, "no"
5183 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5184 TOOL_CTRL_ID_CONFIRM, "confirm"
5187 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5188 TOOL_CTRL_ID_PLAYER_1, "player 1"
5191 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5192 TOOL_CTRL_ID_PLAYER_2, "player 2"
5195 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5196 TOOL_CTRL_ID_PLAYER_3, "player 3"
5199 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5200 TOOL_CTRL_ID_PLAYER_4, "player 4"
5204 void CreateToolButtons()
5208 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5210 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
5211 struct TextPosInfo *pos = toolbutton_info[i].pos;
5212 struct GadgetInfo *gi;
5213 Bitmap *deco_bitmap = None;
5214 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5215 unsigned int event_mask = GD_EVENT_RELEASED;
5218 int gd_x = gfx->src_x;
5219 int gd_y = gfx->src_y;
5220 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5221 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5224 if (global.use_envelope_request)
5225 setRequestPosition(&dx, &dy, TRUE);
5227 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5229 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5231 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5232 pos->size, &deco_bitmap, &deco_x, &deco_y);
5233 deco_xpos = (gfx->width - pos->size) / 2;
5234 deco_ypos = (gfx->height - pos->size) / 2;
5237 gi = CreateGadget(GDI_CUSTOM_ID, id,
5238 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5239 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
5240 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
5241 GDI_WIDTH, gfx->width,
5242 GDI_HEIGHT, gfx->height,
5243 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5244 GDI_STATE, GD_BUTTON_UNPRESSED,
5245 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5246 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5247 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5248 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5249 GDI_DECORATION_SIZE, pos->size, pos->size,
5250 GDI_DECORATION_SHIFTING, 1, 1,
5251 GDI_DIRECT_DRAW, FALSE,
5252 GDI_EVENT_MASK, event_mask,
5253 GDI_CALLBACK_ACTION, HandleToolButtons,
5257 Error(ERR_EXIT, "cannot create gadget");
5259 tool_gadget[id] = gi;
5263 void FreeToolButtons()
5267 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5268 FreeGadget(tool_gadget[i]);
5271 static void UnmapToolButtons()
5275 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5276 UnmapGadget(tool_gadget[i]);
5279 static void HandleToolButtons(struct GadgetInfo *gi)
5281 request_gadget_id = gi->custom_id;
5284 static struct Mapping_EM_to_RND_object
5287 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
5288 boolean is_backside; /* backside of moving element */
5294 em_object_mapping_list[] =
5297 Xblank, TRUE, FALSE,
5301 Yacid_splash_eB, FALSE, FALSE,
5302 EL_ACID_SPLASH_RIGHT, -1, -1
5305 Yacid_splash_wB, FALSE, FALSE,
5306 EL_ACID_SPLASH_LEFT, -1, -1
5309 #ifdef EM_ENGINE_BAD_ROLL
5311 Xstone_force_e, FALSE, FALSE,
5312 EL_ROCK, -1, MV_BIT_RIGHT
5315 Xstone_force_w, FALSE, FALSE,
5316 EL_ROCK, -1, MV_BIT_LEFT
5319 Xnut_force_e, FALSE, FALSE,
5320 EL_NUT, -1, MV_BIT_RIGHT
5323 Xnut_force_w, FALSE, FALSE,
5324 EL_NUT, -1, MV_BIT_LEFT
5327 Xspring_force_e, FALSE, FALSE,
5328 EL_SPRING, -1, MV_BIT_RIGHT
5331 Xspring_force_w, FALSE, FALSE,
5332 EL_SPRING, -1, MV_BIT_LEFT
5335 Xemerald_force_e, FALSE, FALSE,
5336 EL_EMERALD, -1, MV_BIT_RIGHT
5339 Xemerald_force_w, FALSE, FALSE,
5340 EL_EMERALD, -1, MV_BIT_LEFT
5343 Xdiamond_force_e, FALSE, FALSE,
5344 EL_DIAMOND, -1, MV_BIT_RIGHT
5347 Xdiamond_force_w, FALSE, FALSE,
5348 EL_DIAMOND, -1, MV_BIT_LEFT
5351 Xbomb_force_e, FALSE, FALSE,
5352 EL_BOMB, -1, MV_BIT_RIGHT
5355 Xbomb_force_w, FALSE, FALSE,
5356 EL_BOMB, -1, MV_BIT_LEFT
5358 #endif /* EM_ENGINE_BAD_ROLL */
5361 Xstone, TRUE, FALSE,
5365 Xstone_pause, FALSE, FALSE,
5369 Xstone_fall, FALSE, FALSE,
5373 Ystone_s, FALSE, FALSE,
5374 EL_ROCK, ACTION_FALLING, -1
5377 Ystone_sB, FALSE, TRUE,
5378 EL_ROCK, ACTION_FALLING, -1
5381 Ystone_e, FALSE, FALSE,
5382 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5385 Ystone_eB, FALSE, TRUE,
5386 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5389 Ystone_w, FALSE, FALSE,
5390 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5393 Ystone_wB, FALSE, TRUE,
5394 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5401 Xnut_pause, FALSE, FALSE,
5405 Xnut_fall, FALSE, FALSE,
5409 Ynut_s, FALSE, FALSE,
5410 EL_NUT, ACTION_FALLING, -1
5413 Ynut_sB, FALSE, TRUE,
5414 EL_NUT, ACTION_FALLING, -1
5417 Ynut_e, FALSE, FALSE,
5418 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5421 Ynut_eB, FALSE, TRUE,
5422 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5425 Ynut_w, FALSE, FALSE,
5426 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5429 Ynut_wB, FALSE, TRUE,
5430 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5433 Xbug_n, TRUE, FALSE,
5437 Xbug_e, TRUE, FALSE,
5438 EL_BUG_RIGHT, -1, -1
5441 Xbug_s, TRUE, FALSE,
5445 Xbug_w, TRUE, FALSE,
5449 Xbug_gon, FALSE, FALSE,
5453 Xbug_goe, FALSE, FALSE,
5454 EL_BUG_RIGHT, -1, -1
5457 Xbug_gos, FALSE, FALSE,
5461 Xbug_gow, FALSE, FALSE,
5465 Ybug_n, FALSE, FALSE,
5466 EL_BUG, ACTION_MOVING, MV_BIT_UP
5469 Ybug_nB, FALSE, TRUE,
5470 EL_BUG, ACTION_MOVING, MV_BIT_UP
5473 Ybug_e, FALSE, FALSE,
5474 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5477 Ybug_eB, FALSE, TRUE,
5478 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5481 Ybug_s, FALSE, FALSE,
5482 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5485 Ybug_sB, FALSE, TRUE,
5486 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5489 Ybug_w, FALSE, FALSE,
5490 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5493 Ybug_wB, FALSE, TRUE,
5494 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5497 Ybug_w_n, FALSE, FALSE,
5498 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5501 Ybug_n_e, FALSE, FALSE,
5502 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5505 Ybug_e_s, FALSE, FALSE,
5506 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5509 Ybug_s_w, FALSE, FALSE,
5510 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5513 Ybug_e_n, FALSE, FALSE,
5514 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5517 Ybug_s_e, FALSE, FALSE,
5518 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5521 Ybug_w_s, FALSE, FALSE,
5522 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5525 Ybug_n_w, FALSE, FALSE,
5526 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5529 Ybug_stone, FALSE, FALSE,
5530 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5533 Ybug_spring, FALSE, FALSE,
5534 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5537 Xtank_n, TRUE, FALSE,
5538 EL_SPACESHIP_UP, -1, -1
5541 Xtank_e, TRUE, FALSE,
5542 EL_SPACESHIP_RIGHT, -1, -1
5545 Xtank_s, TRUE, FALSE,
5546 EL_SPACESHIP_DOWN, -1, -1
5549 Xtank_w, TRUE, FALSE,
5550 EL_SPACESHIP_LEFT, -1, -1
5553 Xtank_gon, FALSE, FALSE,
5554 EL_SPACESHIP_UP, -1, -1
5557 Xtank_goe, FALSE, FALSE,
5558 EL_SPACESHIP_RIGHT, -1, -1
5561 Xtank_gos, FALSE, FALSE,
5562 EL_SPACESHIP_DOWN, -1, -1
5565 Xtank_gow, FALSE, FALSE,
5566 EL_SPACESHIP_LEFT, -1, -1
5569 Ytank_n, FALSE, FALSE,
5570 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5573 Ytank_nB, FALSE, TRUE,
5574 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5577 Ytank_e, FALSE, FALSE,
5578 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5581 Ytank_eB, FALSE, TRUE,
5582 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5585 Ytank_s, FALSE, FALSE,
5586 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5589 Ytank_sB, FALSE, TRUE,
5590 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5593 Ytank_w, FALSE, FALSE,
5594 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5597 Ytank_wB, FALSE, TRUE,
5598 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5601 Ytank_w_n, FALSE, FALSE,
5602 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5605 Ytank_n_e, FALSE, FALSE,
5606 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5609 Ytank_e_s, FALSE, FALSE,
5610 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5613 Ytank_s_w, FALSE, FALSE,
5614 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5617 Ytank_e_n, FALSE, FALSE,
5618 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5621 Ytank_s_e, FALSE, FALSE,
5622 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5625 Ytank_w_s, FALSE, FALSE,
5626 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5629 Ytank_n_w, FALSE, FALSE,
5630 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5633 Ytank_stone, FALSE, FALSE,
5634 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5637 Ytank_spring, FALSE, FALSE,
5638 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5641 Xandroid, TRUE, FALSE,
5642 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5645 Xandroid_1_n, FALSE, FALSE,
5646 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5649 Xandroid_2_n, FALSE, FALSE,
5650 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5653 Xandroid_1_e, FALSE, FALSE,
5654 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5657 Xandroid_2_e, FALSE, FALSE,
5658 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5661 Xandroid_1_w, FALSE, FALSE,
5662 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5665 Xandroid_2_w, FALSE, FALSE,
5666 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5669 Xandroid_1_s, FALSE, FALSE,
5670 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5673 Xandroid_2_s, FALSE, FALSE,
5674 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5677 Yandroid_n, FALSE, FALSE,
5678 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5681 Yandroid_nB, FALSE, TRUE,
5682 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5685 Yandroid_ne, FALSE, FALSE,
5686 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5689 Yandroid_neB, FALSE, TRUE,
5690 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5693 Yandroid_e, FALSE, FALSE,
5694 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5697 Yandroid_eB, FALSE, TRUE,
5698 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5701 Yandroid_se, FALSE, FALSE,
5702 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5705 Yandroid_seB, FALSE, TRUE,
5706 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5709 Yandroid_s, FALSE, FALSE,
5710 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5713 Yandroid_sB, FALSE, TRUE,
5714 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5717 Yandroid_sw, FALSE, FALSE,
5718 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5721 Yandroid_swB, FALSE, TRUE,
5722 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5725 Yandroid_w, FALSE, FALSE,
5726 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5729 Yandroid_wB, FALSE, TRUE,
5730 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5733 Yandroid_nw, FALSE, FALSE,
5734 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5737 Yandroid_nwB, FALSE, TRUE,
5738 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5741 Xspring, TRUE, FALSE,
5745 Xspring_pause, FALSE, FALSE,
5749 Xspring_e, FALSE, FALSE,
5753 Xspring_w, FALSE, FALSE,
5757 Xspring_fall, FALSE, FALSE,
5761 Yspring_s, FALSE, FALSE,
5762 EL_SPRING, ACTION_FALLING, -1
5765 Yspring_sB, FALSE, TRUE,
5766 EL_SPRING, ACTION_FALLING, -1
5769 Yspring_e, FALSE, FALSE,
5770 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5773 Yspring_eB, FALSE, TRUE,
5774 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5777 Yspring_w, FALSE, FALSE,
5778 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5781 Yspring_wB, FALSE, TRUE,
5782 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5785 Yspring_kill_e, FALSE, FALSE,
5786 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5789 Yspring_kill_eB, FALSE, TRUE,
5790 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5793 Yspring_kill_w, FALSE, FALSE,
5794 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5797 Yspring_kill_wB, FALSE, TRUE,
5798 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5801 Xeater_n, TRUE, FALSE,
5802 EL_YAMYAM_UP, -1, -1
5805 Xeater_e, TRUE, FALSE,
5806 EL_YAMYAM_RIGHT, -1, -1
5809 Xeater_w, TRUE, FALSE,
5810 EL_YAMYAM_LEFT, -1, -1
5813 Xeater_s, TRUE, FALSE,
5814 EL_YAMYAM_DOWN, -1, -1
5817 Yeater_n, FALSE, FALSE,
5818 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5821 Yeater_nB, FALSE, TRUE,
5822 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5825 Yeater_e, FALSE, FALSE,
5826 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5829 Yeater_eB, FALSE, TRUE,
5830 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5833 Yeater_s, FALSE, FALSE,
5834 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5837 Yeater_sB, FALSE, TRUE,
5838 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5841 Yeater_w, FALSE, FALSE,
5842 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5845 Yeater_wB, FALSE, TRUE,
5846 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5849 Yeater_stone, FALSE, FALSE,
5850 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5853 Yeater_spring, FALSE, FALSE,
5854 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5857 Xalien, TRUE, FALSE,
5861 Xalien_pause, FALSE, FALSE,
5865 Yalien_n, FALSE, FALSE,
5866 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5869 Yalien_nB, FALSE, TRUE,
5870 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5873 Yalien_e, FALSE, FALSE,
5874 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5877 Yalien_eB, FALSE, TRUE,
5878 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5881 Yalien_s, FALSE, FALSE,
5882 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5885 Yalien_sB, FALSE, TRUE,
5886 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5889 Yalien_w, FALSE, FALSE,
5890 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5893 Yalien_wB, FALSE, TRUE,
5894 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5897 Yalien_stone, FALSE, FALSE,
5898 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5901 Yalien_spring, FALSE, FALSE,
5902 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5905 Xemerald, TRUE, FALSE,
5909 Xemerald_pause, FALSE, FALSE,
5913 Xemerald_fall, FALSE, FALSE,
5917 Xemerald_shine, FALSE, FALSE,
5918 EL_EMERALD, ACTION_TWINKLING, -1
5921 Yemerald_s, FALSE, FALSE,
5922 EL_EMERALD, ACTION_FALLING, -1
5925 Yemerald_sB, FALSE, TRUE,
5926 EL_EMERALD, ACTION_FALLING, -1
5929 Yemerald_e, FALSE, FALSE,
5930 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5933 Yemerald_eB, FALSE, TRUE,
5934 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5937 Yemerald_w, FALSE, FALSE,
5938 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5941 Yemerald_wB, FALSE, TRUE,
5942 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5945 Yemerald_eat, FALSE, FALSE,
5946 EL_EMERALD, ACTION_COLLECTING, -1
5949 Yemerald_stone, FALSE, FALSE,
5950 EL_NUT, ACTION_BREAKING, -1
5953 Xdiamond, TRUE, FALSE,
5957 Xdiamond_pause, FALSE, FALSE,
5961 Xdiamond_fall, FALSE, FALSE,
5965 Xdiamond_shine, FALSE, FALSE,
5966 EL_DIAMOND, ACTION_TWINKLING, -1
5969 Ydiamond_s, FALSE, FALSE,
5970 EL_DIAMOND, ACTION_FALLING, -1
5973 Ydiamond_sB, FALSE, TRUE,
5974 EL_DIAMOND, ACTION_FALLING, -1
5977 Ydiamond_e, FALSE, FALSE,
5978 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5981 Ydiamond_eB, FALSE, TRUE,
5982 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5985 Ydiamond_w, FALSE, FALSE,
5986 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5989 Ydiamond_wB, FALSE, TRUE,
5990 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5993 Ydiamond_eat, FALSE, FALSE,
5994 EL_DIAMOND, ACTION_COLLECTING, -1
5997 Ydiamond_stone, FALSE, FALSE,
5998 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
6001 Xdrip_fall, TRUE, FALSE,
6002 EL_AMOEBA_DROP, -1, -1
6005 Xdrip_stretch, FALSE, FALSE,
6006 EL_AMOEBA_DROP, ACTION_FALLING, -1
6009 Xdrip_stretchB, FALSE, TRUE,
6010 EL_AMOEBA_DROP, ACTION_FALLING, -1
6013 Xdrip_eat, FALSE, FALSE,
6014 EL_AMOEBA_DROP, ACTION_GROWING, -1
6017 Ydrip_s1, FALSE, FALSE,
6018 EL_AMOEBA_DROP, ACTION_FALLING, -1
6021 Ydrip_s1B, FALSE, TRUE,
6022 EL_AMOEBA_DROP, ACTION_FALLING, -1
6025 Ydrip_s2, FALSE, FALSE,
6026 EL_AMOEBA_DROP, ACTION_FALLING, -1
6029 Ydrip_s2B, FALSE, TRUE,
6030 EL_AMOEBA_DROP, ACTION_FALLING, -1
6037 Xbomb_pause, FALSE, FALSE,
6041 Xbomb_fall, FALSE, FALSE,
6045 Ybomb_s, FALSE, FALSE,
6046 EL_BOMB, ACTION_FALLING, -1
6049 Ybomb_sB, FALSE, TRUE,
6050 EL_BOMB, ACTION_FALLING, -1
6053 Ybomb_e, FALSE, FALSE,
6054 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6057 Ybomb_eB, FALSE, TRUE,
6058 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6061 Ybomb_w, FALSE, FALSE,
6062 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6065 Ybomb_wB, FALSE, TRUE,
6066 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6069 Ybomb_eat, FALSE, FALSE,
6070 EL_BOMB, ACTION_ACTIVATING, -1
6073 Xballoon, TRUE, FALSE,
6077 Yballoon_n, FALSE, FALSE,
6078 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6081 Yballoon_nB, FALSE, TRUE,
6082 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6085 Yballoon_e, FALSE, FALSE,
6086 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6089 Yballoon_eB, FALSE, TRUE,
6090 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6093 Yballoon_s, FALSE, FALSE,
6094 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6097 Yballoon_sB, FALSE, TRUE,
6098 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6101 Yballoon_w, FALSE, FALSE,
6102 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6105 Yballoon_wB, FALSE, TRUE,
6106 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6109 Xgrass, TRUE, FALSE,
6110 EL_EMC_GRASS, -1, -1
6113 Ygrass_nB, FALSE, FALSE,
6114 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6117 Ygrass_eB, FALSE, FALSE,
6118 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6121 Ygrass_sB, FALSE, FALSE,
6122 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6125 Ygrass_wB, FALSE, FALSE,
6126 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6133 Ydirt_nB, FALSE, FALSE,
6134 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6137 Ydirt_eB, FALSE, FALSE,
6138 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6141 Ydirt_sB, FALSE, FALSE,
6142 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6145 Ydirt_wB, FALSE, FALSE,
6146 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6149 Xacid_ne, TRUE, FALSE,
6150 EL_ACID_POOL_TOPRIGHT, -1, -1
6153 Xacid_se, TRUE, FALSE,
6154 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6157 Xacid_s, TRUE, FALSE,
6158 EL_ACID_POOL_BOTTOM, -1, -1
6161 Xacid_sw, TRUE, FALSE,
6162 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6165 Xacid_nw, TRUE, FALSE,
6166 EL_ACID_POOL_TOPLEFT, -1, -1
6169 Xacid_1, TRUE, FALSE,
6173 Xacid_2, FALSE, FALSE,
6177 Xacid_3, FALSE, FALSE,
6181 Xacid_4, FALSE, FALSE,
6185 Xacid_5, FALSE, FALSE,
6189 Xacid_6, FALSE, FALSE,
6193 Xacid_7, FALSE, FALSE,
6197 Xacid_8, FALSE, FALSE,
6201 Xball_1, TRUE, FALSE,
6202 EL_EMC_MAGIC_BALL, -1, -1
6205 Xball_1B, FALSE, FALSE,
6206 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6209 Xball_2, FALSE, FALSE,
6210 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6213 Xball_2B, FALSE, FALSE,
6214 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6217 Yball_eat, FALSE, FALSE,
6218 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6221 Ykey_1_eat, FALSE, FALSE,
6222 EL_EM_KEY_1, ACTION_COLLECTING, -1
6225 Ykey_2_eat, FALSE, FALSE,
6226 EL_EM_KEY_2, ACTION_COLLECTING, -1
6229 Ykey_3_eat, FALSE, FALSE,
6230 EL_EM_KEY_3, ACTION_COLLECTING, -1
6233 Ykey_4_eat, FALSE, FALSE,
6234 EL_EM_KEY_4, ACTION_COLLECTING, -1
6237 Ykey_5_eat, FALSE, FALSE,
6238 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6241 Ykey_6_eat, FALSE, FALSE,
6242 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6245 Ykey_7_eat, FALSE, FALSE,
6246 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6249 Ykey_8_eat, FALSE, FALSE,
6250 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6253 Ylenses_eat, FALSE, FALSE,
6254 EL_EMC_LENSES, ACTION_COLLECTING, -1
6257 Ymagnify_eat, FALSE, FALSE,
6258 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6261 Ygrass_eat, FALSE, FALSE,
6262 EL_EMC_GRASS, ACTION_SNAPPING, -1
6265 Ydirt_eat, FALSE, FALSE,
6266 EL_SAND, ACTION_SNAPPING, -1
6269 Xgrow_ns, TRUE, FALSE,
6270 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6273 Ygrow_ns_eat, FALSE, FALSE,
6274 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6277 Xgrow_ew, TRUE, FALSE,
6278 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6281 Ygrow_ew_eat, FALSE, FALSE,
6282 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6285 Xwonderwall, TRUE, FALSE,
6286 EL_MAGIC_WALL, -1, -1
6289 XwonderwallB, FALSE, FALSE,
6290 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6293 Xamoeba_1, TRUE, FALSE,
6294 EL_AMOEBA_DRY, ACTION_OTHER, -1
6297 Xamoeba_2, FALSE, FALSE,
6298 EL_AMOEBA_DRY, ACTION_OTHER, -1
6301 Xamoeba_3, FALSE, FALSE,
6302 EL_AMOEBA_DRY, ACTION_OTHER, -1
6305 Xamoeba_4, FALSE, FALSE,
6306 EL_AMOEBA_DRY, ACTION_OTHER, -1
6309 Xamoeba_5, TRUE, FALSE,
6310 EL_AMOEBA_WET, ACTION_OTHER, -1
6313 Xamoeba_6, FALSE, FALSE,
6314 EL_AMOEBA_WET, ACTION_OTHER, -1
6317 Xamoeba_7, FALSE, FALSE,
6318 EL_AMOEBA_WET, ACTION_OTHER, -1
6321 Xamoeba_8, FALSE, FALSE,
6322 EL_AMOEBA_WET, ACTION_OTHER, -1
6325 Xdoor_1, TRUE, FALSE,
6326 EL_EM_GATE_1, -1, -1
6329 Xdoor_2, TRUE, FALSE,
6330 EL_EM_GATE_2, -1, -1
6333 Xdoor_3, TRUE, FALSE,
6334 EL_EM_GATE_3, -1, -1
6337 Xdoor_4, TRUE, FALSE,
6338 EL_EM_GATE_4, -1, -1
6341 Xdoor_5, TRUE, FALSE,
6342 EL_EMC_GATE_5, -1, -1
6345 Xdoor_6, TRUE, FALSE,
6346 EL_EMC_GATE_6, -1, -1
6349 Xdoor_7, TRUE, FALSE,
6350 EL_EMC_GATE_7, -1, -1
6353 Xdoor_8, TRUE, FALSE,
6354 EL_EMC_GATE_8, -1, -1
6357 Xkey_1, TRUE, FALSE,
6361 Xkey_2, TRUE, FALSE,
6365 Xkey_3, TRUE, FALSE,
6369 Xkey_4, TRUE, FALSE,
6373 Xkey_5, TRUE, FALSE,
6374 EL_EMC_KEY_5, -1, -1
6377 Xkey_6, TRUE, FALSE,
6378 EL_EMC_KEY_6, -1, -1
6381 Xkey_7, TRUE, FALSE,
6382 EL_EMC_KEY_7, -1, -1
6385 Xkey_8, TRUE, FALSE,
6386 EL_EMC_KEY_8, -1, -1
6389 Xwind_n, TRUE, FALSE,
6390 EL_BALLOON_SWITCH_UP, -1, -1
6393 Xwind_e, TRUE, FALSE,
6394 EL_BALLOON_SWITCH_RIGHT, -1, -1
6397 Xwind_s, TRUE, FALSE,
6398 EL_BALLOON_SWITCH_DOWN, -1, -1
6401 Xwind_w, TRUE, FALSE,
6402 EL_BALLOON_SWITCH_LEFT, -1, -1
6405 Xwind_nesw, TRUE, FALSE,
6406 EL_BALLOON_SWITCH_ANY, -1, -1
6409 Xwind_stop, TRUE, FALSE,
6410 EL_BALLOON_SWITCH_NONE, -1, -1
6414 EL_EM_EXIT_CLOSED, -1, -1
6417 Xexit_1, TRUE, FALSE,
6418 EL_EM_EXIT_OPEN, -1, -1
6421 Xexit_2, FALSE, FALSE,
6422 EL_EM_EXIT_OPEN, -1, -1
6425 Xexit_3, FALSE, FALSE,
6426 EL_EM_EXIT_OPEN, -1, -1
6429 Xdynamite, TRUE, FALSE,
6430 EL_EM_DYNAMITE, -1, -1
6433 Ydynamite_eat, FALSE, FALSE,
6434 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6437 Xdynamite_1, TRUE, FALSE,
6438 EL_EM_DYNAMITE_ACTIVE, -1, -1
6441 Xdynamite_2, FALSE, FALSE,
6442 EL_EM_DYNAMITE_ACTIVE, -1, -1
6445 Xdynamite_3, FALSE, FALSE,
6446 EL_EM_DYNAMITE_ACTIVE, -1, -1
6449 Xdynamite_4, FALSE, FALSE,
6450 EL_EM_DYNAMITE_ACTIVE, -1, -1
6453 Xbumper, TRUE, FALSE,
6454 EL_EMC_SPRING_BUMPER, -1, -1
6457 XbumperB, FALSE, FALSE,
6458 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6461 Xwheel, TRUE, FALSE,
6462 EL_ROBOT_WHEEL, -1, -1
6465 XwheelB, FALSE, FALSE,
6466 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6469 Xswitch, TRUE, FALSE,
6470 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6473 XswitchB, FALSE, FALSE,
6474 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6478 EL_QUICKSAND_EMPTY, -1, -1
6481 Xsand_stone, TRUE, FALSE,
6482 EL_QUICKSAND_FULL, -1, -1
6485 Xsand_stonein_1, FALSE, TRUE,
6486 EL_ROCK, ACTION_FILLING, -1
6489 Xsand_stonein_2, FALSE, TRUE,
6490 EL_ROCK, ACTION_FILLING, -1
6493 Xsand_stonein_3, FALSE, TRUE,
6494 EL_ROCK, ACTION_FILLING, -1
6497 Xsand_stonein_4, FALSE, TRUE,
6498 EL_ROCK, ACTION_FILLING, -1
6501 Xsand_stonesand_1, FALSE, FALSE,
6502 EL_QUICKSAND_EMPTYING, -1, -1
6505 Xsand_stonesand_2, FALSE, FALSE,
6506 EL_QUICKSAND_EMPTYING, -1, -1
6509 Xsand_stonesand_3, FALSE, FALSE,
6510 EL_QUICKSAND_EMPTYING, -1, -1
6513 Xsand_stonesand_4, FALSE, FALSE,
6514 EL_QUICKSAND_EMPTYING, -1, -1
6517 Xsand_stonesand_quickout_1, FALSE, FALSE,
6518 EL_QUICKSAND_EMPTYING, -1, -1
6521 Xsand_stonesand_quickout_2, FALSE, FALSE,
6522 EL_QUICKSAND_EMPTYING, -1, -1
6525 Xsand_stoneout_1, FALSE, FALSE,
6526 EL_ROCK, ACTION_EMPTYING, -1
6529 Xsand_stoneout_2, FALSE, FALSE,
6530 EL_ROCK, ACTION_EMPTYING, -1
6533 Xsand_sandstone_1, FALSE, FALSE,
6534 EL_QUICKSAND_FILLING, -1, -1
6537 Xsand_sandstone_2, FALSE, FALSE,
6538 EL_QUICKSAND_FILLING, -1, -1
6541 Xsand_sandstone_3, FALSE, FALSE,
6542 EL_QUICKSAND_FILLING, -1, -1
6545 Xsand_sandstone_4, FALSE, FALSE,
6546 EL_QUICKSAND_FILLING, -1, -1
6549 Xplant, TRUE, FALSE,
6550 EL_EMC_PLANT, -1, -1
6553 Yplant, FALSE, FALSE,
6554 EL_EMC_PLANT, -1, -1
6557 Xlenses, TRUE, FALSE,
6558 EL_EMC_LENSES, -1, -1
6561 Xmagnify, TRUE, FALSE,
6562 EL_EMC_MAGNIFIER, -1, -1
6565 Xdripper, TRUE, FALSE,
6566 EL_EMC_DRIPPER, -1, -1
6569 XdripperB, FALSE, FALSE,
6570 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6573 Xfake_blank, TRUE, FALSE,
6574 EL_INVISIBLE_WALL, -1, -1
6577 Xfake_blankB, FALSE, FALSE,
6578 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6581 Xfake_grass, TRUE, FALSE,
6582 EL_EMC_FAKE_GRASS, -1, -1
6585 Xfake_grassB, FALSE, FALSE,
6586 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6589 Xfake_door_1, TRUE, FALSE,
6590 EL_EM_GATE_1_GRAY, -1, -1
6593 Xfake_door_2, TRUE, FALSE,
6594 EL_EM_GATE_2_GRAY, -1, -1
6597 Xfake_door_3, TRUE, FALSE,
6598 EL_EM_GATE_3_GRAY, -1, -1
6601 Xfake_door_4, TRUE, FALSE,
6602 EL_EM_GATE_4_GRAY, -1, -1
6605 Xfake_door_5, TRUE, FALSE,
6606 EL_EMC_GATE_5_GRAY, -1, -1
6609 Xfake_door_6, TRUE, FALSE,
6610 EL_EMC_GATE_6_GRAY, -1, -1
6613 Xfake_door_7, TRUE, FALSE,
6614 EL_EMC_GATE_7_GRAY, -1, -1
6617 Xfake_door_8, TRUE, FALSE,
6618 EL_EMC_GATE_8_GRAY, -1, -1
6621 Xfake_acid_1, TRUE, FALSE,
6622 EL_EMC_FAKE_ACID, -1, -1
6625 Xfake_acid_2, FALSE, FALSE,
6626 EL_EMC_FAKE_ACID, -1, -1
6629 Xfake_acid_3, FALSE, FALSE,
6630 EL_EMC_FAKE_ACID, -1, -1
6633 Xfake_acid_4, FALSE, FALSE,
6634 EL_EMC_FAKE_ACID, -1, -1
6637 Xfake_acid_5, FALSE, FALSE,
6638 EL_EMC_FAKE_ACID, -1, -1
6641 Xfake_acid_6, FALSE, FALSE,
6642 EL_EMC_FAKE_ACID, -1, -1
6645 Xfake_acid_7, FALSE, FALSE,
6646 EL_EMC_FAKE_ACID, -1, -1
6649 Xfake_acid_8, FALSE, FALSE,
6650 EL_EMC_FAKE_ACID, -1, -1
6653 Xsteel_1, TRUE, FALSE,
6654 EL_STEELWALL, -1, -1
6657 Xsteel_2, TRUE, FALSE,
6658 EL_EMC_STEELWALL_2, -1, -1
6661 Xsteel_3, TRUE, FALSE,
6662 EL_EMC_STEELWALL_3, -1, -1
6665 Xsteel_4, TRUE, FALSE,
6666 EL_EMC_STEELWALL_4, -1, -1
6669 Xwall_1, TRUE, FALSE,
6673 Xwall_2, TRUE, FALSE,
6674 EL_EMC_WALL_14, -1, -1
6677 Xwall_3, TRUE, FALSE,
6678 EL_EMC_WALL_15, -1, -1
6681 Xwall_4, TRUE, FALSE,
6682 EL_EMC_WALL_16, -1, -1
6685 Xround_wall_1, TRUE, FALSE,
6686 EL_WALL_SLIPPERY, -1, -1
6689 Xround_wall_2, TRUE, FALSE,
6690 EL_EMC_WALL_SLIPPERY_2, -1, -1
6693 Xround_wall_3, TRUE, FALSE,
6694 EL_EMC_WALL_SLIPPERY_3, -1, -1
6697 Xround_wall_4, TRUE, FALSE,
6698 EL_EMC_WALL_SLIPPERY_4, -1, -1
6701 Xdecor_1, TRUE, FALSE,
6702 EL_EMC_WALL_8, -1, -1
6705 Xdecor_2, TRUE, FALSE,
6706 EL_EMC_WALL_6, -1, -1
6709 Xdecor_3, TRUE, FALSE,
6710 EL_EMC_WALL_4, -1, -1
6713 Xdecor_4, TRUE, FALSE,
6714 EL_EMC_WALL_7, -1, -1
6717 Xdecor_5, TRUE, FALSE,
6718 EL_EMC_WALL_5, -1, -1
6721 Xdecor_6, TRUE, FALSE,
6722 EL_EMC_WALL_9, -1, -1
6725 Xdecor_7, TRUE, FALSE,
6726 EL_EMC_WALL_10, -1, -1
6729 Xdecor_8, TRUE, FALSE,
6730 EL_EMC_WALL_1, -1, -1
6733 Xdecor_9, TRUE, FALSE,
6734 EL_EMC_WALL_2, -1, -1
6737 Xdecor_10, TRUE, FALSE,
6738 EL_EMC_WALL_3, -1, -1
6741 Xdecor_11, TRUE, FALSE,
6742 EL_EMC_WALL_11, -1, -1
6745 Xdecor_12, TRUE, FALSE,
6746 EL_EMC_WALL_12, -1, -1
6749 Xalpha_0, TRUE, FALSE,
6750 EL_CHAR('0'), -1, -1
6753 Xalpha_1, TRUE, FALSE,
6754 EL_CHAR('1'), -1, -1
6757 Xalpha_2, TRUE, FALSE,
6758 EL_CHAR('2'), -1, -1
6761 Xalpha_3, TRUE, FALSE,
6762 EL_CHAR('3'), -1, -1
6765 Xalpha_4, TRUE, FALSE,
6766 EL_CHAR('4'), -1, -1
6769 Xalpha_5, TRUE, FALSE,
6770 EL_CHAR('5'), -1, -1
6773 Xalpha_6, TRUE, FALSE,
6774 EL_CHAR('6'), -1, -1
6777 Xalpha_7, TRUE, FALSE,
6778 EL_CHAR('7'), -1, -1
6781 Xalpha_8, TRUE, FALSE,
6782 EL_CHAR('8'), -1, -1
6785 Xalpha_9, TRUE, FALSE,
6786 EL_CHAR('9'), -1, -1
6789 Xalpha_excla, TRUE, FALSE,
6790 EL_CHAR('!'), -1, -1
6793 Xalpha_quote, TRUE, FALSE,
6794 EL_CHAR('"'), -1, -1
6797 Xalpha_comma, TRUE, FALSE,
6798 EL_CHAR(','), -1, -1
6801 Xalpha_minus, TRUE, FALSE,
6802 EL_CHAR('-'), -1, -1
6805 Xalpha_perio, TRUE, FALSE,
6806 EL_CHAR('.'), -1, -1
6809 Xalpha_colon, TRUE, FALSE,
6810 EL_CHAR(':'), -1, -1
6813 Xalpha_quest, TRUE, FALSE,
6814 EL_CHAR('?'), -1, -1
6817 Xalpha_a, TRUE, FALSE,
6818 EL_CHAR('A'), -1, -1
6821 Xalpha_b, TRUE, FALSE,
6822 EL_CHAR('B'), -1, -1
6825 Xalpha_c, TRUE, FALSE,
6826 EL_CHAR('C'), -1, -1
6829 Xalpha_d, TRUE, FALSE,
6830 EL_CHAR('D'), -1, -1
6833 Xalpha_e, TRUE, FALSE,
6834 EL_CHAR('E'), -1, -1
6837 Xalpha_f, TRUE, FALSE,
6838 EL_CHAR('F'), -1, -1
6841 Xalpha_g, TRUE, FALSE,
6842 EL_CHAR('G'), -1, -1
6845 Xalpha_h, TRUE, FALSE,
6846 EL_CHAR('H'), -1, -1
6849 Xalpha_i, TRUE, FALSE,
6850 EL_CHAR('I'), -1, -1
6853 Xalpha_j, TRUE, FALSE,
6854 EL_CHAR('J'), -1, -1
6857 Xalpha_k, TRUE, FALSE,
6858 EL_CHAR('K'), -1, -1
6861 Xalpha_l, TRUE, FALSE,
6862 EL_CHAR('L'), -1, -1
6865 Xalpha_m, TRUE, FALSE,
6866 EL_CHAR('M'), -1, -1
6869 Xalpha_n, TRUE, FALSE,
6870 EL_CHAR('N'), -1, -1
6873 Xalpha_o, TRUE, FALSE,
6874 EL_CHAR('O'), -1, -1
6877 Xalpha_p, TRUE, FALSE,
6878 EL_CHAR('P'), -1, -1
6881 Xalpha_q, TRUE, FALSE,
6882 EL_CHAR('Q'), -1, -1
6885 Xalpha_r, TRUE, FALSE,
6886 EL_CHAR('R'), -1, -1
6889 Xalpha_s, TRUE, FALSE,
6890 EL_CHAR('S'), -1, -1
6893 Xalpha_t, TRUE, FALSE,
6894 EL_CHAR('T'), -1, -1
6897 Xalpha_u, TRUE, FALSE,
6898 EL_CHAR('U'), -1, -1
6901 Xalpha_v, TRUE, FALSE,
6902 EL_CHAR('V'), -1, -1
6905 Xalpha_w, TRUE, FALSE,
6906 EL_CHAR('W'), -1, -1
6909 Xalpha_x, TRUE, FALSE,
6910 EL_CHAR('X'), -1, -1
6913 Xalpha_y, TRUE, FALSE,
6914 EL_CHAR('Y'), -1, -1
6917 Xalpha_z, TRUE, FALSE,
6918 EL_CHAR('Z'), -1, -1
6921 Xalpha_arrow_e, TRUE, FALSE,
6922 EL_CHAR('>'), -1, -1
6925 Xalpha_arrow_w, TRUE, FALSE,
6926 EL_CHAR('<'), -1, -1
6929 Xalpha_copyr, TRUE, FALSE,
6930 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6934 Xboom_bug, FALSE, FALSE,
6935 EL_BUG, ACTION_EXPLODING, -1
6938 Xboom_bomb, FALSE, FALSE,
6939 EL_BOMB, ACTION_EXPLODING, -1
6942 Xboom_android, FALSE, FALSE,
6943 EL_EMC_ANDROID, ACTION_OTHER, -1
6946 Xboom_1, FALSE, FALSE,
6947 EL_DEFAULT, ACTION_EXPLODING, -1
6950 Xboom_2, FALSE, FALSE,
6951 EL_DEFAULT, ACTION_EXPLODING, -1
6954 Znormal, FALSE, FALSE,
6958 Zdynamite, FALSE, FALSE,
6962 Zplayer, FALSE, FALSE,
6966 ZBORDER, FALSE, FALSE,
6976 static struct Mapping_EM_to_RND_player
6985 em_player_mapping_list[] =
6989 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6993 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6997 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
7001 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
7005 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
7009 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
7013 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
7017 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
7021 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
7025 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
7029 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
7033 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
7037 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7041 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7045 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7049 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7053 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7057 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7061 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7065 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7069 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7073 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7077 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7081 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7085 EL_PLAYER_1, ACTION_DEFAULT, -1,
7089 EL_PLAYER_2, ACTION_DEFAULT, -1,
7093 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7097 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7101 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7105 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7109 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7113 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7117 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7121 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7125 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7129 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7133 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7137 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7141 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7145 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7149 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7153 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7157 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7161 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7165 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7169 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7173 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7177 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7181 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7185 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7189 EL_PLAYER_3, ACTION_DEFAULT, -1,
7193 EL_PLAYER_4, ACTION_DEFAULT, -1,
7202 int map_element_RND_to_EM(int element_rnd)
7204 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7205 static boolean mapping_initialized = FALSE;
7207 if (!mapping_initialized)
7211 /* return "Xalpha_quest" for all undefined elements in mapping array */
7212 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7213 mapping_RND_to_EM[i] = Xalpha_quest;
7215 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7216 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7217 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7218 em_object_mapping_list[i].element_em;
7220 mapping_initialized = TRUE;
7223 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7224 return mapping_RND_to_EM[element_rnd];
7226 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7231 int map_element_EM_to_RND(int element_em)
7233 static unsigned short mapping_EM_to_RND[TILE_MAX];
7234 static boolean mapping_initialized = FALSE;
7236 if (!mapping_initialized)
7240 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
7241 for (i = 0; i < TILE_MAX; i++)
7242 mapping_EM_to_RND[i] = EL_UNKNOWN;
7244 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7245 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7246 em_object_mapping_list[i].element_rnd;
7248 mapping_initialized = TRUE;
7251 if (element_em >= 0 && element_em < TILE_MAX)
7252 return mapping_EM_to_RND[element_em];
7254 Error(ERR_WARN, "invalid EM level element %d", element_em);
7259 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7261 struct LevelInfo_EM *level_em = level->native_em_level;
7262 struct LEVEL *lev = level_em->lev;
7265 for (i = 0; i < TILE_MAX; i++)
7266 lev->android_array[i] = Xblank;
7268 for (i = 0; i < level->num_android_clone_elements; i++)
7270 int element_rnd = level->android_clone_element[i];
7271 int element_em = map_element_RND_to_EM(element_rnd);
7273 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7274 if (em_object_mapping_list[j].element_rnd == element_rnd)
7275 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7279 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7281 struct LevelInfo_EM *level_em = level->native_em_level;
7282 struct LEVEL *lev = level_em->lev;
7285 level->num_android_clone_elements = 0;
7287 for (i = 0; i < TILE_MAX; i++)
7289 int element_em = lev->android_array[i];
7291 boolean element_found = FALSE;
7293 if (element_em == Xblank)
7296 element_rnd = map_element_EM_to_RND(element_em);
7298 for (j = 0; j < level->num_android_clone_elements; j++)
7299 if (level->android_clone_element[j] == element_rnd)
7300 element_found = TRUE;
7304 level->android_clone_element[level->num_android_clone_elements++] =
7307 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7312 if (level->num_android_clone_elements == 0)
7314 level->num_android_clone_elements = 1;
7315 level->android_clone_element[0] = EL_EMPTY;
7319 int map_direction_RND_to_EM(int direction)
7321 return (direction == MV_UP ? 0 :
7322 direction == MV_RIGHT ? 1 :
7323 direction == MV_DOWN ? 2 :
7324 direction == MV_LEFT ? 3 :
7328 int map_direction_EM_to_RND(int direction)
7330 return (direction == 0 ? MV_UP :
7331 direction == 1 ? MV_RIGHT :
7332 direction == 2 ? MV_DOWN :
7333 direction == 3 ? MV_LEFT :
7337 int map_element_RND_to_SP(int element_rnd)
7339 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
7341 if (element_rnd >= EL_SP_START &&
7342 element_rnd <= EL_SP_END)
7343 element_sp = element_rnd - EL_SP_START;
7344 else if (element_rnd == EL_EMPTY_SPACE)
7346 else if (element_rnd == EL_INVISIBLE_WALL)
7352 int map_element_SP_to_RND(int element_sp)
7354 int element_rnd = EL_UNKNOWN;
7356 if (element_sp >= 0x00 &&
7358 element_rnd = EL_SP_START + element_sp;
7359 else if (element_sp == 0x28)
7360 element_rnd = EL_INVISIBLE_WALL;
7365 int map_action_SP_to_RND(int action_sp)
7369 case actActive: return ACTION_ACTIVE;
7370 case actImpact: return ACTION_IMPACT;
7371 case actExploding: return ACTION_EXPLODING;
7372 case actDigging: return ACTION_DIGGING;
7373 case actSnapping: return ACTION_SNAPPING;
7374 case actCollecting: return ACTION_COLLECTING;
7375 case actPassing: return ACTION_PASSING;
7376 case actPushing: return ACTION_PUSHING;
7377 case actDropping: return ACTION_DROPPING;
7379 default: return ACTION_DEFAULT;
7383 int map_element_RND_to_MM(int element_rnd)
7385 return (element_rnd >= EL_MM_START_1 &&
7386 element_rnd <= EL_MM_END_1 ?
7387 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7389 element_rnd >= EL_MM_START_2 &&
7390 element_rnd <= EL_MM_END_2 ?
7391 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7393 element_rnd >= EL_CHAR_START &&
7394 element_rnd <= EL_CHAR_END ?
7395 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7397 element_rnd >= EL_MM_RUNTIME_START &&
7398 element_rnd <= EL_MM_RUNTIME_END ?
7399 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7401 element_rnd >= EL_MM_DUMMY_START &&
7402 element_rnd <= EL_MM_DUMMY_END ?
7403 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7405 EL_MM_EMPTY_NATIVE);
7408 int map_element_MM_to_RND(int element_mm)
7410 return (element_mm == EL_MM_EMPTY_NATIVE ||
7411 element_mm == EL_DF_EMPTY_NATIVE ?
7414 element_mm >= EL_MM_START_1_NATIVE &&
7415 element_mm <= EL_MM_END_1_NATIVE ?
7416 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7418 element_mm >= EL_MM_START_2_NATIVE &&
7419 element_mm <= EL_MM_END_2_NATIVE ?
7420 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7422 element_mm >= EL_MM_CHAR_START_NATIVE &&
7423 element_mm <= EL_MM_CHAR_END_NATIVE ?
7424 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7426 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7427 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7428 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7430 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7431 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7432 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7437 int map_action_MM_to_RND(int action_mm)
7439 /* all MM actions are defined to exactly match their RND counterparts */
7443 int map_sound_MM_to_RND(int sound_mm)
7447 case SND_MM_GAME_LEVELTIME_CHARGING:
7448 return SND_GAME_LEVELTIME_CHARGING;
7450 case SND_MM_GAME_HEALTH_CHARGING:
7451 return SND_GAME_HEALTH_CHARGING;
7454 return SND_UNDEFINED;
7458 int map_mm_wall_element(int element)
7460 return (element >= EL_MM_STEEL_WALL_START &&
7461 element <= EL_MM_STEEL_WALL_END ?
7464 element >= EL_MM_WOODEN_WALL_START &&
7465 element <= EL_MM_WOODEN_WALL_END ?
7468 element >= EL_MM_ICE_WALL_START &&
7469 element <= EL_MM_ICE_WALL_END ?
7472 element >= EL_MM_AMOEBA_WALL_START &&
7473 element <= EL_MM_AMOEBA_WALL_END ?
7476 element >= EL_DF_STEEL_WALL_START &&
7477 element <= EL_DF_STEEL_WALL_END ?
7480 element >= EL_DF_WOODEN_WALL_START &&
7481 element <= EL_DF_WOODEN_WALL_END ?
7487 int map_mm_wall_element_editor(int element)
7491 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
7492 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
7493 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
7494 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
7495 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
7496 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
7498 default: return element;
7502 int get_next_element(int element)
7506 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7507 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7508 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7509 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7510 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7511 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7512 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7513 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7514 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7515 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7516 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7518 default: return element;
7522 int el2img_mm(int element_mm)
7524 return el2img(map_element_MM_to_RND(element_mm));
7527 int el_act_dir2img(int element, int action, int direction)
7529 element = GFX_ELEMENT(element);
7530 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7532 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7533 return element_info[element].direction_graphic[action][direction];
7536 static int el_act_dir2crm(int element, int action, int direction)
7538 element = GFX_ELEMENT(element);
7539 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7541 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7542 return element_info[element].direction_crumbled[action][direction];
7545 int el_act2img(int element, int action)
7547 element = GFX_ELEMENT(element);
7549 return element_info[element].graphic[action];
7552 int el_act2crm(int element, int action)
7554 element = GFX_ELEMENT(element);
7556 return element_info[element].crumbled[action];
7559 int el_dir2img(int element, int direction)
7561 element = GFX_ELEMENT(element);
7563 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7566 int el2baseimg(int element)
7568 return element_info[element].graphic[ACTION_DEFAULT];
7571 int el2img(int element)
7573 element = GFX_ELEMENT(element);
7575 return element_info[element].graphic[ACTION_DEFAULT];
7578 int el2edimg(int element)
7580 element = GFX_ELEMENT(element);
7582 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7585 int el2preimg(int element)
7587 element = GFX_ELEMENT(element);
7589 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7592 int el2panelimg(int element)
7594 element = GFX_ELEMENT(element);
7596 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7599 int font2baseimg(int font_nr)
7601 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7604 int getBeltNrFromBeltElement(int element)
7606 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7607 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7608 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
7611 int getBeltNrFromBeltActiveElement(int element)
7613 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
7614 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
7615 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
7618 int getBeltNrFromBeltSwitchElement(int element)
7620 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
7621 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
7622 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
7625 int getBeltDirNrFromBeltElement(int element)
7627 static int belt_base_element[4] =
7629 EL_CONVEYOR_BELT_1_LEFT,
7630 EL_CONVEYOR_BELT_2_LEFT,
7631 EL_CONVEYOR_BELT_3_LEFT,
7632 EL_CONVEYOR_BELT_4_LEFT
7635 int belt_nr = getBeltNrFromBeltElement(element);
7636 int belt_dir_nr = element - belt_base_element[belt_nr];
7638 return (belt_dir_nr % 3);
7641 int getBeltDirNrFromBeltSwitchElement(int element)
7643 static int belt_base_element[4] =
7645 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7646 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7647 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7648 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7651 int belt_nr = getBeltNrFromBeltSwitchElement(element);
7652 int belt_dir_nr = element - belt_base_element[belt_nr];
7654 return (belt_dir_nr % 3);
7657 int getBeltDirFromBeltElement(int element)
7659 static int belt_move_dir[3] =
7666 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7668 return belt_move_dir[belt_dir_nr];
7671 int getBeltDirFromBeltSwitchElement(int element)
7673 static int belt_move_dir[3] =
7680 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7682 return belt_move_dir[belt_dir_nr];
7685 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7687 static int belt_base_element[4] =
7689 EL_CONVEYOR_BELT_1_LEFT,
7690 EL_CONVEYOR_BELT_2_LEFT,
7691 EL_CONVEYOR_BELT_3_LEFT,
7692 EL_CONVEYOR_BELT_4_LEFT
7695 return belt_base_element[belt_nr] + belt_dir_nr;
7698 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7700 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7702 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7705 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7707 static int belt_base_element[4] =
7709 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7710 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7711 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7712 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7715 return belt_base_element[belt_nr] + belt_dir_nr;
7718 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7720 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7722 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7725 boolean getTeamMode_EM()
7727 return game.team_mode;
7730 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7732 int game_frame_delay_value;
7734 game_frame_delay_value =
7735 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7736 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7739 if (tape.playing && tape.warp_forward && !tape.pausing)
7740 game_frame_delay_value = 0;
7742 return game_frame_delay_value;
7745 unsigned int InitRND(int seed)
7747 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7748 return InitEngineRandom_EM(seed);
7749 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7750 return InitEngineRandom_SP(seed);
7751 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
7752 return InitEngineRandom_MM(seed);
7754 return InitEngineRandom_RND(seed);
7757 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7758 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7760 inline static int get_effective_element_EM(int tile, int frame_em)
7762 int element = object_mapping[tile].element_rnd;
7763 int action = object_mapping[tile].action;
7764 boolean is_backside = object_mapping[tile].is_backside;
7765 boolean action_removing = (action == ACTION_DIGGING ||
7766 action == ACTION_SNAPPING ||
7767 action == ACTION_COLLECTING);
7773 case Yacid_splash_eB:
7774 case Yacid_splash_wB:
7775 return (frame_em > 5 ? EL_EMPTY : element);
7781 else /* frame_em == 7 */
7785 case Yacid_splash_eB:
7786 case Yacid_splash_wB:
7789 case Yemerald_stone:
7792 case Ydiamond_stone:
7796 case Xdrip_stretchB:
7815 case Xsand_stonein_1:
7816 case Xsand_stonein_2:
7817 case Xsand_stonein_3:
7818 case Xsand_stonein_4:
7822 return (is_backside || action_removing ? EL_EMPTY : element);
7827 inline static boolean check_linear_animation_EM(int tile)
7831 case Xsand_stonesand_1:
7832 case Xsand_stonesand_quickout_1:
7833 case Xsand_sandstone_1:
7834 case Xsand_stonein_1:
7835 case Xsand_stoneout_1:
7854 case Yacid_splash_eB:
7855 case Yacid_splash_wB:
7856 case Yemerald_stone:
7863 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7864 boolean has_crumbled_graphics,
7865 int crumbled, int sync_frame)
7867 /* if element can be crumbled, but certain action graphics are just empty
7868 space (like instantly snapping sand to empty space in 1 frame), do not
7869 treat these empty space graphics as crumbled graphics in EMC engine */
7870 if (crumbled == IMG_EMPTY_SPACE)
7871 has_crumbled_graphics = FALSE;
7873 if (has_crumbled_graphics)
7875 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7876 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7877 g_crumbled->anim_delay,
7878 g_crumbled->anim_mode,
7879 g_crumbled->anim_start_frame,
7882 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7883 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7885 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7886 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
7888 g_em->has_crumbled_graphics = TRUE;
7892 g_em->crumbled_bitmap = NULL;
7893 g_em->crumbled_src_x = 0;
7894 g_em->crumbled_src_y = 0;
7895 g_em->crumbled_border_size = 0;
7896 g_em->crumbled_tile_size = 0;
7898 g_em->has_crumbled_graphics = FALSE;
7902 void ResetGfxAnimation_EM(int x, int y, int tile)
7907 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7908 int tile, int frame_em, int x, int y)
7910 int action = object_mapping[tile].action;
7911 int direction = object_mapping[tile].direction;
7912 int effective_element = get_effective_element_EM(tile, frame_em);
7913 int graphic = (direction == MV_NONE ?
7914 el_act2img(effective_element, action) :
7915 el_act_dir2img(effective_element, action, direction));
7916 struct GraphicInfo *g = &graphic_info[graphic];
7918 boolean action_removing = (action == ACTION_DIGGING ||
7919 action == ACTION_SNAPPING ||
7920 action == ACTION_COLLECTING);
7921 boolean action_moving = (action == ACTION_FALLING ||
7922 action == ACTION_MOVING ||
7923 action == ACTION_PUSHING ||
7924 action == ACTION_EATING ||
7925 action == ACTION_FILLING ||
7926 action == ACTION_EMPTYING);
7927 boolean action_falling = (action == ACTION_FALLING ||
7928 action == ACTION_FILLING ||
7929 action == ACTION_EMPTYING);
7931 /* special case: graphic uses "2nd movement tile" and has defined
7932 7 frames for movement animation (or less) => use default graphic
7933 for last (8th) frame which ends the movement animation */
7934 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7936 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7937 graphic = (direction == MV_NONE ?
7938 el_act2img(effective_element, action) :
7939 el_act_dir2img(effective_element, action, direction));
7941 g = &graphic_info[graphic];
7944 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7948 else if (action_moving)
7950 boolean is_backside = object_mapping[tile].is_backside;
7954 int direction = object_mapping[tile].direction;
7955 int move_dir = (action_falling ? MV_DOWN : direction);
7960 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7961 if (g->double_movement && frame_em == 0)
7965 if (move_dir == MV_LEFT)
7966 GfxFrame[x - 1][y] = GfxFrame[x][y];
7967 else if (move_dir == MV_RIGHT)
7968 GfxFrame[x + 1][y] = GfxFrame[x][y];
7969 else if (move_dir == MV_UP)
7970 GfxFrame[x][y - 1] = GfxFrame[x][y];
7971 else if (move_dir == MV_DOWN)
7972 GfxFrame[x][y + 1] = GfxFrame[x][y];
7979 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7980 if (tile == Xsand_stonesand_quickout_1 ||
7981 tile == Xsand_stonesand_quickout_2)
7985 if (graphic_info[graphic].anim_global_sync)
7986 sync_frame = FrameCounter;
7987 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7988 sync_frame = GfxFrame[x][y];
7990 sync_frame = 0; /* playfield border (pseudo steel) */
7992 SetRandomAnimationValue(x, y);
7994 int frame = getAnimationFrame(g->anim_frames,
7997 g->anim_start_frame,
8000 g_em->unique_identifier =
8001 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
8004 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
8005 int tile, int frame_em, int x, int y)
8007 int action = object_mapping[tile].action;
8008 int direction = object_mapping[tile].direction;
8009 boolean is_backside = object_mapping[tile].is_backside;
8010 int effective_element = get_effective_element_EM(tile, frame_em);
8011 int effective_action = action;
8012 int graphic = (direction == MV_NONE ?
8013 el_act2img(effective_element, effective_action) :
8014 el_act_dir2img(effective_element, effective_action,
8016 int crumbled = (direction == MV_NONE ?
8017 el_act2crm(effective_element, effective_action) :
8018 el_act_dir2crm(effective_element, effective_action,
8020 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8021 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8022 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8023 struct GraphicInfo *g = &graphic_info[graphic];
8026 /* special case: graphic uses "2nd movement tile" and has defined
8027 7 frames for movement animation (or less) => use default graphic
8028 for last (8th) frame which ends the movement animation */
8029 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
8031 effective_action = ACTION_DEFAULT;
8032 graphic = (direction == MV_NONE ?
8033 el_act2img(effective_element, effective_action) :
8034 el_act_dir2img(effective_element, effective_action,
8036 crumbled = (direction == MV_NONE ?
8037 el_act2crm(effective_element, effective_action) :
8038 el_act_dir2crm(effective_element, effective_action,
8041 g = &graphic_info[graphic];
8044 if (graphic_info[graphic].anim_global_sync)
8045 sync_frame = FrameCounter;
8046 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8047 sync_frame = GfxFrame[x][y];
8049 sync_frame = 0; /* playfield border (pseudo steel) */
8051 SetRandomAnimationValue(x, y);
8053 int frame = getAnimationFrame(g->anim_frames,
8056 g->anim_start_frame,
8059 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8060 g->double_movement && is_backside);
8062 /* (updating the "crumbled" graphic definitions is probably not really needed,
8063 as animations for crumbled graphics can't be longer than one EMC cycle) */
8064 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8068 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8069 int player_nr, int anim, int frame_em)
8071 int element = player_mapping[player_nr][anim].element_rnd;
8072 int action = player_mapping[player_nr][anim].action;
8073 int direction = player_mapping[player_nr][anim].direction;
8074 int graphic = (direction == MV_NONE ?
8075 el_act2img(element, action) :
8076 el_act_dir2img(element, action, direction));
8077 struct GraphicInfo *g = &graphic_info[graphic];
8080 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8082 stored_player[player_nr].StepFrame = frame_em;
8084 sync_frame = stored_player[player_nr].Frame;
8086 int frame = getAnimationFrame(g->anim_frames,
8089 g->anim_start_frame,
8092 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8093 &g_em->src_x, &g_em->src_y, FALSE);
8096 void InitGraphicInfo_EM(void)
8101 int num_em_gfx_errors = 0;
8103 if (graphic_info_em_object[0][0].bitmap == NULL)
8105 /* EM graphics not yet initialized in em_open_all() */
8110 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8113 /* always start with reliable default values */
8114 for (i = 0; i < TILE_MAX; i++)
8116 object_mapping[i].element_rnd = EL_UNKNOWN;
8117 object_mapping[i].is_backside = FALSE;
8118 object_mapping[i].action = ACTION_DEFAULT;
8119 object_mapping[i].direction = MV_NONE;
8122 /* always start with reliable default values */
8123 for (p = 0; p < MAX_PLAYERS; p++)
8125 for (i = 0; i < SPR_MAX; i++)
8127 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8128 player_mapping[p][i].action = ACTION_DEFAULT;
8129 player_mapping[p][i].direction = MV_NONE;
8133 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8135 int e = em_object_mapping_list[i].element_em;
8137 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8138 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8140 if (em_object_mapping_list[i].action != -1)
8141 object_mapping[e].action = em_object_mapping_list[i].action;
8143 if (em_object_mapping_list[i].direction != -1)
8144 object_mapping[e].direction =
8145 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8148 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8150 int a = em_player_mapping_list[i].action_em;
8151 int p = em_player_mapping_list[i].player_nr;
8153 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8155 if (em_player_mapping_list[i].action != -1)
8156 player_mapping[p][a].action = em_player_mapping_list[i].action;
8158 if (em_player_mapping_list[i].direction != -1)
8159 player_mapping[p][a].direction =
8160 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8163 for (i = 0; i < TILE_MAX; i++)
8165 int element = object_mapping[i].element_rnd;
8166 int action = object_mapping[i].action;
8167 int direction = object_mapping[i].direction;
8168 boolean is_backside = object_mapping[i].is_backside;
8169 boolean action_exploding = ((action == ACTION_EXPLODING ||
8170 action == ACTION_SMASHED_BY_ROCK ||
8171 action == ACTION_SMASHED_BY_SPRING) &&
8172 element != EL_DIAMOND);
8173 boolean action_active = (action == ACTION_ACTIVE);
8174 boolean action_other = (action == ACTION_OTHER);
8176 for (j = 0; j < 8; j++)
8178 int effective_element = get_effective_element_EM(i, j);
8179 int effective_action = (j < 7 ? action :
8180 i == Xdrip_stretch ? action :
8181 i == Xdrip_stretchB ? action :
8182 i == Ydrip_s1 ? action :
8183 i == Ydrip_s1B ? action :
8184 i == Xball_1B ? action :
8185 i == Xball_2 ? action :
8186 i == Xball_2B ? action :
8187 i == Yball_eat ? action :
8188 i == Ykey_1_eat ? action :
8189 i == Ykey_2_eat ? action :
8190 i == Ykey_3_eat ? action :
8191 i == Ykey_4_eat ? action :
8192 i == Ykey_5_eat ? action :
8193 i == Ykey_6_eat ? action :
8194 i == Ykey_7_eat ? action :
8195 i == Ykey_8_eat ? action :
8196 i == Ylenses_eat ? action :
8197 i == Ymagnify_eat ? action :
8198 i == Ygrass_eat ? action :
8199 i == Ydirt_eat ? action :
8200 i == Xsand_stonein_1 ? action :
8201 i == Xsand_stonein_2 ? action :
8202 i == Xsand_stonein_3 ? action :
8203 i == Xsand_stonein_4 ? action :
8204 i == Xsand_stoneout_1 ? action :
8205 i == Xsand_stoneout_2 ? action :
8206 i == Xboom_android ? ACTION_EXPLODING :
8207 action_exploding ? ACTION_EXPLODING :
8208 action_active ? action :
8209 action_other ? action :
8211 int graphic = (el_act_dir2img(effective_element, effective_action,
8213 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8215 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8216 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8217 boolean has_action_graphics = (graphic != base_graphic);
8218 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8219 struct GraphicInfo *g = &graphic_info[graphic];
8220 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8223 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
8224 boolean special_animation = (action != ACTION_DEFAULT &&
8225 g->anim_frames == 3 &&
8226 g->anim_delay == 2 &&
8227 g->anim_mode & ANIM_LINEAR);
8228 int sync_frame = (i == Xdrip_stretch ? 7 :
8229 i == Xdrip_stretchB ? 7 :
8230 i == Ydrip_s2 ? j + 8 :
8231 i == Ydrip_s2B ? j + 8 :
8240 i == Xfake_acid_1 ? 0 :
8241 i == Xfake_acid_2 ? 10 :
8242 i == Xfake_acid_3 ? 20 :
8243 i == Xfake_acid_4 ? 30 :
8244 i == Xfake_acid_5 ? 40 :
8245 i == Xfake_acid_6 ? 50 :
8246 i == Xfake_acid_7 ? 60 :
8247 i == Xfake_acid_8 ? 70 :
8249 i == Xball_2B ? j + 8 :
8250 i == Yball_eat ? j + 1 :
8251 i == Ykey_1_eat ? j + 1 :
8252 i == Ykey_2_eat ? j + 1 :
8253 i == Ykey_3_eat ? j + 1 :
8254 i == Ykey_4_eat ? j + 1 :
8255 i == Ykey_5_eat ? j + 1 :
8256 i == Ykey_6_eat ? j + 1 :
8257 i == Ykey_7_eat ? j + 1 :
8258 i == Ykey_8_eat ? j + 1 :
8259 i == Ylenses_eat ? j + 1 :
8260 i == Ymagnify_eat ? j + 1 :
8261 i == Ygrass_eat ? j + 1 :
8262 i == Ydirt_eat ? j + 1 :
8263 i == Xamoeba_1 ? 0 :
8264 i == Xamoeba_2 ? 1 :
8265 i == Xamoeba_3 ? 2 :
8266 i == Xamoeba_4 ? 3 :
8267 i == Xamoeba_5 ? 0 :
8268 i == Xamoeba_6 ? 1 :
8269 i == Xamoeba_7 ? 2 :
8270 i == Xamoeba_8 ? 3 :
8271 i == Xexit_2 ? j + 8 :
8272 i == Xexit_3 ? j + 16 :
8273 i == Xdynamite_1 ? 0 :
8274 i == Xdynamite_2 ? 8 :
8275 i == Xdynamite_3 ? 16 :
8276 i == Xdynamite_4 ? 24 :
8277 i == Xsand_stonein_1 ? j + 1 :
8278 i == Xsand_stonein_2 ? j + 9 :
8279 i == Xsand_stonein_3 ? j + 17 :
8280 i == Xsand_stonein_4 ? j + 25 :
8281 i == Xsand_stoneout_1 && j == 0 ? 0 :
8282 i == Xsand_stoneout_1 && j == 1 ? 0 :
8283 i == Xsand_stoneout_1 && j == 2 ? 1 :
8284 i == Xsand_stoneout_1 && j == 3 ? 2 :
8285 i == Xsand_stoneout_1 && j == 4 ? 2 :
8286 i == Xsand_stoneout_1 && j == 5 ? 3 :
8287 i == Xsand_stoneout_1 && j == 6 ? 4 :
8288 i == Xsand_stoneout_1 && j == 7 ? 4 :
8289 i == Xsand_stoneout_2 && j == 0 ? 5 :
8290 i == Xsand_stoneout_2 && j == 1 ? 6 :
8291 i == Xsand_stoneout_2 && j == 2 ? 7 :
8292 i == Xsand_stoneout_2 && j == 3 ? 8 :
8293 i == Xsand_stoneout_2 && j == 4 ? 9 :
8294 i == Xsand_stoneout_2 && j == 5 ? 11 :
8295 i == Xsand_stoneout_2 && j == 6 ? 13 :
8296 i == Xsand_stoneout_2 && j == 7 ? 15 :
8297 i == Xboom_bug && j == 1 ? 2 :
8298 i == Xboom_bug && j == 2 ? 2 :
8299 i == Xboom_bug && j == 3 ? 4 :
8300 i == Xboom_bug && j == 4 ? 4 :
8301 i == Xboom_bug && j == 5 ? 2 :
8302 i == Xboom_bug && j == 6 ? 2 :
8303 i == Xboom_bug && j == 7 ? 0 :
8304 i == Xboom_bomb && j == 1 ? 2 :
8305 i == Xboom_bomb && j == 2 ? 2 :
8306 i == Xboom_bomb && j == 3 ? 4 :
8307 i == Xboom_bomb && j == 4 ? 4 :
8308 i == Xboom_bomb && j == 5 ? 2 :
8309 i == Xboom_bomb && j == 6 ? 2 :
8310 i == Xboom_bomb && j == 7 ? 0 :
8311 i == Xboom_android && j == 7 ? 6 :
8312 i == Xboom_1 && j == 1 ? 2 :
8313 i == Xboom_1 && j == 2 ? 2 :
8314 i == Xboom_1 && j == 3 ? 4 :
8315 i == Xboom_1 && j == 4 ? 4 :
8316 i == Xboom_1 && j == 5 ? 6 :
8317 i == Xboom_1 && j == 6 ? 6 :
8318 i == Xboom_1 && j == 7 ? 8 :
8319 i == Xboom_2 && j == 0 ? 8 :
8320 i == Xboom_2 && j == 1 ? 8 :
8321 i == Xboom_2 && j == 2 ? 10 :
8322 i == Xboom_2 && j == 3 ? 10 :
8323 i == Xboom_2 && j == 4 ? 10 :
8324 i == Xboom_2 && j == 5 ? 12 :
8325 i == Xboom_2 && j == 6 ? 12 :
8326 i == Xboom_2 && j == 7 ? 12 :
8327 special_animation && j == 4 ? 3 :
8328 effective_action != action ? 0 :
8332 Bitmap *debug_bitmap = g_em->bitmap;
8333 int debug_src_x = g_em->src_x;
8334 int debug_src_y = g_em->src_y;
8337 int frame = getAnimationFrame(g->anim_frames,
8340 g->anim_start_frame,
8343 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8344 g->double_movement && is_backside);
8346 g_em->bitmap = src_bitmap;
8347 g_em->src_x = src_x;
8348 g_em->src_y = src_y;
8349 g_em->src_offset_x = 0;
8350 g_em->src_offset_y = 0;
8351 g_em->dst_offset_x = 0;
8352 g_em->dst_offset_y = 0;
8353 g_em->width = TILEX;
8354 g_em->height = TILEY;
8356 g_em->preserve_background = FALSE;
8358 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8361 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8362 effective_action == ACTION_MOVING ||
8363 effective_action == ACTION_PUSHING ||
8364 effective_action == ACTION_EATING)) ||
8365 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8366 effective_action == ACTION_EMPTYING)))
8369 (effective_action == ACTION_FALLING ||
8370 effective_action == ACTION_FILLING ||
8371 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8372 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8373 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8374 int num_steps = (i == Ydrip_s1 ? 16 :
8375 i == Ydrip_s1B ? 16 :
8376 i == Ydrip_s2 ? 16 :
8377 i == Ydrip_s2B ? 16 :
8378 i == Xsand_stonein_1 ? 32 :
8379 i == Xsand_stonein_2 ? 32 :
8380 i == Xsand_stonein_3 ? 32 :
8381 i == Xsand_stonein_4 ? 32 :
8382 i == Xsand_stoneout_1 ? 16 :
8383 i == Xsand_stoneout_2 ? 16 : 8);
8384 int cx = ABS(dx) * (TILEX / num_steps);
8385 int cy = ABS(dy) * (TILEY / num_steps);
8386 int step_frame = (i == Ydrip_s2 ? j + 8 :
8387 i == Ydrip_s2B ? j + 8 :
8388 i == Xsand_stonein_2 ? j + 8 :
8389 i == Xsand_stonein_3 ? j + 16 :
8390 i == Xsand_stonein_4 ? j + 24 :
8391 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8392 int step = (is_backside ? step_frame : num_steps - step_frame);
8394 if (is_backside) /* tile where movement starts */
8396 if (dx < 0 || dy < 0)
8398 g_em->src_offset_x = cx * step;
8399 g_em->src_offset_y = cy * step;
8403 g_em->dst_offset_x = cx * step;
8404 g_em->dst_offset_y = cy * step;
8407 else /* tile where movement ends */
8409 if (dx < 0 || dy < 0)
8411 g_em->dst_offset_x = cx * step;
8412 g_em->dst_offset_y = cy * step;
8416 g_em->src_offset_x = cx * step;
8417 g_em->src_offset_y = cy * step;
8421 g_em->width = TILEX - cx * step;
8422 g_em->height = TILEY - cy * step;
8425 /* create unique graphic identifier to decide if tile must be redrawn */
8426 /* bit 31 - 16 (16 bit): EM style graphic
8427 bit 15 - 12 ( 4 bit): EM style frame
8428 bit 11 - 6 ( 6 bit): graphic width
8429 bit 5 - 0 ( 6 bit): graphic height */
8430 g_em->unique_identifier =
8431 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8435 /* skip check for EMC elements not contained in original EMC artwork */
8436 if (element == EL_EMC_FAKE_ACID)
8439 if (g_em->bitmap != debug_bitmap ||
8440 g_em->src_x != debug_src_x ||
8441 g_em->src_y != debug_src_y ||
8442 g_em->src_offset_x != 0 ||
8443 g_em->src_offset_y != 0 ||
8444 g_em->dst_offset_x != 0 ||
8445 g_em->dst_offset_y != 0 ||
8446 g_em->width != TILEX ||
8447 g_em->height != TILEY)
8449 static int last_i = -1;
8457 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8458 i, element, element_info[element].token_name,
8459 element_action_info[effective_action].suffix, direction);
8461 if (element != effective_element)
8462 printf(" [%d ('%s')]",
8464 element_info[effective_element].token_name);
8468 if (g_em->bitmap != debug_bitmap)
8469 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8470 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8472 if (g_em->src_x != debug_src_x ||
8473 g_em->src_y != debug_src_y)
8474 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8475 j, (is_backside ? 'B' : 'F'),
8476 g_em->src_x, g_em->src_y,
8477 g_em->src_x / 32, g_em->src_y / 32,
8478 debug_src_x, debug_src_y,
8479 debug_src_x / 32, debug_src_y / 32);
8481 if (g_em->src_offset_x != 0 ||
8482 g_em->src_offset_y != 0 ||
8483 g_em->dst_offset_x != 0 ||
8484 g_em->dst_offset_y != 0)
8485 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8487 g_em->src_offset_x, g_em->src_offset_y,
8488 g_em->dst_offset_x, g_em->dst_offset_y);
8490 if (g_em->width != TILEX ||
8491 g_em->height != TILEY)
8492 printf(" %d (%d): size %d,%d should be %d,%d\n",
8494 g_em->width, g_em->height, TILEX, TILEY);
8496 num_em_gfx_errors++;
8503 for (i = 0; i < TILE_MAX; i++)
8505 for (j = 0; j < 8; j++)
8507 int element = object_mapping[i].element_rnd;
8508 int action = object_mapping[i].action;
8509 int direction = object_mapping[i].direction;
8510 boolean is_backside = object_mapping[i].is_backside;
8511 int graphic_action = el_act_dir2img(element, action, direction);
8512 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8514 if ((action == ACTION_SMASHED_BY_ROCK ||
8515 action == ACTION_SMASHED_BY_SPRING ||
8516 action == ACTION_EATING) &&
8517 graphic_action == graphic_default)
8519 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8520 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8521 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8522 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8525 /* no separate animation for "smashed by rock" -- use rock instead */
8526 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8527 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8529 g_em->bitmap = g_xx->bitmap;
8530 g_em->src_x = g_xx->src_x;
8531 g_em->src_y = g_xx->src_y;
8532 g_em->src_offset_x = g_xx->src_offset_x;
8533 g_em->src_offset_y = g_xx->src_offset_y;
8534 g_em->dst_offset_x = g_xx->dst_offset_x;
8535 g_em->dst_offset_y = g_xx->dst_offset_y;
8536 g_em->width = g_xx->width;
8537 g_em->height = g_xx->height;
8538 g_em->unique_identifier = g_xx->unique_identifier;
8541 g_em->preserve_background = TRUE;
8546 for (p = 0; p < MAX_PLAYERS; p++)
8548 for (i = 0; i < SPR_MAX; i++)
8550 int element = player_mapping[p][i].element_rnd;
8551 int action = player_mapping[p][i].action;
8552 int direction = player_mapping[p][i].direction;
8554 for (j = 0; j < 8; j++)
8556 int effective_element = element;
8557 int effective_action = action;
8558 int graphic = (direction == MV_NONE ?
8559 el_act2img(effective_element, effective_action) :
8560 el_act_dir2img(effective_element, effective_action,
8562 struct GraphicInfo *g = &graphic_info[graphic];
8563 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8569 Bitmap *debug_bitmap = g_em->bitmap;
8570 int debug_src_x = g_em->src_x;
8571 int debug_src_y = g_em->src_y;
8574 int frame = getAnimationFrame(g->anim_frames,
8577 g->anim_start_frame,
8580 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8582 g_em->bitmap = src_bitmap;
8583 g_em->src_x = src_x;
8584 g_em->src_y = src_y;
8585 g_em->src_offset_x = 0;
8586 g_em->src_offset_y = 0;
8587 g_em->dst_offset_x = 0;
8588 g_em->dst_offset_y = 0;
8589 g_em->width = TILEX;
8590 g_em->height = TILEY;
8594 /* skip check for EMC elements not contained in original EMC artwork */
8595 if (element == EL_PLAYER_3 ||
8596 element == EL_PLAYER_4)
8599 if (g_em->bitmap != debug_bitmap ||
8600 g_em->src_x != debug_src_x ||
8601 g_em->src_y != debug_src_y)
8603 static int last_i = -1;
8611 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
8612 p, i, element, element_info[element].token_name,
8613 element_action_info[effective_action].suffix, direction);
8615 if (element != effective_element)
8616 printf(" [%d ('%s')]",
8618 element_info[effective_element].token_name);
8622 if (g_em->bitmap != debug_bitmap)
8623 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
8624 j, (int)(g_em->bitmap), (int)(debug_bitmap));
8626 if (g_em->src_x != debug_src_x ||
8627 g_em->src_y != debug_src_y)
8628 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8630 g_em->src_x, g_em->src_y,
8631 g_em->src_x / 32, g_em->src_y / 32,
8632 debug_src_x, debug_src_y,
8633 debug_src_x / 32, debug_src_y / 32);
8635 num_em_gfx_errors++;
8645 printf("::: [%d errors found]\n", num_em_gfx_errors);
8651 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
8652 boolean any_player_moving,
8653 boolean any_player_snapping,
8654 boolean any_player_dropping)
8656 if (frame == 0 && !any_player_dropping)
8658 if (!local_player->was_waiting)
8660 if (!CheckSaveEngineSnapshotToList())
8663 local_player->was_waiting = TRUE;
8666 else if (any_player_moving || any_player_snapping || any_player_dropping)
8668 local_player->was_waiting = FALSE;
8672 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8673 boolean murphy_is_dropping)
8675 if (murphy_is_waiting)
8677 if (!local_player->was_waiting)
8679 if (!CheckSaveEngineSnapshotToList())
8682 local_player->was_waiting = TRUE;
8687 local_player->was_waiting = FALSE;
8691 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8692 boolean any_player_moving,
8693 boolean any_player_snapping,
8694 boolean any_player_dropping)
8696 if (tape.single_step && tape.recording && !tape.pausing)
8697 if (frame == 0 && !any_player_dropping)
8698 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8700 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8701 any_player_snapping, any_player_dropping);
8704 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8705 boolean murphy_is_dropping)
8707 boolean murphy_starts_dropping = FALSE;
8710 for (i = 0; i < MAX_PLAYERS; i++)
8711 if (stored_player[i].force_dropping)
8712 murphy_starts_dropping = TRUE;
8714 if (tape.single_step && tape.recording && !tape.pausing)
8715 if (murphy_is_waiting && !murphy_starts_dropping)
8716 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8718 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8721 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8722 int graphic, int sync_frame, int x, int y)
8724 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8726 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8729 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8731 return (IS_NEXT_FRAME(sync_frame, graphic));
8734 int getGraphicInfo_Delay(int graphic)
8736 return graphic_info[graphic].anim_delay;
8739 void PlayMenuSoundExt(int sound)
8741 if (sound == SND_UNDEFINED)
8744 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8745 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8748 if (IS_LOOP_SOUND(sound))
8749 PlaySoundLoop(sound);
8754 void PlayMenuSound()
8756 PlayMenuSoundExt(menu.sound[game_status]);
8759 void PlayMenuSoundStereo(int sound, int stereo_position)
8761 if (sound == SND_UNDEFINED)
8764 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8765 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8768 if (IS_LOOP_SOUND(sound))
8769 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8771 PlaySoundStereo(sound, stereo_position);
8774 void PlayMenuSoundIfLoopExt(int sound)
8776 if (sound == SND_UNDEFINED)
8779 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8780 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8783 if (IS_LOOP_SOUND(sound))
8784 PlaySoundLoop(sound);
8787 void PlayMenuSoundIfLoop()
8789 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8792 void PlayMenuMusicExt(int music)
8794 if (music == MUS_UNDEFINED)
8797 if (!setup.sound_music)
8803 void PlayMenuMusic()
8805 char *curr_music = getCurrentlyPlayingMusicFilename();
8806 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8808 if (!strEqual(curr_music, next_music))
8809 PlayMenuMusicExt(menu.music[game_status]);
8812 void PlayMenuSoundsAndMusic()
8818 static void FadeMenuSounds()
8823 static void FadeMenuMusic()
8825 char *curr_music = getCurrentlyPlayingMusicFilename();
8826 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8828 if (!strEqual(curr_music, next_music))
8832 void FadeMenuSoundsAndMusic()
8838 void PlaySoundActivating()
8841 PlaySound(SND_MENU_ITEM_ACTIVATING);
8845 void PlaySoundSelecting()
8848 PlaySound(SND_MENU_ITEM_SELECTING);
8852 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8854 boolean change_fullscreen = (setup.fullscreen !=
8855 video.fullscreen_enabled);
8856 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8857 setup.window_scaling_percent !=
8858 video.window_scaling_percent);
8860 if (change_window_scaling_percent && video.fullscreen_enabled)
8863 if (!change_window_scaling_percent && !video.fullscreen_available)
8866 #if defined(TARGET_SDL2)
8867 if (change_window_scaling_percent)
8869 SDLSetWindowScaling(setup.window_scaling_percent);
8873 else if (change_fullscreen)
8875 SDLSetWindowFullscreen(setup.fullscreen);
8877 /* set setup value according to successfully changed fullscreen mode */
8878 setup.fullscreen = video.fullscreen_enabled;
8884 if (change_fullscreen ||
8885 change_window_scaling_percent)
8887 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8889 /* save backbuffer content which gets lost when toggling fullscreen mode */
8890 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8892 if (change_window_scaling_percent)
8894 /* keep window mode, but change window scaling */
8895 video.fullscreen_enabled = TRUE; /* force new window scaling */
8898 /* toggle fullscreen */
8899 ChangeVideoModeIfNeeded(setup.fullscreen);
8901 /* set setup value according to successfully changed fullscreen mode */
8902 setup.fullscreen = video.fullscreen_enabled;
8904 /* restore backbuffer content from temporary backbuffer backup bitmap */
8905 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8907 FreeBitmap(tmp_backbuffer);
8909 /* update visible window/screen */
8910 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8914 void JoinRectangles(int *x, int *y, int *width, int *height,
8915 int x2, int y2, int width2, int height2)
8917 // do not join with "off-screen" rectangle
8918 if (x2 == -1 || y2 == -1)
8923 *width = MAX(*width, width2);
8924 *height = MAX(*height, height2);
8927 void SetAnimStatus(int anim_status_new)
8929 if (anim_status_new == GAME_MODE_MAIN)
8930 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
8931 else if (anim_status_new == GAME_MODE_SCORES)
8932 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
8934 global.anim_status_next = anim_status_new;
8936 // directly set screen modes that are entered without fading
8937 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
8938 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
8939 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
8940 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
8941 global.anim_status = global.anim_status_next;
8944 void SetGameStatus(int game_status_new)
8946 if (game_status_new != game_status)
8947 game_status_last_screen = game_status;
8949 game_status = game_status_new;
8951 SetAnimStatus(game_status_new);
8954 void SetFontStatus(int game_status_new)
8956 static int last_game_status = -1;
8958 if (game_status_new != -1)
8960 // set game status for font use after storing last game status
8961 last_game_status = game_status;
8962 game_status = game_status_new;
8966 // reset game status after font use from last stored game status
8967 game_status = last_game_status;
8971 void ResetFontStatus()
8976 boolean CheckIfPlayfieldViewportHasChanged()
8978 // if game status has not changed, playfield viewport has not changed either
8979 if (game_status == game_status_last)
8982 // check if playfield viewport has changed with current game status
8983 struct RectWithBorder *vp_playfield = &viewport.playfield[game_status];
8984 int new_real_sx = vp_playfield->x;
8985 int new_real_sy = vp_playfield->y;
8986 int new_full_sxsize = vp_playfield->width;
8987 int new_full_sysize = vp_playfield->height;
8989 return (new_real_sx != REAL_SX ||
8990 new_real_sy != REAL_SY ||
8991 new_full_sxsize != FULL_SXSIZE ||
8992 new_full_sysize != FULL_SYSIZE);
8995 boolean CheckIfGlobalBorderOrPlayfieldViewportHasChanged()
8997 return (CheckIfGlobalBorderHasChanged() ||
8998 CheckIfPlayfieldViewportHasChanged());
9001 void ChangeViewportPropertiesIfNeeded()
9003 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
9004 FALSE : setup.small_game_graphics);
9005 int gfx_game_mode = game_status;
9006 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
9008 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
9009 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
9010 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
9011 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
9012 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
9013 int new_win_xsize = vp_window->width;
9014 int new_win_ysize = vp_window->height;
9015 int border_size = vp_playfield->border_size;
9016 int new_sx = vp_playfield->x + border_size;
9017 int new_sy = vp_playfield->y + border_size;
9018 int new_sxsize = vp_playfield->width - 2 * border_size;
9019 int new_sysize = vp_playfield->height - 2 * border_size;
9020 int new_real_sx = vp_playfield->x;
9021 int new_real_sy = vp_playfield->y;
9022 int new_full_sxsize = vp_playfield->width;
9023 int new_full_sysize = vp_playfield->height;
9024 int new_dx = vp_door_1->x;
9025 int new_dy = vp_door_1->y;
9026 int new_dxsize = vp_door_1->width;
9027 int new_dysize = vp_door_1->height;
9028 int new_vx = vp_door_2->x;
9029 int new_vy = vp_door_2->y;
9030 int new_vxsize = vp_door_2->width;
9031 int new_vysize = vp_door_2->height;
9032 int new_ex = vp_door_3->x;
9033 int new_ey = vp_door_3->y;
9034 int new_exsize = vp_door_3->width;
9035 int new_eysize = vp_door_3->height;
9036 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
9037 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
9038 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
9039 int new_scr_fieldx = new_sxsize / tilesize;
9040 int new_scr_fieldy = new_sysize / tilesize;
9041 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
9042 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
9043 boolean init_gfx_buffers = FALSE;
9044 boolean init_video_buffer = FALSE;
9045 boolean init_gadgets_and_anims = FALSE;
9046 boolean init_em_graphics = FALSE;
9048 if (new_win_xsize != WIN_XSIZE ||
9049 new_win_ysize != WIN_YSIZE)
9051 WIN_XSIZE = new_win_xsize;
9052 WIN_YSIZE = new_win_ysize;
9054 init_video_buffer = TRUE;
9055 init_gfx_buffers = TRUE;
9056 init_gadgets_and_anims = TRUE;
9058 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
9061 if (new_scr_fieldx != SCR_FIELDX ||
9062 new_scr_fieldy != SCR_FIELDY)
9064 /* this always toggles between MAIN and GAME when using small tile size */
9066 SCR_FIELDX = new_scr_fieldx;
9067 SCR_FIELDY = new_scr_fieldy;
9069 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9080 new_sxsize != SXSIZE ||
9081 new_sysize != SYSIZE ||
9082 new_dxsize != DXSIZE ||
9083 new_dysize != DYSIZE ||
9084 new_vxsize != VXSIZE ||
9085 new_vysize != VYSIZE ||
9086 new_exsize != EXSIZE ||
9087 new_eysize != EYSIZE ||
9088 new_real_sx != REAL_SX ||
9089 new_real_sy != REAL_SY ||
9090 new_full_sxsize != FULL_SXSIZE ||
9091 new_full_sysize != FULL_SYSIZE ||
9092 new_tilesize_var != TILESIZE_VAR
9095 // ------------------------------------------------------------------------
9096 // determine next fading area for changed viewport definitions
9097 // ------------------------------------------------------------------------
9099 // start with current playfield area (default fading area)
9102 FADE_SXSIZE = FULL_SXSIZE;
9103 FADE_SYSIZE = FULL_SYSIZE;
9105 // add new playfield area if position or size has changed
9106 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9107 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9109 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9110 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9113 // add current and new door 1 area if position or size has changed
9114 if (new_dx != DX || new_dy != DY ||
9115 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9117 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9118 DX, DY, DXSIZE, DYSIZE);
9119 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9120 new_dx, new_dy, new_dxsize, new_dysize);
9123 // add current and new door 2 area if position or size has changed
9124 if (new_dx != VX || new_dy != VY ||
9125 new_dxsize != VXSIZE || new_dysize != VYSIZE)
9127 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9128 VX, VY, VXSIZE, VYSIZE);
9129 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9130 new_vx, new_vy, new_vxsize, new_vysize);
9133 // ------------------------------------------------------------------------
9134 // handle changed tile size
9135 // ------------------------------------------------------------------------
9137 if (new_tilesize_var != TILESIZE_VAR)
9139 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9141 // changing tile size invalidates scroll values of engine snapshots
9142 FreeEngineSnapshotSingle();
9144 // changing tile size requires update of graphic mapping for EM engine
9145 init_em_graphics = TRUE;
9156 SXSIZE = new_sxsize;
9157 SYSIZE = new_sysize;
9158 DXSIZE = new_dxsize;
9159 DYSIZE = new_dysize;
9160 VXSIZE = new_vxsize;
9161 VYSIZE = new_vysize;
9162 EXSIZE = new_exsize;
9163 EYSIZE = new_eysize;
9164 REAL_SX = new_real_sx;
9165 REAL_SY = new_real_sy;
9166 FULL_SXSIZE = new_full_sxsize;
9167 FULL_SYSIZE = new_full_sysize;
9168 TILESIZE_VAR = new_tilesize_var;
9170 init_gfx_buffers = TRUE;
9171 init_gadgets_and_anims = TRUE;
9173 // printf("::: viewports: init_gfx_buffers\n");
9174 // printf("::: viewports: init_gadgets_and_anims\n");
9177 if (init_gfx_buffers)
9179 // printf("::: init_gfx_buffers\n");
9181 SCR_FIELDX = new_scr_fieldx_buffers;
9182 SCR_FIELDY = new_scr_fieldy_buffers;
9186 SCR_FIELDX = new_scr_fieldx;
9187 SCR_FIELDY = new_scr_fieldy;
9189 SetDrawDeactivationMask(REDRAW_NONE);
9190 SetDrawBackgroundMask(REDRAW_FIELD);
9193 if (init_video_buffer)
9195 // printf("::: init_video_buffer\n");
9197 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9198 InitImageTextures();
9201 if (init_gadgets_and_anims)
9203 // printf("::: init_gadgets_and_anims\n");
9206 InitGlobalAnimations();
9209 if (init_em_graphics)
9211 InitGraphicInfo_EM();