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)
363 int level_xsize = level.native_mm_level->fieldx;
364 int full_xsize = level_xsize * TILESIZE_VAR;
366 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
369 int lx = px / TILESIZE_VAR;
374 static int getLevelFromScreenY_MM(int sy)
376 int level_ysize = level.native_mm_level->fieldy;
377 int full_ysize = level_ysize * TILESIZE_VAR;
379 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
382 int ly = py / TILESIZE_VAR;
387 int getLevelFromScreenX(int x)
389 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
390 return getLevelFromScreenX_EM(x);
391 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
392 return getLevelFromScreenX_SP(x);
393 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
394 return getLevelFromScreenX_MM(x);
396 return getLevelFromScreenX_RND(x);
399 int getLevelFromScreenY(int y)
401 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
402 return getLevelFromScreenY_EM(y);
403 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
404 return getLevelFromScreenY_SP(y);
405 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
406 return getLevelFromScreenY_MM(y);
408 return getLevelFromScreenY_RND(y);
411 void DumpTile(int x, int y)
416 printf_line("-", 79);
417 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
418 printf_line("-", 79);
420 if (!IN_LEV_FIELD(x, y))
422 printf("(not in level field)\n");
428 printf(" Feld: %d\t['%s']\n", Feld[x][y],
429 element_info[Feld[x][y]].token_name);
430 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
431 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
432 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
433 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
434 printf(" MovPos: %d\n", MovPos[x][y]);
435 printf(" MovDir: %d\n", MovDir[x][y]);
436 printf(" MovDelay: %d\n", MovDelay[x][y]);
437 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
438 printf(" CustomValue: %d\n", CustomValue[x][y]);
439 printf(" GfxElement: %d\n", GfxElement[x][y]);
440 printf(" GfxAction: %d\n", GfxAction[x][y]);
441 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
442 printf(" Player x/y: %d, %d\n", local_player->jx, local_player->jy);
446 void DumpTileFromScreen(int sx, int sy)
448 int lx = getLevelFromScreenX(sx);
449 int ly = getLevelFromScreenY(sy);
454 void SetDrawtoField(int mode)
456 if (mode == DRAW_TO_FIELDBUFFER)
462 BX2 = SCR_FIELDX + 1;
463 BY2 = SCR_FIELDY + 1;
465 drawto_field = fieldbuffer;
467 else /* DRAW_TO_BACKBUFFER */
473 BX2 = SCR_FIELDX - 1;
474 BY2 = SCR_FIELDY - 1;
476 drawto_field = backbuffer;
480 static void RedrawPlayfield_RND()
482 if (game.envelope_active)
485 DrawLevel(REDRAW_ALL);
489 void RedrawPlayfield()
491 if (game_status != GAME_MODE_PLAYING)
494 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
495 RedrawPlayfield_EM(TRUE);
496 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
497 RedrawPlayfield_SP(TRUE);
498 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
499 RedrawPlayfield_MM();
500 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
501 RedrawPlayfield_RND();
503 BlitScreenToBitmap(backbuffer);
505 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
509 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
512 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
513 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
515 if (x == -1 && y == -1)
518 if (draw_target == DRAW_TO_SCREEN)
519 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
521 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
524 static void DrawMaskedBorderExt_FIELD(int draw_target)
526 if (global.border_status >= GAME_MODE_MAIN &&
527 global.border_status <= GAME_MODE_PLAYING &&
528 border.draw_masked[global.border_status])
529 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
533 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
535 // when drawing to backbuffer, never draw border over open doors
536 if (draw_target == DRAW_TO_BACKBUFFER &&
537 (GetDoorState() & DOOR_OPEN_1))
540 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
541 (global.border_status != GAME_MODE_EDITOR ||
542 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
543 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
546 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
548 // when drawing to backbuffer, never draw border over open doors
549 if (draw_target == DRAW_TO_BACKBUFFER &&
550 (GetDoorState() & DOOR_OPEN_2))
553 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
554 global.border_status != GAME_MODE_EDITOR)
555 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
558 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
560 /* currently not available */
563 static void DrawMaskedBorderExt_ALL(int draw_target)
565 DrawMaskedBorderExt_FIELD(draw_target);
566 DrawMaskedBorderExt_DOOR_1(draw_target);
567 DrawMaskedBorderExt_DOOR_2(draw_target);
568 DrawMaskedBorderExt_DOOR_3(draw_target);
571 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
573 /* never draw masked screen borders on borderless screens */
574 if (global.border_status == GAME_MODE_LOADING ||
575 global.border_status == GAME_MODE_TITLE)
578 if (redraw_mask & REDRAW_ALL)
579 DrawMaskedBorderExt_ALL(draw_target);
582 if (redraw_mask & REDRAW_FIELD)
583 DrawMaskedBorderExt_FIELD(draw_target);
584 if (redraw_mask & REDRAW_DOOR_1)
585 DrawMaskedBorderExt_DOOR_1(draw_target);
586 if (redraw_mask & REDRAW_DOOR_2)
587 DrawMaskedBorderExt_DOOR_2(draw_target);
588 if (redraw_mask & REDRAW_DOOR_3)
589 DrawMaskedBorderExt_DOOR_3(draw_target);
593 void DrawMaskedBorder_FIELD()
595 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
598 void DrawMaskedBorder(int redraw_mask)
600 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
603 void DrawMaskedBorderToTarget(int draw_target)
605 if (draw_target == DRAW_TO_BACKBUFFER ||
606 draw_target == DRAW_TO_SCREEN)
608 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
612 int last_border_status = global.border_status;
614 if (draw_target == DRAW_TO_FADE_SOURCE)
616 global.border_status = gfx.fade_border_source_status;
617 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
619 else if (draw_target == DRAW_TO_FADE_TARGET)
621 global.border_status = gfx.fade_border_target_status;
622 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
625 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
627 global.border_status = last_border_status;
628 gfx.masked_border_bitmap_ptr = backbuffer;
632 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
634 int fx = getFieldbufferOffsetX_RND();
635 int fy = getFieldbufferOffsetY_RND();
637 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
640 void BlitScreenToBitmap(Bitmap *target_bitmap)
642 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
643 BlitScreenToBitmap_EM(target_bitmap);
644 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
645 BlitScreenToBitmap_SP(target_bitmap);
646 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
647 BlitScreenToBitmap_MM(target_bitmap);
648 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
649 BlitScreenToBitmap_RND(target_bitmap);
651 redraw_mask |= REDRAW_FIELD;
654 void DrawFramesPerSecond()
657 int font_nr = FONT_TEXT_2;
658 int font_width = getFontWidth(font_nr);
659 int draw_deactivation_mask = GetDrawDeactivationMask();
660 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
662 /* draw FPS with leading space (needed if field buffer deactivated) */
663 sprintf(text, " %04.1f fps", global.frames_per_second);
665 /* override draw deactivation mask (required for invisible warp mode) */
666 SetDrawDeactivationMask(REDRAW_NONE);
668 /* draw opaque FPS if field buffer deactivated, else draw masked FPS */
669 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
670 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
672 /* set draw deactivation mask to previous value */
673 SetDrawDeactivationMask(draw_deactivation_mask);
675 /* force full-screen redraw in this frame */
676 redraw_mask = REDRAW_ALL;
680 static void PrintFrameTimeDebugging()
682 static unsigned int last_counter = 0;
683 unsigned int counter = Counter();
684 int diff_1 = counter - last_counter;
685 int diff_2 = diff_1 - GAME_FRAME_DELAY;
687 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
688 char diff_bar[2 * diff_2_max + 5];
692 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
694 for (i = 0; i < diff_2_max; i++)
695 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
696 i >= diff_2_max - diff_2_cut ? '-' : ' ');
698 diff_bar[pos++] = '|';
700 for (i = 0; i < diff_2_max; i++)
701 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
703 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
705 diff_bar[pos++] = '\0';
707 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
710 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
713 last_counter = counter;
717 static int unifiedRedrawMask(int mask)
719 if (mask & REDRAW_ALL)
722 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
728 static boolean equalRedrawMasks(int mask_1, int mask_2)
730 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
735 static int last_redraw_mask = REDRAW_NONE;
737 // force screen redraw in every frame to continue drawing global animations
738 // (but always use the last redraw mask to prevent unwanted side effects)
739 if (redraw_mask == REDRAW_NONE)
740 redraw_mask = last_redraw_mask;
742 last_redraw_mask = redraw_mask;
745 // masked border now drawn immediately when blitting backbuffer to window
747 // draw masked border to all viewports, if defined
748 DrawMaskedBorder(redraw_mask);
751 // draw frames per second (only if debug mode is enabled)
752 if (redraw_mask & REDRAW_FPS)
753 DrawFramesPerSecond();
755 // remove playfield redraw before potentially merging with doors redraw
756 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
757 redraw_mask &= ~REDRAW_FIELD;
759 // redraw complete window if both playfield and (some) doors need redraw
760 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
761 redraw_mask = REDRAW_ALL;
763 /* although redrawing the whole window would be fine for normal gameplay,
764 being able to only redraw the playfield is required for deactivating
765 certain drawing areas (mainly playfield) to work, which is needed for
766 warp-forward to be fast enough (by skipping redraw of most frames) */
768 if (redraw_mask & REDRAW_ALL)
770 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
772 else if (redraw_mask & REDRAW_FIELD)
774 BlitBitmap(backbuffer, window,
775 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
777 else if (redraw_mask & REDRAW_DOORS)
779 // merge door areas to prevent calling screen redraw more than once
785 if (redraw_mask & REDRAW_DOOR_1)
789 x2 = MAX(x2, DX + DXSIZE);
790 y2 = MAX(y2, DY + DYSIZE);
793 if (redraw_mask & REDRAW_DOOR_2)
797 x2 = MAX(x2, VX + VXSIZE);
798 y2 = MAX(y2, VY + VYSIZE);
801 if (redraw_mask & REDRAW_DOOR_3)
805 x2 = MAX(x2, EX + EXSIZE);
806 y2 = MAX(y2, EY + EYSIZE);
809 // make sure that at least one pixel is blitted, and inside the screen
810 // (else nothing is blitted, causing the animations not to be updated)
811 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
812 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
813 x2 = MIN(MAX(1, x2), WIN_XSIZE);
814 y2 = MIN(MAX(1, y2), WIN_YSIZE);
816 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
819 redraw_mask = REDRAW_NONE;
822 PrintFrameTimeDebugging();
826 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
828 unsigned int frame_delay_value_old = GetVideoFrameDelay();
830 SetVideoFrameDelay(frame_delay_value);
834 SetVideoFrameDelay(frame_delay_value_old);
837 static int fade_type_skip = FADE_TYPE_NONE;
839 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
841 void (*draw_border_function)(void) = NULL;
842 int x, y, width, height;
843 int fade_delay, post_delay;
845 if (fade_type == FADE_TYPE_FADE_OUT)
847 if (fade_type_skip != FADE_TYPE_NONE)
849 /* skip all fade operations until specified fade operation */
850 if (fade_type & fade_type_skip)
851 fade_type_skip = FADE_TYPE_NONE;
856 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
860 redraw_mask |= fade_mask;
862 if (fade_type == FADE_TYPE_SKIP)
864 fade_type_skip = fade_mode;
869 fade_delay = fading.fade_delay;
870 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
872 if (fade_type_skip != FADE_TYPE_NONE)
874 /* skip all fade operations until specified fade operation */
875 if (fade_type & fade_type_skip)
876 fade_type_skip = FADE_TYPE_NONE;
881 if (global.autoplay_leveldir)
886 if (fade_mask == REDRAW_FIELD)
891 height = FADE_SYSIZE;
893 if (border.draw_masked_when_fading)
894 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
896 DrawMaskedBorder_FIELD(); /* draw once */
898 else /* REDRAW_ALL */
906 if (!setup.fade_screens ||
908 fading.fade_mode == FADE_MODE_NONE)
910 if (fade_mode == FADE_MODE_FADE_OUT)
913 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
915 redraw_mask &= ~fade_mask;
920 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
921 draw_border_function);
923 redraw_mask &= ~fade_mask;
926 static void SetScreenStates_BeforeFadingIn()
928 // temporarily set screen mode for animations to screen after fading in
929 global.anim_status = global.anim_status_next;
931 // store backbuffer with all animations that will be started after fading in
932 if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
933 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
935 // set screen mode for animations back to fading
936 global.anim_status = GAME_MODE_PSEUDO_FADING;
939 static void SetScreenStates_AfterFadingIn()
941 // store new source screen (to use correct masked border for fading)
942 gfx.fade_border_source_status = global.border_status;
944 global.anim_status = global.anim_status_next;
947 static void SetScreenStates_BeforeFadingOut()
949 // store new target screen (to use correct masked border for fading)
950 gfx.fade_border_target_status = game_status;
952 // set screen mode for animations to fading
953 global.anim_status = GAME_MODE_PSEUDO_FADING;
955 // store backbuffer with all animations that will be stopped for fading out
956 if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
957 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
960 static void SetScreenStates_AfterFadingOut()
962 global.border_status = game_status;
965 void FadeIn(int fade_mask)
967 SetScreenStates_BeforeFadingIn();
970 DrawMaskedBorder(REDRAW_ALL);
973 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
974 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
976 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
980 FADE_SXSIZE = FULL_SXSIZE;
981 FADE_SYSIZE = FULL_SYSIZE;
983 if (game_status == GAME_MODE_PLAYING &&
984 strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
985 SetOverlayActive(TRUE);
987 SetScreenStates_AfterFadingIn();
989 // force update of global animation status in case of rapid screen changes
990 redraw_mask = REDRAW_ALL;
994 void FadeOut(int fade_mask)
996 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
997 if (!equalRedrawMasks(fade_mask, redraw_mask))
1000 SetScreenStates_BeforeFadingOut();
1002 SetOverlayActive(FALSE);
1005 DrawMaskedBorder(REDRAW_ALL);
1008 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1009 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1011 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1013 SetScreenStates_AfterFadingOut();
1016 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1018 static struct TitleFadingInfo fading_leave_stored;
1021 fading_leave_stored = fading_leave;
1023 fading = fading_leave_stored;
1026 void FadeSetEnterMenu()
1028 fading = menu.enter_menu;
1030 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1033 void FadeSetLeaveMenu()
1035 fading = menu.leave_menu;
1037 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1040 void FadeSetEnterScreen()
1042 fading = menu.enter_screen[game_status];
1044 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
1047 void FadeSetNextScreen()
1049 fading = menu.next_screen[game_status];
1051 // (do not overwrite fade mode set by FadeSetEnterScreen)
1052 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1055 void FadeSetLeaveScreen()
1057 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
1060 void FadeSetFromType(int type)
1062 if (type & TYPE_ENTER_SCREEN)
1063 FadeSetEnterScreen();
1064 else if (type & TYPE_ENTER)
1066 else if (type & TYPE_LEAVE)
1070 void FadeSetDisabled()
1072 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1074 fading = fading_none;
1077 void FadeSkipNextFadeIn()
1079 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1082 void FadeSkipNextFadeOut()
1084 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1087 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1089 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1091 return (graphic == IMG_UNDEFINED ? NULL :
1092 graphic_info[graphic].bitmap != NULL || redefined ?
1093 graphic_info[graphic].bitmap :
1094 graphic_info[default_graphic].bitmap);
1097 Bitmap *getBackgroundBitmap(int graphic)
1099 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1102 Bitmap *getGlobalBorderBitmap(int graphic)
1104 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1107 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1110 (status == GAME_MODE_MAIN ||
1111 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1112 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1113 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1114 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1117 return getGlobalBorderBitmap(graphic);
1120 void SetWindowBackgroundImageIfDefined(int graphic)
1122 if (graphic_info[graphic].bitmap)
1123 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1126 void SetMainBackgroundImageIfDefined(int graphic)
1128 if (graphic_info[graphic].bitmap)
1129 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1132 void SetDoorBackgroundImageIfDefined(int graphic)
1134 if (graphic_info[graphic].bitmap)
1135 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1138 void SetWindowBackgroundImage(int graphic)
1140 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1143 void SetMainBackgroundImage(int graphic)
1145 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1148 void SetDoorBackgroundImage(int graphic)
1150 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1153 void SetPanelBackground()
1155 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1157 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1158 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1160 SetDoorBackgroundBitmap(bitmap_db_panel);
1163 void DrawBackground(int x, int y, int width, int height)
1165 /* "drawto" might still point to playfield buffer here (hall of fame) */
1166 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1168 if (IN_GFX_FIELD_FULL(x, y))
1169 redraw_mask |= REDRAW_FIELD;
1170 else if (IN_GFX_DOOR_1(x, y))
1171 redraw_mask |= REDRAW_DOOR_1;
1172 else if (IN_GFX_DOOR_2(x, y))
1173 redraw_mask |= REDRAW_DOOR_2;
1174 else if (IN_GFX_DOOR_3(x, y))
1175 redraw_mask |= REDRAW_DOOR_3;
1178 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1180 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1182 if (font->bitmap == NULL)
1185 DrawBackground(x, y, width, height);
1188 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1190 struct GraphicInfo *g = &graphic_info[graphic];
1192 if (g->bitmap == NULL)
1195 DrawBackground(x, y, width, height);
1198 static int game_status_last = -1;
1199 static Bitmap *global_border_bitmap_last = NULL;
1200 static Bitmap *global_border_bitmap = NULL;
1201 static int real_sx_last = -1, real_sy_last = -1;
1202 static int full_sxsize_last = -1, full_sysize_last = -1;
1203 static int dx_last = -1, dy_last = -1;
1204 static int dxsize_last = -1, dysize_last = -1;
1205 static int vx_last = -1, vy_last = -1;
1206 static int vxsize_last = -1, vysize_last = -1;
1208 boolean CheckIfGlobalBorderHasChanged()
1210 // if game status has not changed, global border has not changed either
1211 if (game_status == game_status_last)
1214 // determine and store new global border bitmap for current game status
1215 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1217 return (global_border_bitmap_last != global_border_bitmap);
1220 boolean CheckIfGlobalBorderRedrawIsNeeded()
1222 // if game status has not changed, nothing has to be redrawn
1223 if (game_status == game_status_last)
1226 // redraw if last screen was title screen
1227 if (game_status_last == GAME_MODE_TITLE)
1230 // redraw if global screen border has changed
1231 if (CheckIfGlobalBorderHasChanged())
1234 // redraw if position or size of playfield area has changed
1235 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1236 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1239 // redraw if position or size of door area has changed
1240 if (dx_last != DX || dy_last != DY ||
1241 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1244 // redraw if position or size of tape area has changed
1245 if (vx_last != VX || vy_last != VY ||
1246 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1252 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1255 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1257 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1260 void RedrawGlobalBorder()
1262 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1264 RedrawGlobalBorderFromBitmap(bitmap);
1266 redraw_mask = REDRAW_ALL;
1269 static void RedrawGlobalBorderIfNeeded()
1271 if (game_status == game_status_last)
1274 // copy current draw buffer to later copy back areas that have not changed
1275 if (game_status_last != GAME_MODE_TITLE)
1276 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1278 if (CheckIfGlobalBorderRedrawIsNeeded())
1280 // redraw global screen border (or clear, if defined to be empty)
1281 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1283 // copy previous playfield and door areas, if they are defined on both
1284 // previous and current screen and if they still have the same size
1286 if (real_sx_last != -1 && real_sy_last != -1 &&
1287 REAL_SX != -1 && REAL_SY != -1 &&
1288 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1289 BlitBitmap(bitmap_db_store_1, backbuffer,
1290 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1293 if (dx_last != -1 && dy_last != -1 &&
1294 DX != -1 && DY != -1 &&
1295 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1296 BlitBitmap(bitmap_db_store_1, backbuffer,
1297 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1299 if (vx_last != -1 && vy_last != -1 &&
1300 VX != -1 && VY != -1 &&
1301 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1302 BlitBitmap(bitmap_db_store_1, backbuffer,
1303 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1305 redraw_mask = REDRAW_ALL;
1308 game_status_last = game_status;
1310 global_border_bitmap_last = global_border_bitmap;
1312 real_sx_last = REAL_SX;
1313 real_sy_last = REAL_SY;
1314 full_sxsize_last = FULL_SXSIZE;
1315 full_sysize_last = FULL_SYSIZE;
1318 dxsize_last = DXSIZE;
1319 dysize_last = DYSIZE;
1322 vxsize_last = VXSIZE;
1323 vysize_last = VYSIZE;
1328 RedrawGlobalBorderIfNeeded();
1330 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1331 /* (when entering hall of fame after playing) */
1332 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1334 /* !!! maybe this should be done before clearing the background !!! */
1335 if (game_status == GAME_MODE_PLAYING)
1337 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1338 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1342 SetDrawtoField(DRAW_TO_BACKBUFFER);
1346 void MarkTileDirty(int x, int y)
1348 redraw_mask |= REDRAW_FIELD;
1351 void SetBorderElement()
1355 BorderElement = EL_EMPTY;
1357 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1359 for (x = 0; x < lev_fieldx; x++)
1361 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1362 BorderElement = EL_STEELWALL;
1364 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1370 void FloodFillLevel(int from_x, int from_y, int fill_element,
1371 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1372 int max_fieldx, int max_fieldy)
1376 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1377 static int safety = 0;
1379 /* check if starting field still has the desired content */
1380 if (field[from_x][from_y] == fill_element)
1385 if (safety > max_fieldx * max_fieldy)
1386 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1388 old_element = field[from_x][from_y];
1389 field[from_x][from_y] = fill_element;
1391 for (i = 0; i < 4; i++)
1393 x = from_x + check[i][0];
1394 y = from_y + check[i][1];
1396 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1397 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1403 void SetRandomAnimationValue(int x, int y)
1405 gfx.anim_random_frame = GfxRandom[x][y];
1408 int getGraphicAnimationFrame(int graphic, int sync_frame)
1410 /* animation synchronized with global frame counter, not move position */
1411 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1412 sync_frame = FrameCounter;
1414 return getAnimationFrame(graphic_info[graphic].anim_frames,
1415 graphic_info[graphic].anim_delay,
1416 graphic_info[graphic].anim_mode,
1417 graphic_info[graphic].anim_start_frame,
1421 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1423 struct GraphicInfo *g = &graphic_info[graphic];
1424 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1426 if (tilesize == gfx.standard_tile_size)
1427 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1428 else if (tilesize == game.tile_size)
1429 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1431 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1434 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1435 boolean get_backside)
1437 struct GraphicInfo *g = &graphic_info[graphic];
1438 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1439 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1441 if (g->offset_y == 0) /* frames are ordered horizontally */
1443 int max_width = g->anim_frames_per_line * g->width;
1444 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1446 *x = pos % max_width;
1447 *y = src_y % g->height + pos / max_width * g->height;
1449 else if (g->offset_x == 0) /* frames are ordered vertically */
1451 int max_height = g->anim_frames_per_line * g->height;
1452 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1454 *x = src_x % g->width + pos / max_height * g->width;
1455 *y = pos % max_height;
1457 else /* frames are ordered diagonally */
1459 *x = src_x + frame * g->offset_x;
1460 *y = src_y + frame * g->offset_y;
1464 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1465 Bitmap **bitmap, int *x, int *y,
1466 boolean get_backside)
1468 struct GraphicInfo *g = &graphic_info[graphic];
1470 // if no in-game graphics defined, always use standard graphic size
1471 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1472 tilesize = TILESIZE;
1474 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1475 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1477 *x = *x * tilesize / g->tile_size;
1478 *y = *y * tilesize / g->tile_size;
1481 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1482 Bitmap **bitmap, int *x, int *y)
1484 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1487 void getFixedGraphicSource(int graphic, int frame,
1488 Bitmap **bitmap, int *x, int *y)
1490 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1493 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1495 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1498 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1499 int *x, int *y, boolean get_backside)
1501 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1505 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1507 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1510 void DrawGraphic(int x, int y, int graphic, int frame)
1513 if (!IN_SCR_FIELD(x, y))
1515 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1516 printf("DrawGraphic(): This should never happen!\n");
1521 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1524 MarkTileDirty(x, y);
1527 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1530 if (!IN_SCR_FIELD(x, y))
1532 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1533 printf("DrawGraphic(): This should never happen!\n");
1538 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1540 MarkTileDirty(x, y);
1543 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1549 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1551 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1554 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1560 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1561 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1564 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1567 if (!IN_SCR_FIELD(x, y))
1569 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1570 printf("DrawGraphicThruMask(): This should never happen!\n");
1575 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1578 MarkTileDirty(x, y);
1581 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1584 if (!IN_SCR_FIELD(x, y))
1586 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1587 printf("DrawGraphicThruMask(): This should never happen!\n");
1592 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1594 MarkTileDirty(x, y);
1597 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1603 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1605 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1609 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1610 int graphic, int frame)
1615 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1617 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1621 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1623 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1625 MarkTileDirty(x / tilesize, y / tilesize);
1628 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1634 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1635 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1638 void DrawMiniGraphic(int x, int y, int graphic)
1640 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1641 MarkTileDirty(x / 2, y / 2);
1644 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1649 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1650 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1653 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1654 int graphic, int frame,
1655 int cut_mode, int mask_mode)
1660 int width = TILEX, height = TILEY;
1663 if (dx || dy) /* shifted graphic */
1665 if (x < BX1) /* object enters playfield from the left */
1672 else if (x > BX2) /* object enters playfield from the right */
1678 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1684 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1686 else if (dx) /* general horizontal movement */
1687 MarkTileDirty(x + SIGN(dx), y);
1689 if (y < BY1) /* object enters playfield from the top */
1691 if (cut_mode == CUT_BELOW) /* object completely above top border */
1699 else if (y > BY2) /* object enters playfield from the bottom */
1705 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1711 else if (dy > 0 && cut_mode == CUT_ABOVE)
1713 if (y == BY2) /* object completely above bottom border */
1719 MarkTileDirty(x, y + 1);
1720 } /* object leaves playfield to the bottom */
1721 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1723 else if (dy) /* general vertical movement */
1724 MarkTileDirty(x, y + SIGN(dy));
1728 if (!IN_SCR_FIELD(x, y))
1730 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1731 printf("DrawGraphicShifted(): This should never happen!\n");
1736 width = width * TILESIZE_VAR / TILESIZE;
1737 height = height * TILESIZE_VAR / TILESIZE;
1738 cx = cx * TILESIZE_VAR / TILESIZE;
1739 cy = cy * TILESIZE_VAR / TILESIZE;
1740 dx = dx * TILESIZE_VAR / TILESIZE;
1741 dy = dy * TILESIZE_VAR / TILESIZE;
1743 if (width > 0 && height > 0)
1745 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1750 dst_x = FX + x * TILEX_VAR + dx;
1751 dst_y = FY + y * TILEY_VAR + dy;
1753 if (mask_mode == USE_MASKING)
1754 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1757 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1760 MarkTileDirty(x, y);
1764 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1765 int graphic, int frame,
1766 int cut_mode, int mask_mode)
1771 int width = TILEX_VAR, height = TILEY_VAR;
1774 int x2 = x + SIGN(dx);
1775 int y2 = y + SIGN(dy);
1777 /* movement with two-tile animations must be sync'ed with movement position,
1778 not with current GfxFrame (which can be higher when using slow movement) */
1779 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1780 int anim_frames = graphic_info[graphic].anim_frames;
1782 /* (we also need anim_delay here for movement animations with less frames) */
1783 int anim_delay = graphic_info[graphic].anim_delay;
1784 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1786 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1787 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1789 /* re-calculate animation frame for two-tile movement animation */
1790 frame = getGraphicAnimationFrame(graphic, sync_frame);
1792 /* check if movement start graphic inside screen area and should be drawn */
1793 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1795 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1797 dst_x = FX + x1 * TILEX_VAR;
1798 dst_y = FY + y1 * TILEY_VAR;
1800 if (mask_mode == USE_MASKING)
1801 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1804 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1807 MarkTileDirty(x1, y1);
1810 /* check if movement end graphic inside screen area and should be drawn */
1811 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1813 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1815 dst_x = FX + x2 * TILEX_VAR;
1816 dst_y = FY + y2 * TILEY_VAR;
1818 if (mask_mode == USE_MASKING)
1819 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1822 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1825 MarkTileDirty(x2, y2);
1829 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1830 int graphic, int frame,
1831 int cut_mode, int mask_mode)
1835 DrawGraphic(x, y, graphic, frame);
1840 if (graphic_info[graphic].double_movement) /* EM style movement images */
1841 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1843 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1846 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1847 int frame, int cut_mode)
1849 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1852 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1853 int cut_mode, int mask_mode)
1855 int lx = LEVELX(x), ly = LEVELY(y);
1859 if (IN_LEV_FIELD(lx, ly))
1861 SetRandomAnimationValue(lx, ly);
1863 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1864 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1866 /* do not use double (EM style) movement graphic when not moving */
1867 if (graphic_info[graphic].double_movement && !dx && !dy)
1869 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1870 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1873 else /* border element */
1875 graphic = el2img(element);
1876 frame = getGraphicAnimationFrame(graphic, -1);
1879 if (element == EL_EXPANDABLE_WALL)
1881 boolean left_stopped = FALSE, right_stopped = FALSE;
1883 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1884 left_stopped = TRUE;
1885 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1886 right_stopped = TRUE;
1888 if (left_stopped && right_stopped)
1890 else if (left_stopped)
1892 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1893 frame = graphic_info[graphic].anim_frames - 1;
1895 else if (right_stopped)
1897 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1898 frame = graphic_info[graphic].anim_frames - 1;
1903 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1904 else if (mask_mode == USE_MASKING)
1905 DrawGraphicThruMask(x, y, graphic, frame);
1907 DrawGraphic(x, y, graphic, frame);
1910 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1911 int cut_mode, int mask_mode)
1913 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1914 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1915 cut_mode, mask_mode);
1918 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1921 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1924 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1927 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1930 void DrawLevelElementThruMask(int x, int y, int element)
1932 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1935 void DrawLevelFieldThruMask(int x, int y)
1937 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1940 /* !!! implementation of quicksand is totally broken !!! */
1941 #define IS_CRUMBLED_TILE(x, y, e) \
1942 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1943 !IS_MOVING(x, y) || \
1944 (e) == EL_QUICKSAND_EMPTYING || \
1945 (e) == EL_QUICKSAND_FAST_EMPTYING))
1947 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1952 int width, height, cx, cy;
1953 int sx = SCREENX(x), sy = SCREENY(y);
1954 int crumbled_border_size = graphic_info[graphic].border_size;
1955 int crumbled_tile_size = graphic_info[graphic].tile_size;
1956 int crumbled_border_size_var =
1957 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
1960 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1962 for (i = 1; i < 4; i++)
1964 int dxx = (i & 1 ? dx : 0);
1965 int dyy = (i & 2 ? dy : 0);
1968 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1971 /* check if neighbour field is of same crumble type */
1972 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1973 graphic_info[graphic].class ==
1974 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1976 /* return if check prevents inner corner */
1977 if (same == (dxx == dx && dyy == dy))
1981 /* if we reach this point, we have an inner corner */
1983 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1985 width = crumbled_border_size_var;
1986 height = crumbled_border_size_var;
1987 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1988 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1990 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1991 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1994 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1999 int width, height, bx, by, cx, cy;
2000 int sx = SCREENX(x), sy = SCREENY(y);
2001 int crumbled_border_size = graphic_info[graphic].border_size;
2002 int crumbled_tile_size = graphic_info[graphic].tile_size;
2003 int crumbled_border_size_var =
2004 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2005 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2008 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2010 /* draw simple, sloppy, non-corner-accurate crumbled border */
2012 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2013 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2014 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2015 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2017 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2018 FX + sx * TILEX_VAR + cx,
2019 FY + sy * TILEY_VAR + cy);
2021 /* (remaining middle border part must be at least as big as corner part) */
2022 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2023 crumbled_border_size_var >= TILESIZE_VAR / 3)
2026 /* correct corners of crumbled border, if needed */
2028 for (i = -1; i <= 1; i += 2)
2030 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2031 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2032 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2035 /* check if neighbour field is of same crumble type */
2036 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2037 graphic_info[graphic].class ==
2038 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2040 /* no crumbled corner, but continued crumbled border */
2042 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2043 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2044 int b1 = (i == 1 ? crumbled_border_size_var :
2045 TILESIZE_VAR - 2 * crumbled_border_size_var);
2047 width = crumbled_border_size_var;
2048 height = crumbled_border_size_var;
2050 if (dir == 1 || dir == 2)
2065 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2067 FX + sx * TILEX_VAR + cx,
2068 FY + sy * TILEY_VAR + cy);
2073 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2075 int sx = SCREENX(x), sy = SCREENY(y);
2078 static int xy[4][2] =
2086 if (!IN_LEV_FIELD(x, y))
2089 element = TILE_GFX_ELEMENT(x, y);
2091 if (IS_CRUMBLED_TILE(x, y, element)) /* crumble field itself */
2093 if (!IN_SCR_FIELD(sx, sy))
2096 /* crumble field borders towards direct neighbour fields */
2097 for (i = 0; i < 4; i++)
2099 int xx = x + xy[i][0];
2100 int yy = y + xy[i][1];
2102 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2105 /* check if neighbour field is of same crumble type */
2106 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2107 graphic_info[graphic].class ==
2108 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2111 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2114 /* crumble inner field corners towards corner neighbour fields */
2115 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2116 graphic_info[graphic].anim_frames == 2)
2118 for (i = 0; i < 4; i++)
2120 int dx = (i & 1 ? +1 : -1);
2121 int dy = (i & 2 ? +1 : -1);
2123 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2127 MarkTileDirty(sx, sy);
2129 else /* center field is not crumbled -- crumble neighbour fields */
2131 /* crumble field borders of direct neighbour fields */
2132 for (i = 0; i < 4; i++)
2134 int xx = x + xy[i][0];
2135 int yy = y + xy[i][1];
2136 int sxx = sx + xy[i][0];
2137 int syy = sy + xy[i][1];
2139 if (!IN_LEV_FIELD(xx, yy) ||
2140 !IN_SCR_FIELD(sxx, syy))
2143 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2146 element = TILE_GFX_ELEMENT(xx, yy);
2148 if (!IS_CRUMBLED_TILE(xx, yy, element))
2151 graphic = el_act2crm(element, ACTION_DEFAULT);
2153 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2155 MarkTileDirty(sxx, syy);
2158 /* crumble inner field corners of corner neighbour fields */
2159 for (i = 0; i < 4; i++)
2161 int dx = (i & 1 ? +1 : -1);
2162 int dy = (i & 2 ? +1 : -1);
2168 if (!IN_LEV_FIELD(xx, yy) ||
2169 !IN_SCR_FIELD(sxx, syy))
2172 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2175 element = TILE_GFX_ELEMENT(xx, yy);
2177 if (!IS_CRUMBLED_TILE(xx, yy, element))
2180 graphic = el_act2crm(element, ACTION_DEFAULT);
2182 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2183 graphic_info[graphic].anim_frames == 2)
2184 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2186 MarkTileDirty(sxx, syy);
2191 void DrawLevelFieldCrumbled(int x, int y)
2195 if (!IN_LEV_FIELD(x, y))
2198 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2199 GfxElement[x][y] != EL_UNDEFINED &&
2200 GFX_CRUMBLED(GfxElement[x][y]))
2202 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2207 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2209 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2212 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2215 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2216 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2217 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2218 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2219 int sx = SCREENX(x), sy = SCREENY(y);
2221 DrawGraphic(sx, sy, graphic1, frame1);
2222 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2225 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2227 int sx = SCREENX(x), sy = SCREENY(y);
2228 static int xy[4][2] =
2237 /* crumble direct neighbour fields (required for field borders) */
2238 for (i = 0; i < 4; i++)
2240 int xx = x + xy[i][0];
2241 int yy = y + xy[i][1];
2242 int sxx = sx + xy[i][0];
2243 int syy = sy + xy[i][1];
2245 if (!IN_LEV_FIELD(xx, yy) ||
2246 !IN_SCR_FIELD(sxx, syy) ||
2247 !GFX_CRUMBLED(Feld[xx][yy]) ||
2251 DrawLevelField(xx, yy);
2254 /* crumble corner neighbour fields (required for inner field corners) */
2255 for (i = 0; i < 4; i++)
2257 int dx = (i & 1 ? +1 : -1);
2258 int dy = (i & 2 ? +1 : -1);
2264 if (!IN_LEV_FIELD(xx, yy) ||
2265 !IN_SCR_FIELD(sxx, syy) ||
2266 !GFX_CRUMBLED(Feld[xx][yy]) ||
2270 int element = TILE_GFX_ELEMENT(xx, yy);
2271 int graphic = el_act2crm(element, ACTION_DEFAULT);
2273 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2274 graphic_info[graphic].anim_frames == 2)
2275 DrawLevelField(xx, yy);
2279 static int getBorderElement(int x, int y)
2283 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2284 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2285 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2286 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2287 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2288 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2289 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2291 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2292 int steel_position = (x == -1 && y == -1 ? 0 :
2293 x == lev_fieldx && y == -1 ? 1 :
2294 x == -1 && y == lev_fieldy ? 2 :
2295 x == lev_fieldx && y == lev_fieldy ? 3 :
2296 x == -1 || x == lev_fieldx ? 4 :
2297 y == -1 || y == lev_fieldy ? 5 : 6);
2299 return border[steel_position][steel_type];
2302 void DrawScreenElement(int x, int y, int element)
2304 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2305 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2308 void DrawLevelElement(int x, int y, int element)
2310 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2311 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2314 void DrawScreenField(int x, int y)
2316 int lx = LEVELX(x), ly = LEVELY(y);
2317 int element, content;
2319 if (!IN_LEV_FIELD(lx, ly))
2321 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2324 element = getBorderElement(lx, ly);
2326 DrawScreenElement(x, y, element);
2331 element = Feld[lx][ly];
2332 content = Store[lx][ly];
2334 if (IS_MOVING(lx, ly))
2336 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2337 boolean cut_mode = NO_CUTTING;
2339 if (element == EL_QUICKSAND_EMPTYING ||
2340 element == EL_QUICKSAND_FAST_EMPTYING ||
2341 element == EL_MAGIC_WALL_EMPTYING ||
2342 element == EL_BD_MAGIC_WALL_EMPTYING ||
2343 element == EL_DC_MAGIC_WALL_EMPTYING ||
2344 element == EL_AMOEBA_DROPPING)
2345 cut_mode = CUT_ABOVE;
2346 else if (element == EL_QUICKSAND_FILLING ||
2347 element == EL_QUICKSAND_FAST_FILLING ||
2348 element == EL_MAGIC_WALL_FILLING ||
2349 element == EL_BD_MAGIC_WALL_FILLING ||
2350 element == EL_DC_MAGIC_WALL_FILLING)
2351 cut_mode = CUT_BELOW;
2353 if (cut_mode == CUT_ABOVE)
2354 DrawScreenElement(x, y, element);
2356 DrawScreenElement(x, y, EL_EMPTY);
2359 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2360 else if (cut_mode == NO_CUTTING)
2361 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2364 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2366 if (cut_mode == CUT_BELOW &&
2367 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2368 DrawLevelElement(lx, ly + 1, element);
2371 if (content == EL_ACID)
2373 int dir = MovDir[lx][ly];
2374 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2375 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2377 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2379 // prevent target field from being drawn again (but without masking)
2380 // (this would happen if target field is scanned after moving element)
2381 Stop[newlx][newly] = TRUE;
2384 else if (IS_BLOCKED(lx, ly))
2389 boolean cut_mode = NO_CUTTING;
2390 int element_old, content_old;
2392 Blocked2Moving(lx, ly, &oldx, &oldy);
2395 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2396 MovDir[oldx][oldy] == MV_RIGHT);
2398 element_old = Feld[oldx][oldy];
2399 content_old = Store[oldx][oldy];
2401 if (element_old == EL_QUICKSAND_EMPTYING ||
2402 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2403 element_old == EL_MAGIC_WALL_EMPTYING ||
2404 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2405 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2406 element_old == EL_AMOEBA_DROPPING)
2407 cut_mode = CUT_ABOVE;
2409 DrawScreenElement(x, y, EL_EMPTY);
2412 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2414 else if (cut_mode == NO_CUTTING)
2415 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2418 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2421 else if (IS_DRAWABLE(element))
2422 DrawScreenElement(x, y, element);
2424 DrawScreenElement(x, y, EL_EMPTY);
2427 void DrawLevelField(int x, int y)
2429 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2430 DrawScreenField(SCREENX(x), SCREENY(y));
2431 else if (IS_MOVING(x, y))
2435 Moving2Blocked(x, y, &newx, &newy);
2436 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2437 DrawScreenField(SCREENX(newx), SCREENY(newy));
2439 else if (IS_BLOCKED(x, y))
2443 Blocked2Moving(x, y, &oldx, &oldy);
2444 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2445 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2449 void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2450 int (*el2img_function)(int))
2452 int element_base = map_mm_wall_element(element);
2453 int element_bits = (IS_DF_WALL(element) ?
2454 element - EL_DF_WALL_START :
2455 element - EL_MM_WALL_START) & 0x000f;
2456 int graphic = el2img_function(element_base);
2457 int tilesize_draw = tilesize / 2;
2462 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2464 for (i = 0; i < 4; i++)
2466 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2467 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2469 if (element_bits & (1 << i))
2470 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize_draw, tilesize_draw,
2471 dst_draw_x, dst_draw_y);
2473 ClearRectangle(drawto, dst_x, dst_y, tilesize_draw, tilesize_draw);
2477 void DrawSizedElement(int x, int y, int element, int tilesize)
2479 if (IS_MM_WALL(element))
2481 DrawSizedWall_MM(SX + x * tilesize, SY + y * tilesize,
2482 element, tilesize, el2edimg);
2486 int graphic = el2edimg(element);
2488 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2492 void DrawMiniElement(int x, int y, int element)
2496 graphic = el2edimg(element);
2497 DrawMiniGraphic(x, y, graphic);
2500 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2503 int x = sx + scroll_x, y = sy + scroll_y;
2505 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2506 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2507 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2508 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2510 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2513 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2515 int x = sx + scroll_x, y = sy + scroll_y;
2517 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2518 DrawMiniElement(sx, sy, EL_EMPTY);
2519 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2520 DrawMiniElement(sx, sy, Feld[x][y]);
2522 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2525 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2526 int x, int y, int xsize, int ysize,
2527 int tile_width, int tile_height)
2531 int dst_x = startx + x * tile_width;
2532 int dst_y = starty + y * tile_height;
2533 int width = graphic_info[graphic].width;
2534 int height = graphic_info[graphic].height;
2535 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2536 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2537 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2538 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2539 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2540 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2541 boolean draw_masked = graphic_info[graphic].draw_masked;
2543 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2545 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2547 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2551 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2552 inner_sx + (x - 1) * tile_width % inner_width);
2553 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2554 inner_sy + (y - 1) * tile_height % inner_height);
2557 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2560 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2564 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2565 int x, int y, int xsize, int ysize, int font_nr)
2567 int font_width = getFontWidth(font_nr);
2568 int font_height = getFontHeight(font_nr);
2570 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2571 font_width, font_height);
2574 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2576 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2577 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2578 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2579 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2580 boolean no_delay = (tape.warp_forward);
2581 unsigned int anim_delay = 0;
2582 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2583 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2584 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2585 int font_width = getFontWidth(font_nr);
2586 int font_height = getFontHeight(font_nr);
2587 int max_xsize = level.envelope[envelope_nr].xsize;
2588 int max_ysize = level.envelope[envelope_nr].ysize;
2589 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2590 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2591 int xend = max_xsize;
2592 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2593 int xstep = (xstart < xend ? 1 : 0);
2594 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2596 int end = MAX(xend - xstart, yend - ystart);
2599 for (i = start; i <= end; i++)
2601 int last_frame = end; // last frame of this "for" loop
2602 int x = xstart + i * xstep;
2603 int y = ystart + i * ystep;
2604 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2605 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2606 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2607 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2610 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2612 BlitScreenToBitmap(backbuffer);
2614 SetDrawtoField(DRAW_TO_BACKBUFFER);
2616 for (yy = 0; yy < ysize; yy++)
2617 for (xx = 0; xx < xsize; xx++)
2618 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2620 DrawTextBuffer(sx + font_width, sy + font_height,
2621 level.envelope[envelope_nr].text, font_nr, max_xsize,
2622 xsize - 2, ysize - 2, 0, mask_mode,
2623 level.envelope[envelope_nr].autowrap,
2624 level.envelope[envelope_nr].centered, FALSE);
2626 redraw_mask |= REDRAW_FIELD;
2629 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2633 void ShowEnvelope(int envelope_nr)
2635 int element = EL_ENVELOPE_1 + envelope_nr;
2636 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2637 int sound_opening = element_info[element].sound[ACTION_OPENING];
2638 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2639 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2640 boolean no_delay = (tape.warp_forward);
2641 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2642 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2643 int anim_mode = graphic_info[graphic].anim_mode;
2644 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2645 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2647 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2649 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2651 if (anim_mode == ANIM_DEFAULT)
2652 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2654 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2657 Delay(wait_delay_value);
2659 WaitForEventToContinue();
2661 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2663 if (anim_mode != ANIM_NONE)
2664 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2666 if (anim_mode == ANIM_DEFAULT)
2667 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2669 game.envelope_active = FALSE;
2671 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2673 redraw_mask |= REDRAW_FIELD;
2677 static void setRequestBasePosition(int *x, int *y)
2679 int sx_base, sy_base;
2681 if (request.x != -1)
2682 sx_base = request.x;
2683 else if (request.align == ALIGN_LEFT)
2685 else if (request.align == ALIGN_RIGHT)
2686 sx_base = SX + SXSIZE;
2688 sx_base = SX + SXSIZE / 2;
2690 if (request.y != -1)
2691 sy_base = request.y;
2692 else if (request.valign == VALIGN_TOP)
2694 else if (request.valign == VALIGN_BOTTOM)
2695 sy_base = SY + SYSIZE;
2697 sy_base = SY + SYSIZE / 2;
2703 static void setRequestPositionExt(int *x, int *y, int width, int height,
2704 boolean add_border_size)
2706 int border_size = request.border_size;
2707 int sx_base, sy_base;
2710 setRequestBasePosition(&sx_base, &sy_base);
2712 if (request.align == ALIGN_LEFT)
2714 else if (request.align == ALIGN_RIGHT)
2715 sx = sx_base - width;
2717 sx = sx_base - width / 2;
2719 if (request.valign == VALIGN_TOP)
2721 else if (request.valign == VALIGN_BOTTOM)
2722 sy = sy_base - height;
2724 sy = sy_base - height / 2;
2726 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2727 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2729 if (add_border_size)
2739 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2741 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2744 void DrawEnvelopeRequest(char *text)
2746 char *text_final = text;
2747 char *text_door_style = NULL;
2748 int graphic = IMG_BACKGROUND_REQUEST;
2749 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2750 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2751 int font_nr = FONT_REQUEST;
2752 int font_width = getFontWidth(font_nr);
2753 int font_height = getFontHeight(font_nr);
2754 int border_size = request.border_size;
2755 int line_spacing = request.line_spacing;
2756 int line_height = font_height + line_spacing;
2757 int max_text_width = request.width - 2 * border_size;
2758 int max_text_height = request.height - 2 * border_size;
2759 int line_length = max_text_width / font_width;
2760 int max_lines = max_text_height / line_height;
2761 int text_width = line_length * font_width;
2762 int width = request.width;
2763 int height = request.height;
2764 int tile_size = MAX(request.step_offset, 1);
2765 int x_steps = width / tile_size;
2766 int y_steps = height / tile_size;
2767 int sx_offset = border_size;
2768 int sy_offset = border_size;
2772 if (request.centered)
2773 sx_offset = (request.width - text_width) / 2;
2775 if (request.wrap_single_words && !request.autowrap)
2777 char *src_text_ptr, *dst_text_ptr;
2779 text_door_style = checked_malloc(2 * strlen(text) + 1);
2781 src_text_ptr = text;
2782 dst_text_ptr = text_door_style;
2784 while (*src_text_ptr)
2786 if (*src_text_ptr == ' ' ||
2787 *src_text_ptr == '?' ||
2788 *src_text_ptr == '!')
2789 *dst_text_ptr++ = '\n';
2791 if (*src_text_ptr != ' ')
2792 *dst_text_ptr++ = *src_text_ptr;
2797 *dst_text_ptr = '\0';
2799 text_final = text_door_style;
2802 setRequestPosition(&sx, &sy, FALSE);
2804 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2806 for (y = 0; y < y_steps; y++)
2807 for (x = 0; x < x_steps; x++)
2808 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2809 x, y, x_steps, y_steps,
2810 tile_size, tile_size);
2812 /* force DOOR font inside door area */
2813 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2815 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2816 line_length, -1, max_lines, line_spacing, mask_mode,
2817 request.autowrap, request.centered, FALSE);
2821 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2822 RedrawGadget(tool_gadget[i]);
2824 // store readily prepared envelope request for later use when animating
2825 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2827 if (text_door_style)
2828 free(text_door_style);
2831 void AnimateEnvelopeRequest(int anim_mode, int action)
2833 int graphic = IMG_BACKGROUND_REQUEST;
2834 boolean draw_masked = graphic_info[graphic].draw_masked;
2835 int delay_value_normal = request.step_delay;
2836 int delay_value_fast = delay_value_normal / 2;
2837 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2838 boolean no_delay = (tape.warp_forward);
2839 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2840 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
2841 unsigned int anim_delay = 0;
2843 int tile_size = MAX(request.step_offset, 1);
2844 int max_xsize = request.width / tile_size;
2845 int max_ysize = request.height / tile_size;
2846 int max_xsize_inner = max_xsize - 2;
2847 int max_ysize_inner = max_ysize - 2;
2849 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2850 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2851 int xend = max_xsize_inner;
2852 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2853 int xstep = (xstart < xend ? 1 : 0);
2854 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2856 int end = MAX(xend - xstart, yend - ystart);
2859 if (setup.quick_doors)
2866 for (i = start; i <= end; i++)
2868 int last_frame = end; // last frame of this "for" loop
2869 int x = xstart + i * xstep;
2870 int y = ystart + i * ystep;
2871 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2872 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2873 int xsize_size_left = (xsize - 1) * tile_size;
2874 int ysize_size_top = (ysize - 1) * tile_size;
2875 int max_xsize_pos = (max_xsize - 1) * tile_size;
2876 int max_ysize_pos = (max_ysize - 1) * tile_size;
2877 int width = xsize * tile_size;
2878 int height = ysize * tile_size;
2883 setRequestPosition(&src_x, &src_y, FALSE);
2884 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2886 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2888 for (yy = 0; yy < 2; yy++)
2890 for (xx = 0; xx < 2; xx++)
2892 int src_xx = src_x + xx * max_xsize_pos;
2893 int src_yy = src_y + yy * max_ysize_pos;
2894 int dst_xx = dst_x + xx * xsize_size_left;
2895 int dst_yy = dst_y + yy * ysize_size_top;
2896 int xx_size = (xx ? tile_size : xsize_size_left);
2897 int yy_size = (yy ? tile_size : ysize_size_top);
2900 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
2901 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2903 BlitBitmap(bitmap_db_store_2, backbuffer,
2904 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2908 redraw_mask |= REDRAW_FIELD;
2912 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2916 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2918 int graphic = IMG_BACKGROUND_REQUEST;
2919 int sound_opening = SND_REQUEST_OPENING;
2920 int sound_closing = SND_REQUEST_CLOSING;
2921 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2922 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2923 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2924 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2925 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2927 if (game_status == GAME_MODE_PLAYING)
2928 BlitScreenToBitmap(backbuffer);
2930 SetDrawtoField(DRAW_TO_BACKBUFFER);
2932 // SetDrawBackgroundMask(REDRAW_NONE);
2934 if (action == ACTION_OPENING)
2936 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2938 if (req_state & REQ_ASK)
2940 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2941 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2943 else if (req_state & REQ_CONFIRM)
2945 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2947 else if (req_state & REQ_PLAYER)
2949 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2950 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2951 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2952 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2955 DrawEnvelopeRequest(text);
2958 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2960 if (action == ACTION_OPENING)
2962 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2964 if (anim_mode == ANIM_DEFAULT)
2965 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2967 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2971 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2973 if (anim_mode != ANIM_NONE)
2974 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2976 if (anim_mode == ANIM_DEFAULT)
2977 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2980 game.envelope_active = FALSE;
2982 if (action == ACTION_CLOSING)
2983 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2985 // SetDrawBackgroundMask(last_draw_background_mask);
2987 redraw_mask |= REDRAW_FIELD;
2991 if (action == ACTION_CLOSING &&
2992 game_status == GAME_MODE_PLAYING &&
2993 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2994 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2997 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2999 if (IS_MM_WALL(element))
3001 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3007 int graphic = el2preimg(element);
3009 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3010 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3015 void DrawLevel(int draw_background_mask)
3019 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3020 SetDrawBackgroundMask(draw_background_mask);
3024 for (x = BX1; x <= BX2; x++)
3025 for (y = BY1; y <= BY2; y++)
3026 DrawScreenField(x, y);
3028 redraw_mask |= REDRAW_FIELD;
3031 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3036 for (x = 0; x < size_x; x++)
3037 for (y = 0; y < size_y; y++)
3038 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3040 redraw_mask |= REDRAW_FIELD;
3043 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3047 for (x = 0; x < size_x; x++)
3048 for (y = 0; y < size_y; y++)
3049 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3051 redraw_mask |= REDRAW_FIELD;
3054 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3056 boolean show_level_border = (BorderElement != EL_EMPTY);
3057 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3058 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3059 int tile_size = preview.tile_size;
3060 int preview_width = preview.xsize * tile_size;
3061 int preview_height = preview.ysize * tile_size;
3062 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3063 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3064 int real_preview_width = real_preview_xsize * tile_size;
3065 int real_preview_height = real_preview_ysize * tile_size;
3066 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3067 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3070 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3073 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3075 dst_x += (preview_width - real_preview_width) / 2;
3076 dst_y += (preview_height - real_preview_height) / 2;
3078 for (x = 0; x < real_preview_xsize; x++)
3080 for (y = 0; y < real_preview_ysize; y++)
3082 int lx = from_x + x + (show_level_border ? -1 : 0);
3083 int ly = from_y + y + (show_level_border ? -1 : 0);
3084 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3085 getBorderElement(lx, ly));
3087 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3088 element, tile_size);
3092 redraw_mask |= REDRAW_FIELD;
3095 #define MICROLABEL_EMPTY 0
3096 #define MICROLABEL_LEVEL_NAME 1
3097 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3098 #define MICROLABEL_LEVEL_AUTHOR 3
3099 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3100 #define MICROLABEL_IMPORTED_FROM 5
3101 #define MICROLABEL_IMPORTED_BY_HEAD 6
3102 #define MICROLABEL_IMPORTED_BY 7
3104 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3106 int max_text_width = SXSIZE;
3107 int font_width = getFontWidth(font_nr);
3109 if (pos->align == ALIGN_CENTER)
3110 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3111 else if (pos->align == ALIGN_RIGHT)
3112 max_text_width = pos->x;
3114 max_text_width = SXSIZE - pos->x;
3116 return max_text_width / font_width;
3119 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3121 char label_text[MAX_OUTPUT_LINESIZE + 1];
3122 int max_len_label_text;
3123 int font_nr = pos->font;
3126 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3129 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3130 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3131 mode == MICROLABEL_IMPORTED_BY_HEAD)
3132 font_nr = pos->font_alt;
3134 max_len_label_text = getMaxTextLength(pos, font_nr);
3136 if (pos->size != -1)
3137 max_len_label_text = pos->size;
3139 for (i = 0; i < max_len_label_text; i++)
3140 label_text[i] = ' ';
3141 label_text[max_len_label_text] = '\0';
3143 if (strlen(label_text) > 0)
3144 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3147 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3148 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3149 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3150 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3151 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3152 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3153 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3154 max_len_label_text);
3155 label_text[max_len_label_text] = '\0';
3157 if (strlen(label_text) > 0)
3158 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3160 redraw_mask |= REDRAW_FIELD;
3163 static void DrawPreviewLevelLabel(int mode)
3165 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3168 static void DrawPreviewLevelInfo(int mode)
3170 if (mode == MICROLABEL_LEVEL_NAME)
3171 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3172 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3173 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3176 static void DrawPreviewLevelExt(boolean restart)
3178 static unsigned int scroll_delay = 0;
3179 static unsigned int label_delay = 0;
3180 static int from_x, from_y, scroll_direction;
3181 static int label_state, label_counter;
3182 unsigned int scroll_delay_value = preview.step_delay;
3183 boolean show_level_border = (BorderElement != EL_EMPTY);
3184 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3185 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3192 if (preview.anim_mode == ANIM_CENTERED)
3194 if (level_xsize > preview.xsize)
3195 from_x = (level_xsize - preview.xsize) / 2;
3196 if (level_ysize > preview.ysize)
3197 from_y = (level_ysize - preview.ysize) / 2;
3200 from_x += preview.xoffset;
3201 from_y += preview.yoffset;
3203 scroll_direction = MV_RIGHT;
3207 DrawPreviewLevelPlayfield(from_x, from_y);
3208 DrawPreviewLevelLabel(label_state);
3210 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3211 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3213 /* initialize delay counters */
3214 DelayReached(&scroll_delay, 0);
3215 DelayReached(&label_delay, 0);
3217 if (leveldir_current->name)
3219 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3220 char label_text[MAX_OUTPUT_LINESIZE + 1];
3221 int font_nr = pos->font;
3222 int max_len_label_text = getMaxTextLength(pos, font_nr);
3224 if (pos->size != -1)
3225 max_len_label_text = pos->size;
3227 strncpy(label_text, leveldir_current->name, max_len_label_text);
3228 label_text[max_len_label_text] = '\0';
3230 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3231 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3237 /* scroll preview level, if needed */
3238 if (preview.anim_mode != ANIM_NONE &&
3239 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3240 DelayReached(&scroll_delay, scroll_delay_value))
3242 switch (scroll_direction)
3247 from_x -= preview.step_offset;
3248 from_x = (from_x < 0 ? 0 : from_x);
3251 scroll_direction = MV_UP;
3255 if (from_x < level_xsize - preview.xsize)
3257 from_x += preview.step_offset;
3258 from_x = (from_x > level_xsize - preview.xsize ?
3259 level_xsize - preview.xsize : from_x);
3262 scroll_direction = MV_DOWN;
3268 from_y -= preview.step_offset;
3269 from_y = (from_y < 0 ? 0 : from_y);
3272 scroll_direction = MV_RIGHT;
3276 if (from_y < level_ysize - preview.ysize)
3278 from_y += preview.step_offset;
3279 from_y = (from_y > level_ysize - preview.ysize ?
3280 level_ysize - preview.ysize : from_y);
3283 scroll_direction = MV_LEFT;
3290 DrawPreviewLevelPlayfield(from_x, from_y);
3293 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
3294 /* redraw micro level label, if needed */
3295 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3296 !strEqual(level.author, ANONYMOUS_NAME) &&
3297 !strEqual(level.author, leveldir_current->name) &&
3298 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3300 int max_label_counter = 23;
3302 if (leveldir_current->imported_from != NULL &&
3303 strlen(leveldir_current->imported_from) > 0)
3304 max_label_counter += 14;
3305 if (leveldir_current->imported_by != NULL &&
3306 strlen(leveldir_current->imported_by) > 0)
3307 max_label_counter += 14;
3309 label_counter = (label_counter + 1) % max_label_counter;
3310 label_state = (label_counter >= 0 && label_counter <= 7 ?
3311 MICROLABEL_LEVEL_NAME :
3312 label_counter >= 9 && label_counter <= 12 ?
3313 MICROLABEL_LEVEL_AUTHOR_HEAD :
3314 label_counter >= 14 && label_counter <= 21 ?
3315 MICROLABEL_LEVEL_AUTHOR :
3316 label_counter >= 23 && label_counter <= 26 ?
3317 MICROLABEL_IMPORTED_FROM_HEAD :
3318 label_counter >= 28 && label_counter <= 35 ?
3319 MICROLABEL_IMPORTED_FROM :
3320 label_counter >= 37 && label_counter <= 40 ?
3321 MICROLABEL_IMPORTED_BY_HEAD :
3322 label_counter >= 42 && label_counter <= 49 ?
3323 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3325 if (leveldir_current->imported_from == NULL &&
3326 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3327 label_state == MICROLABEL_IMPORTED_FROM))
3328 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3329 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3331 DrawPreviewLevelLabel(label_state);
3335 void DrawPreviewLevelInitial()
3337 DrawPreviewLevelExt(TRUE);
3340 void DrawPreviewLevelAnimation()
3342 DrawPreviewLevelExt(FALSE);
3345 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3346 int graphic, int sync_frame,
3349 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3351 if (mask_mode == USE_MASKING)
3352 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3354 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3357 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3358 int graphic, int sync_frame, int mask_mode)
3360 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3362 if (mask_mode == USE_MASKING)
3363 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3365 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3368 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3370 int lx = LEVELX(x), ly = LEVELY(y);
3372 if (!IN_SCR_FIELD(x, y))
3375 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3376 graphic, GfxFrame[lx][ly], NO_MASKING);
3378 MarkTileDirty(x, y);
3381 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3383 int lx = LEVELX(x), ly = LEVELY(y);
3385 if (!IN_SCR_FIELD(x, y))
3388 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3389 graphic, GfxFrame[lx][ly], NO_MASKING);
3390 MarkTileDirty(x, y);
3393 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3395 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3398 void DrawLevelElementAnimation(int x, int y, int element)
3400 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3402 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3405 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3407 int sx = SCREENX(x), sy = SCREENY(y);
3409 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3412 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3415 DrawGraphicAnimation(sx, sy, graphic);
3418 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3419 DrawLevelFieldCrumbled(x, y);
3421 if (GFX_CRUMBLED(Feld[x][y]))
3422 DrawLevelFieldCrumbled(x, y);
3426 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3428 int sx = SCREENX(x), sy = SCREENY(y);
3431 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3434 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3436 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3439 DrawGraphicAnimation(sx, sy, graphic);
3441 if (GFX_CRUMBLED(element))
3442 DrawLevelFieldCrumbled(x, y);
3445 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3447 if (player->use_murphy)
3449 /* this works only because currently only one player can be "murphy" ... */
3450 static int last_horizontal_dir = MV_LEFT;
3451 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3453 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3454 last_horizontal_dir = move_dir;
3456 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3458 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3460 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3466 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3469 static boolean equalGraphics(int graphic1, int graphic2)
3471 struct GraphicInfo *g1 = &graphic_info[graphic1];
3472 struct GraphicInfo *g2 = &graphic_info[graphic2];
3474 return (g1->bitmap == g2->bitmap &&
3475 g1->src_x == g2->src_x &&
3476 g1->src_y == g2->src_y &&
3477 g1->anim_frames == g2->anim_frames &&
3478 g1->anim_delay == g2->anim_delay &&
3479 g1->anim_mode == g2->anim_mode);
3482 void DrawAllPlayers()
3486 for (i = 0; i < MAX_PLAYERS; i++)
3487 if (stored_player[i].active)
3488 DrawPlayer(&stored_player[i]);
3491 void DrawPlayerField(int x, int y)
3493 if (!IS_PLAYER(x, y))
3496 DrawPlayer(PLAYERINFO(x, y));
3499 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3501 void DrawPlayer(struct PlayerInfo *player)
3503 int jx = player->jx;
3504 int jy = player->jy;
3505 int move_dir = player->MovDir;
3506 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3507 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3508 int last_jx = (player->is_moving ? jx - dx : jx);
3509 int last_jy = (player->is_moving ? jy - dy : jy);
3510 int next_jx = jx + dx;
3511 int next_jy = jy + dy;
3512 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3513 boolean player_is_opaque = FALSE;
3514 int sx = SCREENX(jx), sy = SCREENY(jy);
3515 int sxx = 0, syy = 0;
3516 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3518 int action = ACTION_DEFAULT;
3519 int last_player_graphic = getPlayerGraphic(player, move_dir);
3520 int last_player_frame = player->Frame;
3523 /* GfxElement[][] is set to the element the player is digging or collecting;
3524 remove also for off-screen player if the player is not moving anymore */
3525 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3526 GfxElement[jx][jy] = EL_UNDEFINED;
3528 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3532 if (!IN_LEV_FIELD(jx, jy))
3534 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3535 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3536 printf("DrawPlayerField(): This should never happen!\n");
3541 if (element == EL_EXPLOSION)
3544 action = (player->is_pushing ? ACTION_PUSHING :
3545 player->is_digging ? ACTION_DIGGING :
3546 player->is_collecting ? ACTION_COLLECTING :
3547 player->is_moving ? ACTION_MOVING :
3548 player->is_snapping ? ACTION_SNAPPING :
3549 player->is_dropping ? ACTION_DROPPING :
3550 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3552 if (player->is_waiting)
3553 move_dir = player->dir_waiting;
3555 InitPlayerGfxAnimation(player, action, move_dir);
3557 /* ----------------------------------------------------------------------- */
3558 /* draw things in the field the player is leaving, if needed */
3559 /* ----------------------------------------------------------------------- */
3561 if (player->is_moving)
3563 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3565 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3567 if (last_element == EL_DYNAMITE_ACTIVE ||
3568 last_element == EL_EM_DYNAMITE_ACTIVE ||
3569 last_element == EL_SP_DISK_RED_ACTIVE)
3570 DrawDynamite(last_jx, last_jy);
3572 DrawLevelFieldThruMask(last_jx, last_jy);
3574 else if (last_element == EL_DYNAMITE_ACTIVE ||
3575 last_element == EL_EM_DYNAMITE_ACTIVE ||
3576 last_element == EL_SP_DISK_RED_ACTIVE)
3577 DrawDynamite(last_jx, last_jy);
3579 /* !!! this is not enough to prevent flickering of players which are
3580 moving next to each others without a free tile between them -- this
3581 can only be solved by drawing all players layer by layer (first the
3582 background, then the foreground etc.) !!! => TODO */
3583 else if (!IS_PLAYER(last_jx, last_jy))
3584 DrawLevelField(last_jx, last_jy);
3587 DrawLevelField(last_jx, last_jy);
3590 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3591 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3594 if (!IN_SCR_FIELD(sx, sy))
3597 /* ----------------------------------------------------------------------- */
3598 /* draw things behind the player, if needed */
3599 /* ----------------------------------------------------------------------- */
3602 DrawLevelElement(jx, jy, Back[jx][jy]);
3603 else if (IS_ACTIVE_BOMB(element))
3604 DrawLevelElement(jx, jy, EL_EMPTY);
3607 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3609 int old_element = GfxElement[jx][jy];
3610 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3611 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3613 if (GFX_CRUMBLED(old_element))
3614 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3616 DrawGraphic(sx, sy, old_graphic, frame);
3618 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3619 player_is_opaque = TRUE;
3623 GfxElement[jx][jy] = EL_UNDEFINED;
3625 /* make sure that pushed elements are drawn with correct frame rate */
3626 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3628 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3629 GfxFrame[jx][jy] = player->StepFrame;
3631 DrawLevelField(jx, jy);
3635 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3636 /* ----------------------------------------------------------------------- */
3637 /* draw player himself */
3638 /* ----------------------------------------------------------------------- */
3640 graphic = getPlayerGraphic(player, move_dir);
3642 /* in the case of changed player action or direction, prevent the current
3643 animation frame from being restarted for identical animations */
3644 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3645 player->Frame = last_player_frame;
3647 frame = getGraphicAnimationFrame(graphic, player->Frame);
3651 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3652 sxx = player->GfxPos;
3654 syy = player->GfxPos;
3657 if (player_is_opaque)
3658 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3660 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3662 if (SHIELD_ON(player))
3664 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3665 IMG_SHIELD_NORMAL_ACTIVE);
3666 int frame = getGraphicAnimationFrame(graphic, -1);
3668 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3672 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3675 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3676 sxx = player->GfxPos;
3678 syy = player->GfxPos;
3682 /* ----------------------------------------------------------------------- */
3683 /* draw things the player is pushing, if needed */
3684 /* ----------------------------------------------------------------------- */
3686 if (player->is_pushing && player->is_moving)
3688 int px = SCREENX(jx), py = SCREENY(jy);
3689 int pxx = (TILEX - ABS(sxx)) * dx;
3690 int pyy = (TILEY - ABS(syy)) * dy;
3691 int gfx_frame = GfxFrame[jx][jy];
3697 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3699 element = Feld[next_jx][next_jy];
3700 gfx_frame = GfxFrame[next_jx][next_jy];
3703 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3705 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3706 frame = getGraphicAnimationFrame(graphic, sync_frame);
3708 /* draw background element under pushed element (like the Sokoban field) */
3709 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3711 /* this allows transparent pushing animation over non-black background */
3714 DrawLevelElement(jx, jy, Back[jx][jy]);
3716 DrawLevelElement(jx, jy, EL_EMPTY);
3718 if (Back[next_jx][next_jy])
3719 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3721 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3723 else if (Back[next_jx][next_jy])
3724 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3727 /* do not draw (EM style) pushing animation when pushing is finished */
3728 /* (two-tile animations usually do not contain start and end frame) */
3729 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3730 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3732 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3734 /* masked drawing is needed for EMC style (double) movement graphics */
3735 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3736 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3740 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3741 /* ----------------------------------------------------------------------- */
3742 /* draw player himself */
3743 /* ----------------------------------------------------------------------- */
3745 graphic = getPlayerGraphic(player, move_dir);
3747 /* in the case of changed player action or direction, prevent the current
3748 animation frame from being restarted for identical animations */
3749 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3750 player->Frame = last_player_frame;
3752 frame = getGraphicAnimationFrame(graphic, player->Frame);
3756 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3757 sxx = player->GfxPos;
3759 syy = player->GfxPos;
3762 if (player_is_opaque)
3763 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3765 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3767 if (SHIELD_ON(player))
3769 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3770 IMG_SHIELD_NORMAL_ACTIVE);
3771 int frame = getGraphicAnimationFrame(graphic, -1);
3773 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3777 /* ----------------------------------------------------------------------- */
3778 /* draw things in front of player (active dynamite or dynabombs) */
3779 /* ----------------------------------------------------------------------- */
3781 if (IS_ACTIVE_BOMB(element))
3783 graphic = el2img(element);
3784 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3786 if (game.emulation == EMU_SUPAPLEX)
3787 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3789 DrawGraphicThruMask(sx, sy, graphic, frame);
3792 if (player_is_moving && last_element == EL_EXPLOSION)
3794 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3795 GfxElement[last_jx][last_jy] : EL_EMPTY);
3796 int graphic = el_act2img(element, ACTION_EXPLODING);
3797 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3798 int phase = ExplodePhase[last_jx][last_jy] - 1;
3799 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3802 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3805 /* ----------------------------------------------------------------------- */
3806 /* draw elements the player is just walking/passing through/under */
3807 /* ----------------------------------------------------------------------- */
3809 if (player_is_moving)
3811 /* handle the field the player is leaving ... */
3812 if (IS_ACCESSIBLE_INSIDE(last_element))
3813 DrawLevelField(last_jx, last_jy);
3814 else if (IS_ACCESSIBLE_UNDER(last_element))
3815 DrawLevelFieldThruMask(last_jx, last_jy);
3818 /* do not redraw accessible elements if the player is just pushing them */
3819 if (!player_is_moving || !player->is_pushing)
3821 /* ... and the field the player is entering */
3822 if (IS_ACCESSIBLE_INSIDE(element))
3823 DrawLevelField(jx, jy);
3824 else if (IS_ACCESSIBLE_UNDER(element))
3825 DrawLevelFieldThruMask(jx, jy);
3828 MarkTileDirty(sx, sy);
3831 /* ------------------------------------------------------------------------- */
3833 void WaitForEventToContinue()
3835 boolean still_wait = TRUE;
3837 if (program.headless)
3840 /* simulate releasing mouse button over last gadget, if still pressed */
3842 HandleGadgets(-1, -1, 0);
3844 button_status = MB_RELEASED;
3852 if (NextValidEvent(&event))
3856 case EVENT_BUTTONPRESS:
3857 case EVENT_KEYPRESS:
3858 #if defined(TARGET_SDL2)
3859 case SDL_CONTROLLERBUTTONDOWN:
3861 case SDL_JOYBUTTONDOWN:
3865 case EVENT_KEYRELEASE:
3866 ClearPlayerAction();
3870 HandleOtherEvents(&event);
3874 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3883 #define MAX_REQUEST_LINES 13
3884 #define MAX_REQUEST_LINE_FONT1_LEN 7
3885 #define MAX_REQUEST_LINE_FONT2_LEN 10
3887 static int RequestHandleEvents(unsigned int req_state)
3889 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3890 local_player->LevelSolved_GameEnd);
3891 int width = request.width;
3892 int height = request.height;
3896 setRequestPosition(&sx, &sy, FALSE);
3898 button_status = MB_RELEASED;
3900 request_gadget_id = -1;
3907 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3909 HandleGameActions();
3911 SetDrawtoField(DRAW_TO_BACKBUFFER);
3913 if (global.use_envelope_request)
3915 /* copy current state of request area to middle of playfield area */
3916 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
3924 while (NextValidEvent(&event))
3928 case EVENT_BUTTONPRESS:
3929 case EVENT_BUTTONRELEASE:
3930 case EVENT_MOTIONNOTIFY:
3934 if (event.type == EVENT_MOTIONNOTIFY)
3939 motion_status = TRUE;
3940 mx = ((MotionEvent *) &event)->x;
3941 my = ((MotionEvent *) &event)->y;
3945 motion_status = FALSE;
3946 mx = ((ButtonEvent *) &event)->x;
3947 my = ((ButtonEvent *) &event)->y;
3948 if (event.type == EVENT_BUTTONPRESS)
3949 button_status = ((ButtonEvent *) &event)->button;
3951 button_status = MB_RELEASED;
3954 /* this sets 'request_gadget_id' */
3955 HandleGadgets(mx, my, button_status);
3957 switch (request_gadget_id)
3959 case TOOL_CTRL_ID_YES:
3962 case TOOL_CTRL_ID_NO:
3965 case TOOL_CTRL_ID_CONFIRM:
3966 result = TRUE | FALSE;
3969 case TOOL_CTRL_ID_PLAYER_1:
3972 case TOOL_CTRL_ID_PLAYER_2:
3975 case TOOL_CTRL_ID_PLAYER_3:
3978 case TOOL_CTRL_ID_PLAYER_4:
3989 #if defined(TARGET_SDL2)
3990 case SDL_WINDOWEVENT:
3991 HandleWindowEvent((WindowEvent *) &event);
3994 case SDL_APP_WILLENTERBACKGROUND:
3995 case SDL_APP_DIDENTERBACKGROUND:
3996 case SDL_APP_WILLENTERFOREGROUND:
3997 case SDL_APP_DIDENTERFOREGROUND:
3998 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4002 case EVENT_KEYPRESS:
4004 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4009 if (req_state & REQ_CONFIRM)
4014 #if defined(TARGET_SDL2)
4017 #if defined(KSYM_Rewind)
4018 case KSYM_Rewind: /* for Amazon Fire TV remote */
4025 #if defined(TARGET_SDL2)
4027 #if defined(KSYM_FastForward)
4028 case KSYM_FastForward: /* for Amazon Fire TV remote */
4035 HandleKeysDebug(key);
4039 if (req_state & REQ_PLAYER)
4045 case EVENT_KEYRELEASE:
4046 ClearPlayerAction();
4049 #if defined(TARGET_SDL2)
4050 case SDL_CONTROLLERBUTTONDOWN:
4051 switch (event.cbutton.button)
4053 case SDL_CONTROLLER_BUTTON_A:
4054 case SDL_CONTROLLER_BUTTON_X:
4055 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4059 case SDL_CONTROLLER_BUTTON_B:
4060 case SDL_CONTROLLER_BUTTON_Y:
4061 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4062 case SDL_CONTROLLER_BUTTON_BACK:
4067 if (req_state & REQ_PLAYER)
4072 case SDL_CONTROLLERBUTTONUP:
4073 HandleJoystickEvent(&event);
4074 ClearPlayerAction();
4079 HandleOtherEvents(&event);
4084 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4086 int joy = AnyJoystick();
4088 if (joy & JOY_BUTTON_1)
4090 else if (joy & JOY_BUTTON_2)
4096 if (global.use_envelope_request)
4098 /* copy back current state of pressed buttons inside request area */
4099 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4109 static boolean RequestDoor(char *text, unsigned int req_state)
4111 unsigned int old_door_state;
4112 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4113 int font_nr = FONT_TEXT_2;
4118 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4120 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4121 font_nr = FONT_TEXT_1;
4124 if (game_status == GAME_MODE_PLAYING)
4125 BlitScreenToBitmap(backbuffer);
4127 /* disable deactivated drawing when quick-loading level tape recording */
4128 if (tape.playing && tape.deactivate_display)
4129 TapeDeactivateDisplayOff(TRUE);
4131 SetMouseCursor(CURSOR_DEFAULT);
4133 #if defined(NETWORK_AVALIABLE)
4134 /* pause network game while waiting for request to answer */
4135 if (options.network &&
4136 game_status == GAME_MODE_PLAYING &&
4137 req_state & REQUEST_WAIT_FOR_INPUT)
4138 SendToServer_PausePlaying();
4141 old_door_state = GetDoorState();
4143 /* simulate releasing mouse button over last gadget, if still pressed */
4145 HandleGadgets(-1, -1, 0);
4149 /* draw released gadget before proceeding */
4152 if (old_door_state & DOOR_OPEN_1)
4154 CloseDoor(DOOR_CLOSE_1);
4156 /* save old door content */
4157 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4158 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4161 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4162 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4164 /* clear door drawing field */
4165 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4167 /* force DOOR font inside door area */
4168 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4170 /* write text for request */
4171 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4173 char text_line[max_request_line_len + 1];
4179 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4181 tc = *(text_ptr + tx);
4182 // if (!tc || tc == ' ')
4183 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4187 if ((tc == '?' || tc == '!') && tl == 0)
4197 strncpy(text_line, text_ptr, tl);
4200 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4201 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4202 text_line, font_nr);
4204 text_ptr += tl + (tc == ' ' ? 1 : 0);
4205 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4210 if (req_state & REQ_ASK)
4212 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4213 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4215 else if (req_state & REQ_CONFIRM)
4217 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4219 else if (req_state & REQ_PLAYER)
4221 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4222 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4223 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4224 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4227 /* copy request gadgets to door backbuffer */
4228 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4230 OpenDoor(DOOR_OPEN_1);
4232 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4234 if (game_status == GAME_MODE_PLAYING)
4236 SetPanelBackground();
4237 SetDrawBackgroundMask(REDRAW_DOOR_1);
4241 SetDrawBackgroundMask(REDRAW_FIELD);
4247 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4249 // ---------- handle request buttons ----------
4250 result = RequestHandleEvents(req_state);
4254 if (!(req_state & REQ_STAY_OPEN))
4256 CloseDoor(DOOR_CLOSE_1);
4258 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4259 (req_state & REQ_REOPEN))
4260 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4265 if (game_status == GAME_MODE_PLAYING)
4267 SetPanelBackground();
4268 SetDrawBackgroundMask(REDRAW_DOOR_1);
4272 SetDrawBackgroundMask(REDRAW_FIELD);
4275 #if defined(NETWORK_AVALIABLE)
4276 /* continue network game after request */
4277 if (options.network &&
4278 game_status == GAME_MODE_PLAYING &&
4279 req_state & REQUEST_WAIT_FOR_INPUT)
4280 SendToServer_ContinuePlaying();
4283 /* restore deactivated drawing when quick-loading level tape recording */
4284 if (tape.playing && tape.deactivate_display)
4285 TapeDeactivateDisplayOn();
4290 static boolean RequestEnvelope(char *text, unsigned int req_state)
4294 if (game_status == GAME_MODE_PLAYING)
4295 BlitScreenToBitmap(backbuffer);
4297 /* disable deactivated drawing when quick-loading level tape recording */
4298 if (tape.playing && tape.deactivate_display)
4299 TapeDeactivateDisplayOff(TRUE);
4301 SetMouseCursor(CURSOR_DEFAULT);
4303 #if defined(NETWORK_AVALIABLE)
4304 /* pause network game while waiting for request to answer */
4305 if (options.network &&
4306 game_status == GAME_MODE_PLAYING &&
4307 req_state & REQUEST_WAIT_FOR_INPUT)
4308 SendToServer_PausePlaying();
4311 /* simulate releasing mouse button over last gadget, if still pressed */
4313 HandleGadgets(-1, -1, 0);
4317 // (replace with setting corresponding request background)
4318 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4319 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4321 /* clear door drawing field */
4322 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4324 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4326 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4328 if (game_status == GAME_MODE_PLAYING)
4330 SetPanelBackground();
4331 SetDrawBackgroundMask(REDRAW_DOOR_1);
4335 SetDrawBackgroundMask(REDRAW_FIELD);
4341 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4343 // ---------- handle request buttons ----------
4344 result = RequestHandleEvents(req_state);
4348 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4352 if (game_status == GAME_MODE_PLAYING)
4354 SetPanelBackground();
4355 SetDrawBackgroundMask(REDRAW_DOOR_1);
4359 SetDrawBackgroundMask(REDRAW_FIELD);
4362 #if defined(NETWORK_AVALIABLE)
4363 /* continue network game after request */
4364 if (options.network &&
4365 game_status == GAME_MODE_PLAYING &&
4366 req_state & REQUEST_WAIT_FOR_INPUT)
4367 SendToServer_ContinuePlaying();
4370 /* restore deactivated drawing when quick-loading level tape recording */
4371 if (tape.playing && tape.deactivate_display)
4372 TapeDeactivateDisplayOn();
4377 boolean Request(char *text, unsigned int req_state)
4379 boolean overlay_active = GetOverlayActive();
4382 SetOverlayActive(FALSE);
4384 if (global.use_envelope_request)
4385 result = RequestEnvelope(text, req_state);
4387 result = RequestDoor(text, req_state);
4389 SetOverlayActive(overlay_active);
4394 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4396 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4397 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4400 if (dpo1->sort_priority != dpo2->sort_priority)
4401 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4403 compare_result = dpo1->nr - dpo2->nr;
4405 return compare_result;
4408 void InitGraphicCompatibilityInfo_Doors()
4414 struct DoorInfo *door;
4418 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4419 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4421 { -1, -1, -1, NULL }
4423 struct Rect door_rect_list[] =
4425 { DX, DY, DXSIZE, DYSIZE },
4426 { VX, VY, VXSIZE, VYSIZE }
4430 for (i = 0; doors[i].door_token != -1; i++)
4432 int door_token = doors[i].door_token;
4433 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4434 int part_1 = doors[i].part_1;
4435 int part_8 = doors[i].part_8;
4436 int part_2 = part_1 + 1;
4437 int part_3 = part_1 + 2;
4438 struct DoorInfo *door = doors[i].door;
4439 struct Rect *door_rect = &door_rect_list[door_index];
4440 boolean door_gfx_redefined = FALSE;
4442 /* check if any door part graphic definitions have been redefined */
4444 for (j = 0; door_part_controls[j].door_token != -1; j++)
4446 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4447 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4449 if (dpc->door_token == door_token && fi->redefined)
4450 door_gfx_redefined = TRUE;
4453 /* check for old-style door graphic/animation modifications */
4455 if (!door_gfx_redefined)
4457 if (door->anim_mode & ANIM_STATIC_PANEL)
4459 door->panel.step_xoffset = 0;
4460 door->panel.step_yoffset = 0;
4463 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4465 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4466 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4467 int num_door_steps, num_panel_steps;
4469 /* remove door part graphics other than the two default wings */
4471 for (j = 0; door_part_controls[j].door_token != -1; j++)
4473 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4474 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4476 if (dpc->graphic >= part_3 &&
4477 dpc->graphic <= part_8)
4481 /* set graphics and screen positions of the default wings */
4483 g_part_1->width = door_rect->width;
4484 g_part_1->height = door_rect->height;
4485 g_part_2->width = door_rect->width;
4486 g_part_2->height = door_rect->height;
4487 g_part_2->src_x = door_rect->width;
4488 g_part_2->src_y = g_part_1->src_y;
4490 door->part_2.x = door->part_1.x;
4491 door->part_2.y = door->part_1.y;
4493 if (door->width != -1)
4495 g_part_1->width = door->width;
4496 g_part_2->width = door->width;
4498 // special treatment for graphics and screen position of right wing
4499 g_part_2->src_x += door_rect->width - door->width;
4500 door->part_2.x += door_rect->width - door->width;
4503 if (door->height != -1)
4505 g_part_1->height = door->height;
4506 g_part_2->height = door->height;
4508 // special treatment for graphics and screen position of bottom wing
4509 g_part_2->src_y += door_rect->height - door->height;
4510 door->part_2.y += door_rect->height - door->height;
4513 /* set animation delays for the default wings and panels */
4515 door->part_1.step_delay = door->step_delay;
4516 door->part_2.step_delay = door->step_delay;
4517 door->panel.step_delay = door->step_delay;
4519 /* set animation draw order for the default wings */
4521 door->part_1.sort_priority = 2; /* draw left wing over ... */
4522 door->part_2.sort_priority = 1; /* ... right wing */
4524 /* set animation draw offset for the default wings */
4526 if (door->anim_mode & ANIM_HORIZONTAL)
4528 door->part_1.step_xoffset = door->step_offset;
4529 door->part_1.step_yoffset = 0;
4530 door->part_2.step_xoffset = door->step_offset * -1;
4531 door->part_2.step_yoffset = 0;
4533 num_door_steps = g_part_1->width / door->step_offset;
4535 else // ANIM_VERTICAL
4537 door->part_1.step_xoffset = 0;
4538 door->part_1.step_yoffset = door->step_offset;
4539 door->part_2.step_xoffset = 0;
4540 door->part_2.step_yoffset = door->step_offset * -1;
4542 num_door_steps = g_part_1->height / door->step_offset;
4545 /* set animation draw offset for the default panels */
4547 if (door->step_offset > 1)
4549 num_panel_steps = 2 * door_rect->height / door->step_offset;
4550 door->panel.start_step = num_panel_steps - num_door_steps;
4551 door->panel.start_step_closing = door->panel.start_step;
4555 num_panel_steps = door_rect->height / door->step_offset;
4556 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4557 door->panel.start_step_closing = door->panel.start_step;
4558 door->panel.step_delay *= 2;
4569 for (i = 0; door_part_controls[i].door_token != -1; i++)
4571 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4572 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4574 /* initialize "start_step_opening" and "start_step_closing", if needed */
4575 if (dpc->pos->start_step_opening == 0 &&
4576 dpc->pos->start_step_closing == 0)
4578 // dpc->pos->start_step_opening = dpc->pos->start_step;
4579 dpc->pos->start_step_closing = dpc->pos->start_step;
4582 /* fill structure for door part draw order (sorted below) */
4584 dpo->sort_priority = dpc->pos->sort_priority;
4587 /* sort door part controls according to sort_priority and graphic number */
4588 qsort(door_part_order, MAX_DOOR_PARTS,
4589 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4592 unsigned int OpenDoor(unsigned int door_state)
4594 if (door_state & DOOR_COPY_BACK)
4596 if (door_state & DOOR_OPEN_1)
4597 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4598 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4600 if (door_state & DOOR_OPEN_2)
4601 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4602 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4604 door_state &= ~DOOR_COPY_BACK;
4607 return MoveDoor(door_state);
4610 unsigned int CloseDoor(unsigned int door_state)
4612 unsigned int old_door_state = GetDoorState();
4614 if (!(door_state & DOOR_NO_COPY_BACK))
4616 if (old_door_state & DOOR_OPEN_1)
4617 BlitBitmap(backbuffer, bitmap_db_door_1,
4618 DX, DY, DXSIZE, DYSIZE, 0, 0);
4620 if (old_door_state & DOOR_OPEN_2)
4621 BlitBitmap(backbuffer, bitmap_db_door_2,
4622 VX, VY, VXSIZE, VYSIZE, 0, 0);
4624 door_state &= ~DOOR_NO_COPY_BACK;
4627 return MoveDoor(door_state);
4630 unsigned int GetDoorState()
4632 return MoveDoor(DOOR_GET_STATE);
4635 unsigned int SetDoorState(unsigned int door_state)
4637 return MoveDoor(door_state | DOOR_SET_STATE);
4640 int euclid(int a, int b)
4642 return (b ? euclid(b, a % b) : a);
4645 unsigned int MoveDoor(unsigned int door_state)
4647 struct Rect door_rect_list[] =
4649 { DX, DY, DXSIZE, DYSIZE },
4650 { VX, VY, VXSIZE, VYSIZE }
4652 static int door1 = DOOR_CLOSE_1;
4653 static int door2 = DOOR_CLOSE_2;
4654 unsigned int door_delay = 0;
4655 unsigned int door_delay_value;
4658 if (door_state == DOOR_GET_STATE)
4659 return (door1 | door2);
4661 if (door_state & DOOR_SET_STATE)
4663 if (door_state & DOOR_ACTION_1)
4664 door1 = door_state & DOOR_ACTION_1;
4665 if (door_state & DOOR_ACTION_2)
4666 door2 = door_state & DOOR_ACTION_2;
4668 return (door1 | door2);
4671 if (!(door_state & DOOR_FORCE_REDRAW))
4673 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4674 door_state &= ~DOOR_OPEN_1;
4675 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4676 door_state &= ~DOOR_CLOSE_1;
4677 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4678 door_state &= ~DOOR_OPEN_2;
4679 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4680 door_state &= ~DOOR_CLOSE_2;
4683 if (global.autoplay_leveldir)
4685 door_state |= DOOR_NO_DELAY;
4686 door_state &= ~DOOR_CLOSE_ALL;
4689 if (game_status == GAME_MODE_EDITOR)
4690 door_state |= DOOR_NO_DELAY;
4692 if (door_state & DOOR_ACTION)
4694 boolean door_panel_drawn[NUM_DOORS];
4695 boolean panel_has_doors[NUM_DOORS];
4696 boolean door_part_skip[MAX_DOOR_PARTS];
4697 boolean door_part_done[MAX_DOOR_PARTS];
4698 boolean door_part_done_all;
4699 int num_steps[MAX_DOOR_PARTS];
4700 int max_move_delay = 0; // delay for complete animations of all doors
4701 int max_step_delay = 0; // delay (ms) between two animation frames
4702 int num_move_steps = 0; // number of animation steps for all doors
4703 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4704 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4705 int current_move_delay = 0;
4709 for (i = 0; i < NUM_DOORS; i++)
4710 panel_has_doors[i] = FALSE;
4712 for (i = 0; i < MAX_DOOR_PARTS; i++)
4714 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4715 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4716 int door_token = dpc->door_token;
4718 door_part_done[i] = FALSE;
4719 door_part_skip[i] = (!(door_state & door_token) ||
4723 for (i = 0; i < MAX_DOOR_PARTS; i++)
4725 int nr = door_part_order[i].nr;
4726 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4727 struct DoorPartPosInfo *pos = dpc->pos;
4728 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4729 int door_token = dpc->door_token;
4730 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4731 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4732 int step_xoffset = ABS(pos->step_xoffset);
4733 int step_yoffset = ABS(pos->step_yoffset);
4734 int step_delay = pos->step_delay;
4735 int current_door_state = door_state & door_token;
4736 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4737 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4738 boolean part_opening = (is_panel ? door_closing : door_opening);
4739 int start_step = (part_opening ? pos->start_step_opening :
4740 pos->start_step_closing);
4741 float move_xsize = (step_xoffset ? g->width : 0);
4742 float move_ysize = (step_yoffset ? g->height : 0);
4743 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4744 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4745 int move_steps = (move_xsteps && move_ysteps ?
4746 MIN(move_xsteps, move_ysteps) :
4747 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4748 int move_delay = move_steps * step_delay;
4750 if (door_part_skip[nr])
4753 max_move_delay = MAX(max_move_delay, move_delay);
4754 max_step_delay = (max_step_delay == 0 ? step_delay :
4755 euclid(max_step_delay, step_delay));
4756 num_steps[nr] = move_steps;
4760 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4762 panel_has_doors[door_index] = TRUE;
4766 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4768 num_move_steps = max_move_delay / max_step_delay;
4769 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4771 door_delay_value = max_step_delay;
4773 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4775 start = num_move_steps - 1;
4779 /* opening door sound has priority over simultaneously closing door */
4780 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4781 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4782 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4783 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4786 for (k = start; k < num_move_steps; k++)
4788 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4790 door_part_done_all = TRUE;
4792 for (i = 0; i < NUM_DOORS; i++)
4793 door_panel_drawn[i] = FALSE;
4795 for (i = 0; i < MAX_DOOR_PARTS; i++)
4797 int nr = door_part_order[i].nr;
4798 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4799 struct DoorPartPosInfo *pos = dpc->pos;
4800 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4801 int door_token = dpc->door_token;
4802 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4803 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4804 boolean is_panel_and_door_has_closed = FALSE;
4805 struct Rect *door_rect = &door_rect_list[door_index];
4806 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4808 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4809 int current_door_state = door_state & door_token;
4810 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4811 boolean door_closing = !door_opening;
4812 boolean part_opening = (is_panel ? door_closing : door_opening);
4813 boolean part_closing = !part_opening;
4814 int start_step = (part_opening ? pos->start_step_opening :
4815 pos->start_step_closing);
4816 int step_delay = pos->step_delay;
4817 int step_factor = step_delay / max_step_delay;
4818 int k1 = (step_factor ? k / step_factor + 1 : k);
4819 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4820 int kk = MAX(0, k2);
4823 int src_x, src_y, src_xx, src_yy;
4824 int dst_x, dst_y, dst_xx, dst_yy;
4827 if (door_part_skip[nr])
4830 if (!(door_state & door_token))
4838 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4839 int kk_door = MAX(0, k2_door);
4840 int sync_frame = kk_door * door_delay_value;
4841 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4843 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
4844 &g_src_x, &g_src_y);
4849 if (!door_panel_drawn[door_index])
4851 ClearRectangle(drawto, door_rect->x, door_rect->y,
4852 door_rect->width, door_rect->height);
4854 door_panel_drawn[door_index] = TRUE;
4857 // draw opening or closing door parts
4859 if (pos->step_xoffset < 0) // door part on right side
4862 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4865 if (dst_xx + width > door_rect->width)
4866 width = door_rect->width - dst_xx;
4868 else // door part on left side
4871 dst_xx = pos->x - kk * pos->step_xoffset;
4875 src_xx = ABS(dst_xx);
4879 width = g->width - src_xx;
4881 if (width > door_rect->width)
4882 width = door_rect->width;
4884 // printf("::: k == %d [%d] \n", k, start_step);
4887 if (pos->step_yoffset < 0) // door part on bottom side
4890 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4893 if (dst_yy + height > door_rect->height)
4894 height = door_rect->height - dst_yy;
4896 else // door part on top side
4899 dst_yy = pos->y - kk * pos->step_yoffset;
4903 src_yy = ABS(dst_yy);
4907 height = g->height - src_yy;
4910 src_x = g_src_x + src_xx;
4911 src_y = g_src_y + src_yy;
4913 dst_x = door_rect->x + dst_xx;
4914 dst_y = door_rect->y + dst_yy;
4916 is_panel_and_door_has_closed =
4919 panel_has_doors[door_index] &&
4920 k >= num_move_steps_doors_only - 1);
4922 if (width >= 0 && width <= g->width &&
4923 height >= 0 && height <= g->height &&
4924 !is_panel_and_door_has_closed)
4926 if (is_panel || !pos->draw_masked)
4927 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4930 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4934 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4936 if ((part_opening && (width < 0 || height < 0)) ||
4937 (part_closing && (width >= g->width && height >= g->height)))
4938 door_part_done[nr] = TRUE;
4940 // continue door part animations, but not panel after door has closed
4941 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4942 door_part_done_all = FALSE;
4945 if (!(door_state & DOOR_NO_DELAY))
4949 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4951 current_move_delay += max_step_delay;
4953 /* prevent OS (Windows) from complaining about program not responding */
4957 if (door_part_done_all)
4962 if (door_state & DOOR_ACTION_1)
4963 door1 = door_state & DOOR_ACTION_1;
4964 if (door_state & DOOR_ACTION_2)
4965 door2 = door_state & DOOR_ACTION_2;
4967 // draw masked border over door area
4968 DrawMaskedBorder(REDRAW_DOOR_1);
4969 DrawMaskedBorder(REDRAW_DOOR_2);
4971 return (door1 | door2);
4974 static boolean useSpecialEditorDoor()
4976 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4977 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4979 // do not draw special editor door if editor border defined or redefined
4980 if (graphic_info[graphic].bitmap != NULL || redefined)
4983 // do not draw special editor door if global border defined to be empty
4984 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4987 // do not draw special editor door if viewport definitions do not match
4991 EY + EYSIZE != VY + VYSIZE)
4997 void DrawSpecialEditorDoor()
4999 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5000 int top_border_width = gfx1->width;
5001 int top_border_height = gfx1->height;
5002 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5003 int ex = EX - outer_border;
5004 int ey = EY - outer_border;
5005 int vy = VY - outer_border;
5006 int exsize = EXSIZE + 2 * outer_border;
5008 if (!useSpecialEditorDoor())
5011 /* draw bigger level editor toolbox window */
5012 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5013 top_border_width, top_border_height, ex, ey - top_border_height);
5014 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5015 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5017 redraw_mask |= REDRAW_ALL;
5020 void UndrawSpecialEditorDoor()
5022 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5023 int top_border_width = gfx1->width;
5024 int top_border_height = gfx1->height;
5025 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5026 int ex = EX - outer_border;
5027 int ey = EY - outer_border;
5028 int ey_top = ey - top_border_height;
5029 int exsize = EXSIZE + 2 * outer_border;
5030 int eysize = EYSIZE + 2 * outer_border;
5032 if (!useSpecialEditorDoor())
5035 /* draw normal tape recorder window */
5036 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5038 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5039 ex, ey_top, top_border_width, top_border_height,
5041 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5042 ex, ey, exsize, eysize, ex, ey);
5046 // if screen background is set to "[NONE]", clear editor toolbox window
5047 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5048 ClearRectangle(drawto, ex, ey, exsize, eysize);
5051 redraw_mask |= REDRAW_ALL;
5055 /* ---------- new tool button stuff ---------------------------------------- */
5060 struct TextPosInfo *pos;
5063 } toolbutton_info[NUM_TOOL_BUTTONS] =
5066 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5067 TOOL_CTRL_ID_YES, "yes"
5070 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5071 TOOL_CTRL_ID_NO, "no"
5074 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5075 TOOL_CTRL_ID_CONFIRM, "confirm"
5078 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5079 TOOL_CTRL_ID_PLAYER_1, "player 1"
5082 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5083 TOOL_CTRL_ID_PLAYER_2, "player 2"
5086 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5087 TOOL_CTRL_ID_PLAYER_3, "player 3"
5090 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5091 TOOL_CTRL_ID_PLAYER_4, "player 4"
5095 void CreateToolButtons()
5099 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5101 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
5102 struct TextPosInfo *pos = toolbutton_info[i].pos;
5103 struct GadgetInfo *gi;
5104 Bitmap *deco_bitmap = None;
5105 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5106 unsigned int event_mask = GD_EVENT_RELEASED;
5109 int gd_x = gfx->src_x;
5110 int gd_y = gfx->src_y;
5111 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5112 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5115 if (global.use_envelope_request)
5116 setRequestPosition(&dx, &dy, TRUE);
5118 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5120 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5122 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5123 pos->size, &deco_bitmap, &deco_x, &deco_y);
5124 deco_xpos = (gfx->width - pos->size) / 2;
5125 deco_ypos = (gfx->height - pos->size) / 2;
5128 gi = CreateGadget(GDI_CUSTOM_ID, id,
5129 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5130 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
5131 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
5132 GDI_WIDTH, gfx->width,
5133 GDI_HEIGHT, gfx->height,
5134 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5135 GDI_STATE, GD_BUTTON_UNPRESSED,
5136 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5137 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5138 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5139 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5140 GDI_DECORATION_SIZE, pos->size, pos->size,
5141 GDI_DECORATION_SHIFTING, 1, 1,
5142 GDI_DIRECT_DRAW, FALSE,
5143 GDI_EVENT_MASK, event_mask,
5144 GDI_CALLBACK_ACTION, HandleToolButtons,
5148 Error(ERR_EXIT, "cannot create gadget");
5150 tool_gadget[id] = gi;
5154 void FreeToolButtons()
5158 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5159 FreeGadget(tool_gadget[i]);
5162 static void UnmapToolButtons()
5166 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5167 UnmapGadget(tool_gadget[i]);
5170 static void HandleToolButtons(struct GadgetInfo *gi)
5172 request_gadget_id = gi->custom_id;
5175 static struct Mapping_EM_to_RND_object
5178 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
5179 boolean is_backside; /* backside of moving element */
5185 em_object_mapping_list[] =
5188 Xblank, TRUE, FALSE,
5192 Yacid_splash_eB, FALSE, FALSE,
5193 EL_ACID_SPLASH_RIGHT, -1, -1
5196 Yacid_splash_wB, FALSE, FALSE,
5197 EL_ACID_SPLASH_LEFT, -1, -1
5200 #ifdef EM_ENGINE_BAD_ROLL
5202 Xstone_force_e, FALSE, FALSE,
5203 EL_ROCK, -1, MV_BIT_RIGHT
5206 Xstone_force_w, FALSE, FALSE,
5207 EL_ROCK, -1, MV_BIT_LEFT
5210 Xnut_force_e, FALSE, FALSE,
5211 EL_NUT, -1, MV_BIT_RIGHT
5214 Xnut_force_w, FALSE, FALSE,
5215 EL_NUT, -1, MV_BIT_LEFT
5218 Xspring_force_e, FALSE, FALSE,
5219 EL_SPRING, -1, MV_BIT_RIGHT
5222 Xspring_force_w, FALSE, FALSE,
5223 EL_SPRING, -1, MV_BIT_LEFT
5226 Xemerald_force_e, FALSE, FALSE,
5227 EL_EMERALD, -1, MV_BIT_RIGHT
5230 Xemerald_force_w, FALSE, FALSE,
5231 EL_EMERALD, -1, MV_BIT_LEFT
5234 Xdiamond_force_e, FALSE, FALSE,
5235 EL_DIAMOND, -1, MV_BIT_RIGHT
5238 Xdiamond_force_w, FALSE, FALSE,
5239 EL_DIAMOND, -1, MV_BIT_LEFT
5242 Xbomb_force_e, FALSE, FALSE,
5243 EL_BOMB, -1, MV_BIT_RIGHT
5246 Xbomb_force_w, FALSE, FALSE,
5247 EL_BOMB, -1, MV_BIT_LEFT
5249 #endif /* EM_ENGINE_BAD_ROLL */
5252 Xstone, TRUE, FALSE,
5256 Xstone_pause, FALSE, FALSE,
5260 Xstone_fall, FALSE, FALSE,
5264 Ystone_s, FALSE, FALSE,
5265 EL_ROCK, ACTION_FALLING, -1
5268 Ystone_sB, FALSE, TRUE,
5269 EL_ROCK, ACTION_FALLING, -1
5272 Ystone_e, FALSE, FALSE,
5273 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5276 Ystone_eB, FALSE, TRUE,
5277 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5280 Ystone_w, FALSE, FALSE,
5281 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5284 Ystone_wB, FALSE, TRUE,
5285 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5292 Xnut_pause, FALSE, FALSE,
5296 Xnut_fall, FALSE, FALSE,
5300 Ynut_s, FALSE, FALSE,
5301 EL_NUT, ACTION_FALLING, -1
5304 Ynut_sB, FALSE, TRUE,
5305 EL_NUT, ACTION_FALLING, -1
5308 Ynut_e, FALSE, FALSE,
5309 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5312 Ynut_eB, FALSE, TRUE,
5313 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5316 Ynut_w, FALSE, FALSE,
5317 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5320 Ynut_wB, FALSE, TRUE,
5321 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5324 Xbug_n, TRUE, FALSE,
5328 Xbug_e, TRUE, FALSE,
5329 EL_BUG_RIGHT, -1, -1
5332 Xbug_s, TRUE, FALSE,
5336 Xbug_w, TRUE, FALSE,
5340 Xbug_gon, FALSE, FALSE,
5344 Xbug_goe, FALSE, FALSE,
5345 EL_BUG_RIGHT, -1, -1
5348 Xbug_gos, FALSE, FALSE,
5352 Xbug_gow, FALSE, FALSE,
5356 Ybug_n, FALSE, FALSE,
5357 EL_BUG, ACTION_MOVING, MV_BIT_UP
5360 Ybug_nB, FALSE, TRUE,
5361 EL_BUG, ACTION_MOVING, MV_BIT_UP
5364 Ybug_e, FALSE, FALSE,
5365 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5368 Ybug_eB, FALSE, TRUE,
5369 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5372 Ybug_s, FALSE, FALSE,
5373 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5376 Ybug_sB, FALSE, TRUE,
5377 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5380 Ybug_w, FALSE, FALSE,
5381 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5384 Ybug_wB, FALSE, TRUE,
5385 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5388 Ybug_w_n, FALSE, FALSE,
5389 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5392 Ybug_n_e, FALSE, FALSE,
5393 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5396 Ybug_e_s, FALSE, FALSE,
5397 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5400 Ybug_s_w, FALSE, FALSE,
5401 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5404 Ybug_e_n, FALSE, FALSE,
5405 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5408 Ybug_s_e, FALSE, FALSE,
5409 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5412 Ybug_w_s, FALSE, FALSE,
5413 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5416 Ybug_n_w, FALSE, FALSE,
5417 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5420 Ybug_stone, FALSE, FALSE,
5421 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5424 Ybug_spring, FALSE, FALSE,
5425 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5428 Xtank_n, TRUE, FALSE,
5429 EL_SPACESHIP_UP, -1, -1
5432 Xtank_e, TRUE, FALSE,
5433 EL_SPACESHIP_RIGHT, -1, -1
5436 Xtank_s, TRUE, FALSE,
5437 EL_SPACESHIP_DOWN, -1, -1
5440 Xtank_w, TRUE, FALSE,
5441 EL_SPACESHIP_LEFT, -1, -1
5444 Xtank_gon, FALSE, FALSE,
5445 EL_SPACESHIP_UP, -1, -1
5448 Xtank_goe, FALSE, FALSE,
5449 EL_SPACESHIP_RIGHT, -1, -1
5452 Xtank_gos, FALSE, FALSE,
5453 EL_SPACESHIP_DOWN, -1, -1
5456 Xtank_gow, FALSE, FALSE,
5457 EL_SPACESHIP_LEFT, -1, -1
5460 Ytank_n, FALSE, FALSE,
5461 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5464 Ytank_nB, FALSE, TRUE,
5465 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5468 Ytank_e, FALSE, FALSE,
5469 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5472 Ytank_eB, FALSE, TRUE,
5473 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5476 Ytank_s, FALSE, FALSE,
5477 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5480 Ytank_sB, FALSE, TRUE,
5481 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5484 Ytank_w, FALSE, FALSE,
5485 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5488 Ytank_wB, FALSE, TRUE,
5489 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5492 Ytank_w_n, FALSE, FALSE,
5493 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5496 Ytank_n_e, FALSE, FALSE,
5497 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5500 Ytank_e_s, FALSE, FALSE,
5501 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5504 Ytank_s_w, FALSE, FALSE,
5505 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5508 Ytank_e_n, FALSE, FALSE,
5509 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5512 Ytank_s_e, FALSE, FALSE,
5513 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5516 Ytank_w_s, FALSE, FALSE,
5517 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5520 Ytank_n_w, FALSE, FALSE,
5521 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5524 Ytank_stone, FALSE, FALSE,
5525 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5528 Ytank_spring, FALSE, FALSE,
5529 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5532 Xandroid, TRUE, FALSE,
5533 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5536 Xandroid_1_n, FALSE, FALSE,
5537 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5540 Xandroid_2_n, FALSE, FALSE,
5541 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5544 Xandroid_1_e, FALSE, FALSE,
5545 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5548 Xandroid_2_e, FALSE, FALSE,
5549 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5552 Xandroid_1_w, FALSE, FALSE,
5553 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5556 Xandroid_2_w, FALSE, FALSE,
5557 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5560 Xandroid_1_s, FALSE, FALSE,
5561 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5564 Xandroid_2_s, FALSE, FALSE,
5565 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5568 Yandroid_n, FALSE, FALSE,
5569 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5572 Yandroid_nB, FALSE, TRUE,
5573 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5576 Yandroid_ne, FALSE, FALSE,
5577 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5580 Yandroid_neB, FALSE, TRUE,
5581 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5584 Yandroid_e, FALSE, FALSE,
5585 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5588 Yandroid_eB, FALSE, TRUE,
5589 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5592 Yandroid_se, FALSE, FALSE,
5593 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5596 Yandroid_seB, FALSE, TRUE,
5597 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5600 Yandroid_s, FALSE, FALSE,
5601 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5604 Yandroid_sB, FALSE, TRUE,
5605 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5608 Yandroid_sw, FALSE, FALSE,
5609 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5612 Yandroid_swB, FALSE, TRUE,
5613 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5616 Yandroid_w, FALSE, FALSE,
5617 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5620 Yandroid_wB, FALSE, TRUE,
5621 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5624 Yandroid_nw, FALSE, FALSE,
5625 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5628 Yandroid_nwB, FALSE, TRUE,
5629 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5632 Xspring, TRUE, FALSE,
5636 Xspring_pause, FALSE, FALSE,
5640 Xspring_e, FALSE, FALSE,
5644 Xspring_w, FALSE, FALSE,
5648 Xspring_fall, FALSE, FALSE,
5652 Yspring_s, FALSE, FALSE,
5653 EL_SPRING, ACTION_FALLING, -1
5656 Yspring_sB, FALSE, TRUE,
5657 EL_SPRING, ACTION_FALLING, -1
5660 Yspring_e, FALSE, FALSE,
5661 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5664 Yspring_eB, FALSE, TRUE,
5665 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5668 Yspring_w, FALSE, FALSE,
5669 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5672 Yspring_wB, FALSE, TRUE,
5673 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5676 Yspring_kill_e, FALSE, FALSE,
5677 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5680 Yspring_kill_eB, FALSE, TRUE,
5681 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5684 Yspring_kill_w, FALSE, FALSE,
5685 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5688 Yspring_kill_wB, FALSE, TRUE,
5689 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5692 Xeater_n, TRUE, FALSE,
5693 EL_YAMYAM_UP, -1, -1
5696 Xeater_e, TRUE, FALSE,
5697 EL_YAMYAM_RIGHT, -1, -1
5700 Xeater_w, TRUE, FALSE,
5701 EL_YAMYAM_LEFT, -1, -1
5704 Xeater_s, TRUE, FALSE,
5705 EL_YAMYAM_DOWN, -1, -1
5708 Yeater_n, FALSE, FALSE,
5709 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5712 Yeater_nB, FALSE, TRUE,
5713 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5716 Yeater_e, FALSE, FALSE,
5717 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5720 Yeater_eB, FALSE, TRUE,
5721 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5724 Yeater_s, FALSE, FALSE,
5725 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5728 Yeater_sB, FALSE, TRUE,
5729 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5732 Yeater_w, FALSE, FALSE,
5733 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5736 Yeater_wB, FALSE, TRUE,
5737 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5740 Yeater_stone, FALSE, FALSE,
5741 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5744 Yeater_spring, FALSE, FALSE,
5745 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5748 Xalien, TRUE, FALSE,
5752 Xalien_pause, FALSE, FALSE,
5756 Yalien_n, FALSE, FALSE,
5757 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5760 Yalien_nB, FALSE, TRUE,
5761 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5764 Yalien_e, FALSE, FALSE,
5765 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5768 Yalien_eB, FALSE, TRUE,
5769 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5772 Yalien_s, FALSE, FALSE,
5773 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5776 Yalien_sB, FALSE, TRUE,
5777 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5780 Yalien_w, FALSE, FALSE,
5781 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5784 Yalien_wB, FALSE, TRUE,
5785 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5788 Yalien_stone, FALSE, FALSE,
5789 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5792 Yalien_spring, FALSE, FALSE,
5793 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5796 Xemerald, TRUE, FALSE,
5800 Xemerald_pause, FALSE, FALSE,
5804 Xemerald_fall, FALSE, FALSE,
5808 Xemerald_shine, FALSE, FALSE,
5809 EL_EMERALD, ACTION_TWINKLING, -1
5812 Yemerald_s, FALSE, FALSE,
5813 EL_EMERALD, ACTION_FALLING, -1
5816 Yemerald_sB, FALSE, TRUE,
5817 EL_EMERALD, ACTION_FALLING, -1
5820 Yemerald_e, FALSE, FALSE,
5821 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5824 Yemerald_eB, FALSE, TRUE,
5825 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5828 Yemerald_w, FALSE, FALSE,
5829 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5832 Yemerald_wB, FALSE, TRUE,
5833 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5836 Yemerald_eat, FALSE, FALSE,
5837 EL_EMERALD, ACTION_COLLECTING, -1
5840 Yemerald_stone, FALSE, FALSE,
5841 EL_NUT, ACTION_BREAKING, -1
5844 Xdiamond, TRUE, FALSE,
5848 Xdiamond_pause, FALSE, FALSE,
5852 Xdiamond_fall, FALSE, FALSE,
5856 Xdiamond_shine, FALSE, FALSE,
5857 EL_DIAMOND, ACTION_TWINKLING, -1
5860 Ydiamond_s, FALSE, FALSE,
5861 EL_DIAMOND, ACTION_FALLING, -1
5864 Ydiamond_sB, FALSE, TRUE,
5865 EL_DIAMOND, ACTION_FALLING, -1
5868 Ydiamond_e, FALSE, FALSE,
5869 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5872 Ydiamond_eB, FALSE, TRUE,
5873 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5876 Ydiamond_w, FALSE, FALSE,
5877 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5880 Ydiamond_wB, FALSE, TRUE,
5881 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5884 Ydiamond_eat, FALSE, FALSE,
5885 EL_DIAMOND, ACTION_COLLECTING, -1
5888 Ydiamond_stone, FALSE, FALSE,
5889 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5892 Xdrip_fall, TRUE, FALSE,
5893 EL_AMOEBA_DROP, -1, -1
5896 Xdrip_stretch, FALSE, FALSE,
5897 EL_AMOEBA_DROP, ACTION_FALLING, -1
5900 Xdrip_stretchB, FALSE, TRUE,
5901 EL_AMOEBA_DROP, ACTION_FALLING, -1
5904 Xdrip_eat, FALSE, FALSE,
5905 EL_AMOEBA_DROP, ACTION_GROWING, -1
5908 Ydrip_s1, FALSE, FALSE,
5909 EL_AMOEBA_DROP, ACTION_FALLING, -1
5912 Ydrip_s1B, FALSE, TRUE,
5913 EL_AMOEBA_DROP, ACTION_FALLING, -1
5916 Ydrip_s2, FALSE, FALSE,
5917 EL_AMOEBA_DROP, ACTION_FALLING, -1
5920 Ydrip_s2B, FALSE, TRUE,
5921 EL_AMOEBA_DROP, ACTION_FALLING, -1
5928 Xbomb_pause, FALSE, FALSE,
5932 Xbomb_fall, FALSE, FALSE,
5936 Ybomb_s, FALSE, FALSE,
5937 EL_BOMB, ACTION_FALLING, -1
5940 Ybomb_sB, FALSE, TRUE,
5941 EL_BOMB, ACTION_FALLING, -1
5944 Ybomb_e, FALSE, FALSE,
5945 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5948 Ybomb_eB, FALSE, TRUE,
5949 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5952 Ybomb_w, FALSE, FALSE,
5953 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5956 Ybomb_wB, FALSE, TRUE,
5957 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5960 Ybomb_eat, FALSE, FALSE,
5961 EL_BOMB, ACTION_ACTIVATING, -1
5964 Xballoon, TRUE, FALSE,
5968 Yballoon_n, FALSE, FALSE,
5969 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5972 Yballoon_nB, FALSE, TRUE,
5973 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5976 Yballoon_e, FALSE, FALSE,
5977 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5980 Yballoon_eB, FALSE, TRUE,
5981 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5984 Yballoon_s, FALSE, FALSE,
5985 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5988 Yballoon_sB, FALSE, TRUE,
5989 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5992 Yballoon_w, FALSE, FALSE,
5993 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5996 Yballoon_wB, FALSE, TRUE,
5997 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6000 Xgrass, TRUE, FALSE,
6001 EL_EMC_GRASS, -1, -1
6004 Ygrass_nB, FALSE, FALSE,
6005 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6008 Ygrass_eB, FALSE, FALSE,
6009 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6012 Ygrass_sB, FALSE, FALSE,
6013 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6016 Ygrass_wB, FALSE, FALSE,
6017 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6024 Ydirt_nB, FALSE, FALSE,
6025 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6028 Ydirt_eB, FALSE, FALSE,
6029 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6032 Ydirt_sB, FALSE, FALSE,
6033 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6036 Ydirt_wB, FALSE, FALSE,
6037 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6040 Xacid_ne, TRUE, FALSE,
6041 EL_ACID_POOL_TOPRIGHT, -1, -1
6044 Xacid_se, TRUE, FALSE,
6045 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6048 Xacid_s, TRUE, FALSE,
6049 EL_ACID_POOL_BOTTOM, -1, -1
6052 Xacid_sw, TRUE, FALSE,
6053 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6056 Xacid_nw, TRUE, FALSE,
6057 EL_ACID_POOL_TOPLEFT, -1, -1
6060 Xacid_1, TRUE, FALSE,
6064 Xacid_2, FALSE, FALSE,
6068 Xacid_3, FALSE, FALSE,
6072 Xacid_4, FALSE, FALSE,
6076 Xacid_5, FALSE, FALSE,
6080 Xacid_6, FALSE, FALSE,
6084 Xacid_7, FALSE, FALSE,
6088 Xacid_8, FALSE, FALSE,
6092 Xball_1, TRUE, FALSE,
6093 EL_EMC_MAGIC_BALL, -1, -1
6096 Xball_1B, FALSE, FALSE,
6097 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6100 Xball_2, FALSE, FALSE,
6101 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6104 Xball_2B, FALSE, FALSE,
6105 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6108 Yball_eat, FALSE, FALSE,
6109 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6112 Ykey_1_eat, FALSE, FALSE,
6113 EL_EM_KEY_1, ACTION_COLLECTING, -1
6116 Ykey_2_eat, FALSE, FALSE,
6117 EL_EM_KEY_2, ACTION_COLLECTING, -1
6120 Ykey_3_eat, FALSE, FALSE,
6121 EL_EM_KEY_3, ACTION_COLLECTING, -1
6124 Ykey_4_eat, FALSE, FALSE,
6125 EL_EM_KEY_4, ACTION_COLLECTING, -1
6128 Ykey_5_eat, FALSE, FALSE,
6129 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6132 Ykey_6_eat, FALSE, FALSE,
6133 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6136 Ykey_7_eat, FALSE, FALSE,
6137 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6140 Ykey_8_eat, FALSE, FALSE,
6141 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6144 Ylenses_eat, FALSE, FALSE,
6145 EL_EMC_LENSES, ACTION_COLLECTING, -1
6148 Ymagnify_eat, FALSE, FALSE,
6149 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6152 Ygrass_eat, FALSE, FALSE,
6153 EL_EMC_GRASS, ACTION_SNAPPING, -1
6156 Ydirt_eat, FALSE, FALSE,
6157 EL_SAND, ACTION_SNAPPING, -1
6160 Xgrow_ns, TRUE, FALSE,
6161 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6164 Ygrow_ns_eat, FALSE, FALSE,
6165 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6168 Xgrow_ew, TRUE, FALSE,
6169 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6172 Ygrow_ew_eat, FALSE, FALSE,
6173 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6176 Xwonderwall, TRUE, FALSE,
6177 EL_MAGIC_WALL, -1, -1
6180 XwonderwallB, FALSE, FALSE,
6181 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6184 Xamoeba_1, TRUE, FALSE,
6185 EL_AMOEBA_DRY, ACTION_OTHER, -1
6188 Xamoeba_2, FALSE, FALSE,
6189 EL_AMOEBA_DRY, ACTION_OTHER, -1
6192 Xamoeba_3, FALSE, FALSE,
6193 EL_AMOEBA_DRY, ACTION_OTHER, -1
6196 Xamoeba_4, FALSE, FALSE,
6197 EL_AMOEBA_DRY, ACTION_OTHER, -1
6200 Xamoeba_5, TRUE, FALSE,
6201 EL_AMOEBA_WET, ACTION_OTHER, -1
6204 Xamoeba_6, FALSE, FALSE,
6205 EL_AMOEBA_WET, ACTION_OTHER, -1
6208 Xamoeba_7, FALSE, FALSE,
6209 EL_AMOEBA_WET, ACTION_OTHER, -1
6212 Xamoeba_8, FALSE, FALSE,
6213 EL_AMOEBA_WET, ACTION_OTHER, -1
6216 Xdoor_1, TRUE, FALSE,
6217 EL_EM_GATE_1, -1, -1
6220 Xdoor_2, TRUE, FALSE,
6221 EL_EM_GATE_2, -1, -1
6224 Xdoor_3, TRUE, FALSE,
6225 EL_EM_GATE_3, -1, -1
6228 Xdoor_4, TRUE, FALSE,
6229 EL_EM_GATE_4, -1, -1
6232 Xdoor_5, TRUE, FALSE,
6233 EL_EMC_GATE_5, -1, -1
6236 Xdoor_6, TRUE, FALSE,
6237 EL_EMC_GATE_6, -1, -1
6240 Xdoor_7, TRUE, FALSE,
6241 EL_EMC_GATE_7, -1, -1
6244 Xdoor_8, TRUE, FALSE,
6245 EL_EMC_GATE_8, -1, -1
6248 Xkey_1, TRUE, FALSE,
6252 Xkey_2, TRUE, FALSE,
6256 Xkey_3, TRUE, FALSE,
6260 Xkey_4, TRUE, FALSE,
6264 Xkey_5, TRUE, FALSE,
6265 EL_EMC_KEY_5, -1, -1
6268 Xkey_6, TRUE, FALSE,
6269 EL_EMC_KEY_6, -1, -1
6272 Xkey_7, TRUE, FALSE,
6273 EL_EMC_KEY_7, -1, -1
6276 Xkey_8, TRUE, FALSE,
6277 EL_EMC_KEY_8, -1, -1
6280 Xwind_n, TRUE, FALSE,
6281 EL_BALLOON_SWITCH_UP, -1, -1
6284 Xwind_e, TRUE, FALSE,
6285 EL_BALLOON_SWITCH_RIGHT, -1, -1
6288 Xwind_s, TRUE, FALSE,
6289 EL_BALLOON_SWITCH_DOWN, -1, -1
6292 Xwind_w, TRUE, FALSE,
6293 EL_BALLOON_SWITCH_LEFT, -1, -1
6296 Xwind_nesw, TRUE, FALSE,
6297 EL_BALLOON_SWITCH_ANY, -1, -1
6300 Xwind_stop, TRUE, FALSE,
6301 EL_BALLOON_SWITCH_NONE, -1, -1
6305 EL_EM_EXIT_CLOSED, -1, -1
6308 Xexit_1, TRUE, FALSE,
6309 EL_EM_EXIT_OPEN, -1, -1
6312 Xexit_2, FALSE, FALSE,
6313 EL_EM_EXIT_OPEN, -1, -1
6316 Xexit_3, FALSE, FALSE,
6317 EL_EM_EXIT_OPEN, -1, -1
6320 Xdynamite, TRUE, FALSE,
6321 EL_EM_DYNAMITE, -1, -1
6324 Ydynamite_eat, FALSE, FALSE,
6325 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6328 Xdynamite_1, TRUE, FALSE,
6329 EL_EM_DYNAMITE_ACTIVE, -1, -1
6332 Xdynamite_2, FALSE, FALSE,
6333 EL_EM_DYNAMITE_ACTIVE, -1, -1
6336 Xdynamite_3, FALSE, FALSE,
6337 EL_EM_DYNAMITE_ACTIVE, -1, -1
6340 Xdynamite_4, FALSE, FALSE,
6341 EL_EM_DYNAMITE_ACTIVE, -1, -1
6344 Xbumper, TRUE, FALSE,
6345 EL_EMC_SPRING_BUMPER, -1, -1
6348 XbumperB, FALSE, FALSE,
6349 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6352 Xwheel, TRUE, FALSE,
6353 EL_ROBOT_WHEEL, -1, -1
6356 XwheelB, FALSE, FALSE,
6357 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6360 Xswitch, TRUE, FALSE,
6361 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6364 XswitchB, FALSE, FALSE,
6365 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6369 EL_QUICKSAND_EMPTY, -1, -1
6372 Xsand_stone, TRUE, FALSE,
6373 EL_QUICKSAND_FULL, -1, -1
6376 Xsand_stonein_1, FALSE, TRUE,
6377 EL_ROCK, ACTION_FILLING, -1
6380 Xsand_stonein_2, FALSE, TRUE,
6381 EL_ROCK, ACTION_FILLING, -1
6384 Xsand_stonein_3, FALSE, TRUE,
6385 EL_ROCK, ACTION_FILLING, -1
6388 Xsand_stonein_4, FALSE, TRUE,
6389 EL_ROCK, ACTION_FILLING, -1
6392 Xsand_stonesand_1, FALSE, FALSE,
6393 EL_QUICKSAND_EMPTYING, -1, -1
6396 Xsand_stonesand_2, FALSE, FALSE,
6397 EL_QUICKSAND_EMPTYING, -1, -1
6400 Xsand_stonesand_3, FALSE, FALSE,
6401 EL_QUICKSAND_EMPTYING, -1, -1
6404 Xsand_stonesand_4, FALSE, FALSE,
6405 EL_QUICKSAND_EMPTYING, -1, -1
6408 Xsand_stonesand_quickout_1, FALSE, FALSE,
6409 EL_QUICKSAND_EMPTYING, -1, -1
6412 Xsand_stonesand_quickout_2, FALSE, FALSE,
6413 EL_QUICKSAND_EMPTYING, -1, -1
6416 Xsand_stoneout_1, FALSE, FALSE,
6417 EL_ROCK, ACTION_EMPTYING, -1
6420 Xsand_stoneout_2, FALSE, FALSE,
6421 EL_ROCK, ACTION_EMPTYING, -1
6424 Xsand_sandstone_1, FALSE, FALSE,
6425 EL_QUICKSAND_FILLING, -1, -1
6428 Xsand_sandstone_2, FALSE, FALSE,
6429 EL_QUICKSAND_FILLING, -1, -1
6432 Xsand_sandstone_3, FALSE, FALSE,
6433 EL_QUICKSAND_FILLING, -1, -1
6436 Xsand_sandstone_4, FALSE, FALSE,
6437 EL_QUICKSAND_FILLING, -1, -1
6440 Xplant, TRUE, FALSE,
6441 EL_EMC_PLANT, -1, -1
6444 Yplant, FALSE, FALSE,
6445 EL_EMC_PLANT, -1, -1
6448 Xlenses, TRUE, FALSE,
6449 EL_EMC_LENSES, -1, -1
6452 Xmagnify, TRUE, FALSE,
6453 EL_EMC_MAGNIFIER, -1, -1
6456 Xdripper, TRUE, FALSE,
6457 EL_EMC_DRIPPER, -1, -1
6460 XdripperB, FALSE, FALSE,
6461 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6464 Xfake_blank, TRUE, FALSE,
6465 EL_INVISIBLE_WALL, -1, -1
6468 Xfake_blankB, FALSE, FALSE,
6469 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6472 Xfake_grass, TRUE, FALSE,
6473 EL_EMC_FAKE_GRASS, -1, -1
6476 Xfake_grassB, FALSE, FALSE,
6477 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6480 Xfake_door_1, TRUE, FALSE,
6481 EL_EM_GATE_1_GRAY, -1, -1
6484 Xfake_door_2, TRUE, FALSE,
6485 EL_EM_GATE_2_GRAY, -1, -1
6488 Xfake_door_3, TRUE, FALSE,
6489 EL_EM_GATE_3_GRAY, -1, -1
6492 Xfake_door_4, TRUE, FALSE,
6493 EL_EM_GATE_4_GRAY, -1, -1
6496 Xfake_door_5, TRUE, FALSE,
6497 EL_EMC_GATE_5_GRAY, -1, -1
6500 Xfake_door_6, TRUE, FALSE,
6501 EL_EMC_GATE_6_GRAY, -1, -1
6504 Xfake_door_7, TRUE, FALSE,
6505 EL_EMC_GATE_7_GRAY, -1, -1
6508 Xfake_door_8, TRUE, FALSE,
6509 EL_EMC_GATE_8_GRAY, -1, -1
6512 Xfake_acid_1, TRUE, FALSE,
6513 EL_EMC_FAKE_ACID, -1, -1
6516 Xfake_acid_2, FALSE, FALSE,
6517 EL_EMC_FAKE_ACID, -1, -1
6520 Xfake_acid_3, FALSE, FALSE,
6521 EL_EMC_FAKE_ACID, -1, -1
6524 Xfake_acid_4, FALSE, FALSE,
6525 EL_EMC_FAKE_ACID, -1, -1
6528 Xfake_acid_5, FALSE, FALSE,
6529 EL_EMC_FAKE_ACID, -1, -1
6532 Xfake_acid_6, FALSE, FALSE,
6533 EL_EMC_FAKE_ACID, -1, -1
6536 Xfake_acid_7, FALSE, FALSE,
6537 EL_EMC_FAKE_ACID, -1, -1
6540 Xfake_acid_8, FALSE, FALSE,
6541 EL_EMC_FAKE_ACID, -1, -1
6544 Xsteel_1, TRUE, FALSE,
6545 EL_STEELWALL, -1, -1
6548 Xsteel_2, TRUE, FALSE,
6549 EL_EMC_STEELWALL_2, -1, -1
6552 Xsteel_3, TRUE, FALSE,
6553 EL_EMC_STEELWALL_3, -1, -1
6556 Xsteel_4, TRUE, FALSE,
6557 EL_EMC_STEELWALL_4, -1, -1
6560 Xwall_1, TRUE, FALSE,
6564 Xwall_2, TRUE, FALSE,
6565 EL_EMC_WALL_14, -1, -1
6568 Xwall_3, TRUE, FALSE,
6569 EL_EMC_WALL_15, -1, -1
6572 Xwall_4, TRUE, FALSE,
6573 EL_EMC_WALL_16, -1, -1
6576 Xround_wall_1, TRUE, FALSE,
6577 EL_WALL_SLIPPERY, -1, -1
6580 Xround_wall_2, TRUE, FALSE,
6581 EL_EMC_WALL_SLIPPERY_2, -1, -1
6584 Xround_wall_3, TRUE, FALSE,
6585 EL_EMC_WALL_SLIPPERY_3, -1, -1
6588 Xround_wall_4, TRUE, FALSE,
6589 EL_EMC_WALL_SLIPPERY_4, -1, -1
6592 Xdecor_1, TRUE, FALSE,
6593 EL_EMC_WALL_8, -1, -1
6596 Xdecor_2, TRUE, FALSE,
6597 EL_EMC_WALL_6, -1, -1
6600 Xdecor_3, TRUE, FALSE,
6601 EL_EMC_WALL_4, -1, -1
6604 Xdecor_4, TRUE, FALSE,
6605 EL_EMC_WALL_7, -1, -1
6608 Xdecor_5, TRUE, FALSE,
6609 EL_EMC_WALL_5, -1, -1
6612 Xdecor_6, TRUE, FALSE,
6613 EL_EMC_WALL_9, -1, -1
6616 Xdecor_7, TRUE, FALSE,
6617 EL_EMC_WALL_10, -1, -1
6620 Xdecor_8, TRUE, FALSE,
6621 EL_EMC_WALL_1, -1, -1
6624 Xdecor_9, TRUE, FALSE,
6625 EL_EMC_WALL_2, -1, -1
6628 Xdecor_10, TRUE, FALSE,
6629 EL_EMC_WALL_3, -1, -1
6632 Xdecor_11, TRUE, FALSE,
6633 EL_EMC_WALL_11, -1, -1
6636 Xdecor_12, TRUE, FALSE,
6637 EL_EMC_WALL_12, -1, -1
6640 Xalpha_0, TRUE, FALSE,
6641 EL_CHAR('0'), -1, -1
6644 Xalpha_1, TRUE, FALSE,
6645 EL_CHAR('1'), -1, -1
6648 Xalpha_2, TRUE, FALSE,
6649 EL_CHAR('2'), -1, -1
6652 Xalpha_3, TRUE, FALSE,
6653 EL_CHAR('3'), -1, -1
6656 Xalpha_4, TRUE, FALSE,
6657 EL_CHAR('4'), -1, -1
6660 Xalpha_5, TRUE, FALSE,
6661 EL_CHAR('5'), -1, -1
6664 Xalpha_6, TRUE, FALSE,
6665 EL_CHAR('6'), -1, -1
6668 Xalpha_7, TRUE, FALSE,
6669 EL_CHAR('7'), -1, -1
6672 Xalpha_8, TRUE, FALSE,
6673 EL_CHAR('8'), -1, -1
6676 Xalpha_9, TRUE, FALSE,
6677 EL_CHAR('9'), -1, -1
6680 Xalpha_excla, TRUE, FALSE,
6681 EL_CHAR('!'), -1, -1
6684 Xalpha_quote, TRUE, FALSE,
6685 EL_CHAR('"'), -1, -1
6688 Xalpha_comma, TRUE, FALSE,
6689 EL_CHAR(','), -1, -1
6692 Xalpha_minus, TRUE, FALSE,
6693 EL_CHAR('-'), -1, -1
6696 Xalpha_perio, TRUE, FALSE,
6697 EL_CHAR('.'), -1, -1
6700 Xalpha_colon, TRUE, FALSE,
6701 EL_CHAR(':'), -1, -1
6704 Xalpha_quest, TRUE, FALSE,
6705 EL_CHAR('?'), -1, -1
6708 Xalpha_a, TRUE, FALSE,
6709 EL_CHAR('A'), -1, -1
6712 Xalpha_b, TRUE, FALSE,
6713 EL_CHAR('B'), -1, -1
6716 Xalpha_c, TRUE, FALSE,
6717 EL_CHAR('C'), -1, -1
6720 Xalpha_d, TRUE, FALSE,
6721 EL_CHAR('D'), -1, -1
6724 Xalpha_e, TRUE, FALSE,
6725 EL_CHAR('E'), -1, -1
6728 Xalpha_f, TRUE, FALSE,
6729 EL_CHAR('F'), -1, -1
6732 Xalpha_g, TRUE, FALSE,
6733 EL_CHAR('G'), -1, -1
6736 Xalpha_h, TRUE, FALSE,
6737 EL_CHAR('H'), -1, -1
6740 Xalpha_i, TRUE, FALSE,
6741 EL_CHAR('I'), -1, -1
6744 Xalpha_j, TRUE, FALSE,
6745 EL_CHAR('J'), -1, -1
6748 Xalpha_k, TRUE, FALSE,
6749 EL_CHAR('K'), -1, -1
6752 Xalpha_l, TRUE, FALSE,
6753 EL_CHAR('L'), -1, -1
6756 Xalpha_m, TRUE, FALSE,
6757 EL_CHAR('M'), -1, -1
6760 Xalpha_n, TRUE, FALSE,
6761 EL_CHAR('N'), -1, -1
6764 Xalpha_o, TRUE, FALSE,
6765 EL_CHAR('O'), -1, -1
6768 Xalpha_p, TRUE, FALSE,
6769 EL_CHAR('P'), -1, -1
6772 Xalpha_q, TRUE, FALSE,
6773 EL_CHAR('Q'), -1, -1
6776 Xalpha_r, TRUE, FALSE,
6777 EL_CHAR('R'), -1, -1
6780 Xalpha_s, TRUE, FALSE,
6781 EL_CHAR('S'), -1, -1
6784 Xalpha_t, TRUE, FALSE,
6785 EL_CHAR('T'), -1, -1
6788 Xalpha_u, TRUE, FALSE,
6789 EL_CHAR('U'), -1, -1
6792 Xalpha_v, TRUE, FALSE,
6793 EL_CHAR('V'), -1, -1
6796 Xalpha_w, TRUE, FALSE,
6797 EL_CHAR('W'), -1, -1
6800 Xalpha_x, TRUE, FALSE,
6801 EL_CHAR('X'), -1, -1
6804 Xalpha_y, TRUE, FALSE,
6805 EL_CHAR('Y'), -1, -1
6808 Xalpha_z, TRUE, FALSE,
6809 EL_CHAR('Z'), -1, -1
6812 Xalpha_arrow_e, TRUE, FALSE,
6813 EL_CHAR('>'), -1, -1
6816 Xalpha_arrow_w, TRUE, FALSE,
6817 EL_CHAR('<'), -1, -1
6820 Xalpha_copyr, TRUE, FALSE,
6821 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6825 Xboom_bug, FALSE, FALSE,
6826 EL_BUG, ACTION_EXPLODING, -1
6829 Xboom_bomb, FALSE, FALSE,
6830 EL_BOMB, ACTION_EXPLODING, -1
6833 Xboom_android, FALSE, FALSE,
6834 EL_EMC_ANDROID, ACTION_OTHER, -1
6837 Xboom_1, FALSE, FALSE,
6838 EL_DEFAULT, ACTION_EXPLODING, -1
6841 Xboom_2, FALSE, FALSE,
6842 EL_DEFAULT, ACTION_EXPLODING, -1
6845 Znormal, FALSE, FALSE,
6849 Zdynamite, FALSE, FALSE,
6853 Zplayer, FALSE, FALSE,
6857 ZBORDER, FALSE, FALSE,
6867 static struct Mapping_EM_to_RND_player
6876 em_player_mapping_list[] =
6880 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6884 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6888 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6892 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6896 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6900 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6904 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6908 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6912 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6916 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6920 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6924 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6928 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6932 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6936 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6940 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6944 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6948 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6952 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6956 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6960 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6964 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6968 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6972 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6976 EL_PLAYER_1, ACTION_DEFAULT, -1,
6980 EL_PLAYER_2, ACTION_DEFAULT, -1,
6984 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6988 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6992 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6996 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7000 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7004 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7008 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7012 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7016 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7020 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7024 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7028 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7032 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7036 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7040 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7044 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7048 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7052 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7056 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7060 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7064 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7068 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7072 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7076 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7080 EL_PLAYER_3, ACTION_DEFAULT, -1,
7084 EL_PLAYER_4, ACTION_DEFAULT, -1,
7093 int map_element_RND_to_EM(int element_rnd)
7095 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7096 static boolean mapping_initialized = FALSE;
7098 if (!mapping_initialized)
7102 /* return "Xalpha_quest" for all undefined elements in mapping array */
7103 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7104 mapping_RND_to_EM[i] = Xalpha_quest;
7106 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7107 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7108 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7109 em_object_mapping_list[i].element_em;
7111 mapping_initialized = TRUE;
7114 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7115 return mapping_RND_to_EM[element_rnd];
7117 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7122 int map_element_EM_to_RND(int element_em)
7124 static unsigned short mapping_EM_to_RND[TILE_MAX];
7125 static boolean mapping_initialized = FALSE;
7127 if (!mapping_initialized)
7131 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
7132 for (i = 0; i < TILE_MAX; i++)
7133 mapping_EM_to_RND[i] = EL_UNKNOWN;
7135 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7136 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7137 em_object_mapping_list[i].element_rnd;
7139 mapping_initialized = TRUE;
7142 if (element_em >= 0 && element_em < TILE_MAX)
7143 return mapping_EM_to_RND[element_em];
7145 Error(ERR_WARN, "invalid EM level element %d", element_em);
7150 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7152 struct LevelInfo_EM *level_em = level->native_em_level;
7153 struct LEVEL *lev = level_em->lev;
7156 for (i = 0; i < TILE_MAX; i++)
7157 lev->android_array[i] = Xblank;
7159 for (i = 0; i < level->num_android_clone_elements; i++)
7161 int element_rnd = level->android_clone_element[i];
7162 int element_em = map_element_RND_to_EM(element_rnd);
7164 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7165 if (em_object_mapping_list[j].element_rnd == element_rnd)
7166 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7170 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7172 struct LevelInfo_EM *level_em = level->native_em_level;
7173 struct LEVEL *lev = level_em->lev;
7176 level->num_android_clone_elements = 0;
7178 for (i = 0; i < TILE_MAX; i++)
7180 int element_em = lev->android_array[i];
7182 boolean element_found = FALSE;
7184 if (element_em == Xblank)
7187 element_rnd = map_element_EM_to_RND(element_em);
7189 for (j = 0; j < level->num_android_clone_elements; j++)
7190 if (level->android_clone_element[j] == element_rnd)
7191 element_found = TRUE;
7195 level->android_clone_element[level->num_android_clone_elements++] =
7198 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7203 if (level->num_android_clone_elements == 0)
7205 level->num_android_clone_elements = 1;
7206 level->android_clone_element[0] = EL_EMPTY;
7210 int map_direction_RND_to_EM(int direction)
7212 return (direction == MV_UP ? 0 :
7213 direction == MV_RIGHT ? 1 :
7214 direction == MV_DOWN ? 2 :
7215 direction == MV_LEFT ? 3 :
7219 int map_direction_EM_to_RND(int direction)
7221 return (direction == 0 ? MV_UP :
7222 direction == 1 ? MV_RIGHT :
7223 direction == 2 ? MV_DOWN :
7224 direction == 3 ? MV_LEFT :
7228 int map_element_RND_to_SP(int element_rnd)
7230 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
7232 if (element_rnd >= EL_SP_START &&
7233 element_rnd <= EL_SP_END)
7234 element_sp = element_rnd - EL_SP_START;
7235 else if (element_rnd == EL_EMPTY_SPACE)
7237 else if (element_rnd == EL_INVISIBLE_WALL)
7243 int map_element_SP_to_RND(int element_sp)
7245 int element_rnd = EL_UNKNOWN;
7247 if (element_sp >= 0x00 &&
7249 element_rnd = EL_SP_START + element_sp;
7250 else if (element_sp == 0x28)
7251 element_rnd = EL_INVISIBLE_WALL;
7256 int map_action_SP_to_RND(int action_sp)
7260 case actActive: return ACTION_ACTIVE;
7261 case actImpact: return ACTION_IMPACT;
7262 case actExploding: return ACTION_EXPLODING;
7263 case actDigging: return ACTION_DIGGING;
7264 case actSnapping: return ACTION_SNAPPING;
7265 case actCollecting: return ACTION_COLLECTING;
7266 case actPassing: return ACTION_PASSING;
7267 case actPushing: return ACTION_PUSHING;
7268 case actDropping: return ACTION_DROPPING;
7270 default: return ACTION_DEFAULT;
7274 int map_element_RND_to_MM(int element_rnd)
7276 return (element_rnd >= EL_MM_START_1 &&
7277 element_rnd <= EL_MM_END_1 ?
7278 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7280 element_rnd >= EL_MM_START_2 &&
7281 element_rnd <= EL_MM_END_2 ?
7282 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7284 element_rnd >= EL_CHAR_START &&
7285 element_rnd <= EL_CHAR_END ?
7286 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7288 element_rnd >= EL_MM_RUNTIME_START &&
7289 element_rnd <= EL_MM_RUNTIME_END ?
7290 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7292 element_rnd >= EL_MM_DUMMY_START &&
7293 element_rnd <= EL_MM_DUMMY_END ?
7294 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7296 EL_MM_EMPTY_NATIVE);
7299 int map_element_MM_to_RND(int element_mm)
7301 return (element_mm == EL_MM_EMPTY_NATIVE ||
7302 element_mm == EL_DF_EMPTY_NATIVE ?
7305 element_mm >= EL_MM_START_1_NATIVE &&
7306 element_mm <= EL_MM_END_1_NATIVE ?
7307 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7309 element_mm >= EL_MM_START_2_NATIVE &&
7310 element_mm <= EL_MM_END_2_NATIVE ?
7311 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7313 element_mm >= EL_MM_CHAR_START_NATIVE &&
7314 element_mm <= EL_MM_CHAR_END_NATIVE ?
7315 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7317 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7318 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7319 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7321 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7322 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7323 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7328 int map_mm_wall_element(int element)
7330 return (element >= EL_MM_STEEL_WALL_START &&
7331 element <= EL_MM_STEEL_WALL_END ?
7334 element >= EL_MM_WOODEN_WALL_START &&
7335 element <= EL_MM_WOODEN_WALL_END ?
7338 element >= EL_MM_ICE_WALL_START &&
7339 element <= EL_MM_ICE_WALL_END ?
7342 element >= EL_MM_AMOEBA_WALL_START &&
7343 element <= EL_MM_AMOEBA_WALL_END ?
7346 element >= EL_DF_STEEL_WALL_START &&
7347 element <= EL_DF_STEEL_WALL_END ?
7350 element >= EL_DF_WOODEN_WALL_START &&
7351 element <= EL_DF_WOODEN_WALL_END ?
7357 int get_next_element(int element)
7361 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7362 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7363 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7364 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7365 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7366 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7367 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7368 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7369 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7370 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7371 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7373 default: return element;
7377 int el2img_mm(int element_mm)
7379 return el2img(map_element_MM_to_RND(element_mm));
7382 int el_act_dir2img(int element, int action, int direction)
7384 element = GFX_ELEMENT(element);
7385 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7387 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7388 return element_info[element].direction_graphic[action][direction];
7391 static int el_act_dir2crm(int element, int action, int direction)
7393 element = GFX_ELEMENT(element);
7394 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7396 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7397 return element_info[element].direction_crumbled[action][direction];
7400 int el_act2img(int element, int action)
7402 element = GFX_ELEMENT(element);
7404 return element_info[element].graphic[action];
7407 int el_act2crm(int element, int action)
7409 element = GFX_ELEMENT(element);
7411 return element_info[element].crumbled[action];
7414 int el_dir2img(int element, int direction)
7416 element = GFX_ELEMENT(element);
7418 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7421 int el2baseimg(int element)
7423 return element_info[element].graphic[ACTION_DEFAULT];
7426 int el2img(int element)
7428 element = GFX_ELEMENT(element);
7430 return element_info[element].graphic[ACTION_DEFAULT];
7433 int el2edimg(int element)
7435 element = GFX_ELEMENT(element);
7437 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7440 int el2preimg(int element)
7442 element = GFX_ELEMENT(element);
7444 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7447 int el2panelimg(int element)
7449 element = GFX_ELEMENT(element);
7451 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7454 int font2baseimg(int font_nr)
7456 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7459 int getBeltNrFromBeltElement(int element)
7461 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7462 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7463 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
7466 int getBeltNrFromBeltActiveElement(int element)
7468 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
7469 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
7470 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
7473 int getBeltNrFromBeltSwitchElement(int element)
7475 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
7476 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
7477 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
7480 int getBeltDirNrFromBeltElement(int element)
7482 static int belt_base_element[4] =
7484 EL_CONVEYOR_BELT_1_LEFT,
7485 EL_CONVEYOR_BELT_2_LEFT,
7486 EL_CONVEYOR_BELT_3_LEFT,
7487 EL_CONVEYOR_BELT_4_LEFT
7490 int belt_nr = getBeltNrFromBeltElement(element);
7491 int belt_dir_nr = element - belt_base_element[belt_nr];
7493 return (belt_dir_nr % 3);
7496 int getBeltDirNrFromBeltSwitchElement(int element)
7498 static int belt_base_element[4] =
7500 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7501 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7502 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7503 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7506 int belt_nr = getBeltNrFromBeltSwitchElement(element);
7507 int belt_dir_nr = element - belt_base_element[belt_nr];
7509 return (belt_dir_nr % 3);
7512 int getBeltDirFromBeltElement(int element)
7514 static int belt_move_dir[3] =
7521 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7523 return belt_move_dir[belt_dir_nr];
7526 int getBeltDirFromBeltSwitchElement(int element)
7528 static int belt_move_dir[3] =
7535 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7537 return belt_move_dir[belt_dir_nr];
7540 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7542 static int belt_base_element[4] =
7544 EL_CONVEYOR_BELT_1_LEFT,
7545 EL_CONVEYOR_BELT_2_LEFT,
7546 EL_CONVEYOR_BELT_3_LEFT,
7547 EL_CONVEYOR_BELT_4_LEFT
7550 return belt_base_element[belt_nr] + belt_dir_nr;
7553 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7555 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7557 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7560 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7562 static int belt_base_element[4] =
7564 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7565 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7566 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7567 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7570 return belt_base_element[belt_nr] + belt_dir_nr;
7573 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7575 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7577 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7580 boolean getTeamMode_EM()
7582 return game.team_mode;
7585 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7587 int game_frame_delay_value;
7589 game_frame_delay_value =
7590 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7591 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7594 if (tape.playing && tape.warp_forward && !tape.pausing)
7595 game_frame_delay_value = 0;
7597 return game_frame_delay_value;
7600 unsigned int InitRND(int seed)
7602 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7603 return InitEngineRandom_EM(seed);
7604 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7605 return InitEngineRandom_SP(seed);
7606 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
7607 return InitEngineRandom_MM(seed);
7609 return InitEngineRandom_RND(seed);
7612 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7613 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7615 inline static int get_effective_element_EM(int tile, int frame_em)
7617 int element = object_mapping[tile].element_rnd;
7618 int action = object_mapping[tile].action;
7619 boolean is_backside = object_mapping[tile].is_backside;
7620 boolean action_removing = (action == ACTION_DIGGING ||
7621 action == ACTION_SNAPPING ||
7622 action == ACTION_COLLECTING);
7628 case Yacid_splash_eB:
7629 case Yacid_splash_wB:
7630 return (frame_em > 5 ? EL_EMPTY : element);
7636 else /* frame_em == 7 */
7640 case Yacid_splash_eB:
7641 case Yacid_splash_wB:
7644 case Yemerald_stone:
7647 case Ydiamond_stone:
7651 case Xdrip_stretchB:
7670 case Xsand_stonein_1:
7671 case Xsand_stonein_2:
7672 case Xsand_stonein_3:
7673 case Xsand_stonein_4:
7677 return (is_backside || action_removing ? EL_EMPTY : element);
7682 inline static boolean check_linear_animation_EM(int tile)
7686 case Xsand_stonesand_1:
7687 case Xsand_stonesand_quickout_1:
7688 case Xsand_sandstone_1:
7689 case Xsand_stonein_1:
7690 case Xsand_stoneout_1:
7709 case Yacid_splash_eB:
7710 case Yacid_splash_wB:
7711 case Yemerald_stone:
7718 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7719 boolean has_crumbled_graphics,
7720 int crumbled, int sync_frame)
7722 /* if element can be crumbled, but certain action graphics are just empty
7723 space (like instantly snapping sand to empty space in 1 frame), do not
7724 treat these empty space graphics as crumbled graphics in EMC engine */
7725 if (crumbled == IMG_EMPTY_SPACE)
7726 has_crumbled_graphics = FALSE;
7728 if (has_crumbled_graphics)
7730 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7731 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7732 g_crumbled->anim_delay,
7733 g_crumbled->anim_mode,
7734 g_crumbled->anim_start_frame,
7737 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7738 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7740 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7741 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
7743 g_em->has_crumbled_graphics = TRUE;
7747 g_em->crumbled_bitmap = NULL;
7748 g_em->crumbled_src_x = 0;
7749 g_em->crumbled_src_y = 0;
7750 g_em->crumbled_border_size = 0;
7751 g_em->crumbled_tile_size = 0;
7753 g_em->has_crumbled_graphics = FALSE;
7757 void ResetGfxAnimation_EM(int x, int y, int tile)
7762 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7763 int tile, int frame_em, int x, int y)
7765 int action = object_mapping[tile].action;
7766 int direction = object_mapping[tile].direction;
7767 int effective_element = get_effective_element_EM(tile, frame_em);
7768 int graphic = (direction == MV_NONE ?
7769 el_act2img(effective_element, action) :
7770 el_act_dir2img(effective_element, action, direction));
7771 struct GraphicInfo *g = &graphic_info[graphic];
7773 boolean action_removing = (action == ACTION_DIGGING ||
7774 action == ACTION_SNAPPING ||
7775 action == ACTION_COLLECTING);
7776 boolean action_moving = (action == ACTION_FALLING ||
7777 action == ACTION_MOVING ||
7778 action == ACTION_PUSHING ||
7779 action == ACTION_EATING ||
7780 action == ACTION_FILLING ||
7781 action == ACTION_EMPTYING);
7782 boolean action_falling = (action == ACTION_FALLING ||
7783 action == ACTION_FILLING ||
7784 action == ACTION_EMPTYING);
7786 /* special case: graphic uses "2nd movement tile" and has defined
7787 7 frames for movement animation (or less) => use default graphic
7788 for last (8th) frame which ends the movement animation */
7789 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7791 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7792 graphic = (direction == MV_NONE ?
7793 el_act2img(effective_element, action) :
7794 el_act_dir2img(effective_element, action, direction));
7796 g = &graphic_info[graphic];
7799 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7803 else if (action_moving)
7805 boolean is_backside = object_mapping[tile].is_backside;
7809 int direction = object_mapping[tile].direction;
7810 int move_dir = (action_falling ? MV_DOWN : direction);
7815 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7816 if (g->double_movement && frame_em == 0)
7820 if (move_dir == MV_LEFT)
7821 GfxFrame[x - 1][y] = GfxFrame[x][y];
7822 else if (move_dir == MV_RIGHT)
7823 GfxFrame[x + 1][y] = GfxFrame[x][y];
7824 else if (move_dir == MV_UP)
7825 GfxFrame[x][y - 1] = GfxFrame[x][y];
7826 else if (move_dir == MV_DOWN)
7827 GfxFrame[x][y + 1] = GfxFrame[x][y];
7834 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7835 if (tile == Xsand_stonesand_quickout_1 ||
7836 tile == Xsand_stonesand_quickout_2)
7840 if (graphic_info[graphic].anim_global_sync)
7841 sync_frame = FrameCounter;
7842 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7843 sync_frame = GfxFrame[x][y];
7845 sync_frame = 0; /* playfield border (pseudo steel) */
7847 SetRandomAnimationValue(x, y);
7849 int frame = getAnimationFrame(g->anim_frames,
7852 g->anim_start_frame,
7855 g_em->unique_identifier =
7856 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7859 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7860 int tile, int frame_em, int x, int y)
7862 int action = object_mapping[tile].action;
7863 int direction = object_mapping[tile].direction;
7864 boolean is_backside = object_mapping[tile].is_backside;
7865 int effective_element = get_effective_element_EM(tile, frame_em);
7866 int effective_action = action;
7867 int graphic = (direction == MV_NONE ?
7868 el_act2img(effective_element, effective_action) :
7869 el_act_dir2img(effective_element, effective_action,
7871 int crumbled = (direction == MV_NONE ?
7872 el_act2crm(effective_element, effective_action) :
7873 el_act_dir2crm(effective_element, effective_action,
7875 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7876 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7877 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7878 struct GraphicInfo *g = &graphic_info[graphic];
7881 /* special case: graphic uses "2nd movement tile" and has defined
7882 7 frames for movement animation (or less) => use default graphic
7883 for last (8th) frame which ends the movement animation */
7884 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7886 effective_action = ACTION_DEFAULT;
7887 graphic = (direction == MV_NONE ?
7888 el_act2img(effective_element, effective_action) :
7889 el_act_dir2img(effective_element, effective_action,
7891 crumbled = (direction == MV_NONE ?
7892 el_act2crm(effective_element, effective_action) :
7893 el_act_dir2crm(effective_element, effective_action,
7896 g = &graphic_info[graphic];
7899 if (graphic_info[graphic].anim_global_sync)
7900 sync_frame = FrameCounter;
7901 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7902 sync_frame = GfxFrame[x][y];
7904 sync_frame = 0; /* playfield border (pseudo steel) */
7906 SetRandomAnimationValue(x, y);
7908 int frame = getAnimationFrame(g->anim_frames,
7911 g->anim_start_frame,
7914 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7915 g->double_movement && is_backside);
7917 /* (updating the "crumbled" graphic definitions is probably not really needed,
7918 as animations for crumbled graphics can't be longer than one EMC cycle) */
7919 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7923 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7924 int player_nr, int anim, int frame_em)
7926 int element = player_mapping[player_nr][anim].element_rnd;
7927 int action = player_mapping[player_nr][anim].action;
7928 int direction = player_mapping[player_nr][anim].direction;
7929 int graphic = (direction == MV_NONE ?
7930 el_act2img(element, action) :
7931 el_act_dir2img(element, action, direction));
7932 struct GraphicInfo *g = &graphic_info[graphic];
7935 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7937 stored_player[player_nr].StepFrame = frame_em;
7939 sync_frame = stored_player[player_nr].Frame;
7941 int frame = getAnimationFrame(g->anim_frames,
7944 g->anim_start_frame,
7947 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7948 &g_em->src_x, &g_em->src_y, FALSE);
7951 void InitGraphicInfo_EM(void)
7956 int num_em_gfx_errors = 0;
7958 if (graphic_info_em_object[0][0].bitmap == NULL)
7960 /* EM graphics not yet initialized in em_open_all() */
7965 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7968 /* always start with reliable default values */
7969 for (i = 0; i < TILE_MAX; i++)
7971 object_mapping[i].element_rnd = EL_UNKNOWN;
7972 object_mapping[i].is_backside = FALSE;
7973 object_mapping[i].action = ACTION_DEFAULT;
7974 object_mapping[i].direction = MV_NONE;
7977 /* always start with reliable default values */
7978 for (p = 0; p < MAX_PLAYERS; p++)
7980 for (i = 0; i < SPR_MAX; i++)
7982 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7983 player_mapping[p][i].action = ACTION_DEFAULT;
7984 player_mapping[p][i].direction = MV_NONE;
7988 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7990 int e = em_object_mapping_list[i].element_em;
7992 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7993 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7995 if (em_object_mapping_list[i].action != -1)
7996 object_mapping[e].action = em_object_mapping_list[i].action;
7998 if (em_object_mapping_list[i].direction != -1)
7999 object_mapping[e].direction =
8000 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8003 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8005 int a = em_player_mapping_list[i].action_em;
8006 int p = em_player_mapping_list[i].player_nr;
8008 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8010 if (em_player_mapping_list[i].action != -1)
8011 player_mapping[p][a].action = em_player_mapping_list[i].action;
8013 if (em_player_mapping_list[i].direction != -1)
8014 player_mapping[p][a].direction =
8015 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8018 for (i = 0; i < TILE_MAX; i++)
8020 int element = object_mapping[i].element_rnd;
8021 int action = object_mapping[i].action;
8022 int direction = object_mapping[i].direction;
8023 boolean is_backside = object_mapping[i].is_backside;
8024 boolean action_exploding = ((action == ACTION_EXPLODING ||
8025 action == ACTION_SMASHED_BY_ROCK ||
8026 action == ACTION_SMASHED_BY_SPRING) &&
8027 element != EL_DIAMOND);
8028 boolean action_active = (action == ACTION_ACTIVE);
8029 boolean action_other = (action == ACTION_OTHER);
8031 for (j = 0; j < 8; j++)
8033 int effective_element = get_effective_element_EM(i, j);
8034 int effective_action = (j < 7 ? action :
8035 i == Xdrip_stretch ? action :
8036 i == Xdrip_stretchB ? action :
8037 i == Ydrip_s1 ? action :
8038 i == Ydrip_s1B ? action :
8039 i == Xball_1B ? action :
8040 i == Xball_2 ? action :
8041 i == Xball_2B ? action :
8042 i == Yball_eat ? action :
8043 i == Ykey_1_eat ? action :
8044 i == Ykey_2_eat ? action :
8045 i == Ykey_3_eat ? action :
8046 i == Ykey_4_eat ? action :
8047 i == Ykey_5_eat ? action :
8048 i == Ykey_6_eat ? action :
8049 i == Ykey_7_eat ? action :
8050 i == Ykey_8_eat ? action :
8051 i == Ylenses_eat ? action :
8052 i == Ymagnify_eat ? action :
8053 i == Ygrass_eat ? action :
8054 i == Ydirt_eat ? action :
8055 i == Xsand_stonein_1 ? action :
8056 i == Xsand_stonein_2 ? action :
8057 i == Xsand_stonein_3 ? action :
8058 i == Xsand_stonein_4 ? action :
8059 i == Xsand_stoneout_1 ? action :
8060 i == Xsand_stoneout_2 ? action :
8061 i == Xboom_android ? ACTION_EXPLODING :
8062 action_exploding ? ACTION_EXPLODING :
8063 action_active ? action :
8064 action_other ? action :
8066 int graphic = (el_act_dir2img(effective_element, effective_action,
8068 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8070 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8071 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8072 boolean has_action_graphics = (graphic != base_graphic);
8073 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8074 struct GraphicInfo *g = &graphic_info[graphic];
8075 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8078 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
8079 boolean special_animation = (action != ACTION_DEFAULT &&
8080 g->anim_frames == 3 &&
8081 g->anim_delay == 2 &&
8082 g->anim_mode & ANIM_LINEAR);
8083 int sync_frame = (i == Xdrip_stretch ? 7 :
8084 i == Xdrip_stretchB ? 7 :
8085 i == Ydrip_s2 ? j + 8 :
8086 i == Ydrip_s2B ? j + 8 :
8095 i == Xfake_acid_1 ? 0 :
8096 i == Xfake_acid_2 ? 10 :
8097 i == Xfake_acid_3 ? 20 :
8098 i == Xfake_acid_4 ? 30 :
8099 i == Xfake_acid_5 ? 40 :
8100 i == Xfake_acid_6 ? 50 :
8101 i == Xfake_acid_7 ? 60 :
8102 i == Xfake_acid_8 ? 70 :
8104 i == Xball_2B ? j + 8 :
8105 i == Yball_eat ? j + 1 :
8106 i == Ykey_1_eat ? j + 1 :
8107 i == Ykey_2_eat ? j + 1 :
8108 i == Ykey_3_eat ? j + 1 :
8109 i == Ykey_4_eat ? j + 1 :
8110 i == Ykey_5_eat ? j + 1 :
8111 i == Ykey_6_eat ? j + 1 :
8112 i == Ykey_7_eat ? j + 1 :
8113 i == Ykey_8_eat ? j + 1 :
8114 i == Ylenses_eat ? j + 1 :
8115 i == Ymagnify_eat ? j + 1 :
8116 i == Ygrass_eat ? j + 1 :
8117 i == Ydirt_eat ? j + 1 :
8118 i == Xamoeba_1 ? 0 :
8119 i == Xamoeba_2 ? 1 :
8120 i == Xamoeba_3 ? 2 :
8121 i == Xamoeba_4 ? 3 :
8122 i == Xamoeba_5 ? 0 :
8123 i == Xamoeba_6 ? 1 :
8124 i == Xamoeba_7 ? 2 :
8125 i == Xamoeba_8 ? 3 :
8126 i == Xexit_2 ? j + 8 :
8127 i == Xexit_3 ? j + 16 :
8128 i == Xdynamite_1 ? 0 :
8129 i == Xdynamite_2 ? 8 :
8130 i == Xdynamite_3 ? 16 :
8131 i == Xdynamite_4 ? 24 :
8132 i == Xsand_stonein_1 ? j + 1 :
8133 i == Xsand_stonein_2 ? j + 9 :
8134 i == Xsand_stonein_3 ? j + 17 :
8135 i == Xsand_stonein_4 ? j + 25 :
8136 i == Xsand_stoneout_1 && j == 0 ? 0 :
8137 i == Xsand_stoneout_1 && j == 1 ? 0 :
8138 i == Xsand_stoneout_1 && j == 2 ? 1 :
8139 i == Xsand_stoneout_1 && j == 3 ? 2 :
8140 i == Xsand_stoneout_1 && j == 4 ? 2 :
8141 i == Xsand_stoneout_1 && j == 5 ? 3 :
8142 i == Xsand_stoneout_1 && j == 6 ? 4 :
8143 i == Xsand_stoneout_1 && j == 7 ? 4 :
8144 i == Xsand_stoneout_2 && j == 0 ? 5 :
8145 i == Xsand_stoneout_2 && j == 1 ? 6 :
8146 i == Xsand_stoneout_2 && j == 2 ? 7 :
8147 i == Xsand_stoneout_2 && j == 3 ? 8 :
8148 i == Xsand_stoneout_2 && j == 4 ? 9 :
8149 i == Xsand_stoneout_2 && j == 5 ? 11 :
8150 i == Xsand_stoneout_2 && j == 6 ? 13 :
8151 i == Xsand_stoneout_2 && j == 7 ? 15 :
8152 i == Xboom_bug && j == 1 ? 2 :
8153 i == Xboom_bug && j == 2 ? 2 :
8154 i == Xboom_bug && j == 3 ? 4 :
8155 i == Xboom_bug && j == 4 ? 4 :
8156 i == Xboom_bug && j == 5 ? 2 :
8157 i == Xboom_bug && j == 6 ? 2 :
8158 i == Xboom_bug && j == 7 ? 0 :
8159 i == Xboom_bomb && j == 1 ? 2 :
8160 i == Xboom_bomb && j == 2 ? 2 :
8161 i == Xboom_bomb && j == 3 ? 4 :
8162 i == Xboom_bomb && j == 4 ? 4 :
8163 i == Xboom_bomb && j == 5 ? 2 :
8164 i == Xboom_bomb && j == 6 ? 2 :
8165 i == Xboom_bomb && j == 7 ? 0 :
8166 i == Xboom_android && j == 7 ? 6 :
8167 i == Xboom_1 && j == 1 ? 2 :
8168 i == Xboom_1 && j == 2 ? 2 :
8169 i == Xboom_1 && j == 3 ? 4 :
8170 i == Xboom_1 && j == 4 ? 4 :
8171 i == Xboom_1 && j == 5 ? 6 :
8172 i == Xboom_1 && j == 6 ? 6 :
8173 i == Xboom_1 && j == 7 ? 8 :
8174 i == Xboom_2 && j == 0 ? 8 :
8175 i == Xboom_2 && j == 1 ? 8 :
8176 i == Xboom_2 && j == 2 ? 10 :
8177 i == Xboom_2 && j == 3 ? 10 :
8178 i == Xboom_2 && j == 4 ? 10 :
8179 i == Xboom_2 && j == 5 ? 12 :
8180 i == Xboom_2 && j == 6 ? 12 :
8181 i == Xboom_2 && j == 7 ? 12 :
8182 special_animation && j == 4 ? 3 :
8183 effective_action != action ? 0 :
8187 Bitmap *debug_bitmap = g_em->bitmap;
8188 int debug_src_x = g_em->src_x;
8189 int debug_src_y = g_em->src_y;
8192 int frame = getAnimationFrame(g->anim_frames,
8195 g->anim_start_frame,
8198 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8199 g->double_movement && is_backside);
8201 g_em->bitmap = src_bitmap;
8202 g_em->src_x = src_x;
8203 g_em->src_y = src_y;
8204 g_em->src_offset_x = 0;
8205 g_em->src_offset_y = 0;
8206 g_em->dst_offset_x = 0;
8207 g_em->dst_offset_y = 0;
8208 g_em->width = TILEX;
8209 g_em->height = TILEY;
8211 g_em->preserve_background = FALSE;
8213 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8216 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8217 effective_action == ACTION_MOVING ||
8218 effective_action == ACTION_PUSHING ||
8219 effective_action == ACTION_EATING)) ||
8220 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8221 effective_action == ACTION_EMPTYING)))
8224 (effective_action == ACTION_FALLING ||
8225 effective_action == ACTION_FILLING ||
8226 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8227 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8228 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8229 int num_steps = (i == Ydrip_s1 ? 16 :
8230 i == Ydrip_s1B ? 16 :
8231 i == Ydrip_s2 ? 16 :
8232 i == Ydrip_s2B ? 16 :
8233 i == Xsand_stonein_1 ? 32 :
8234 i == Xsand_stonein_2 ? 32 :
8235 i == Xsand_stonein_3 ? 32 :
8236 i == Xsand_stonein_4 ? 32 :
8237 i == Xsand_stoneout_1 ? 16 :
8238 i == Xsand_stoneout_2 ? 16 : 8);
8239 int cx = ABS(dx) * (TILEX / num_steps);
8240 int cy = ABS(dy) * (TILEY / num_steps);
8241 int step_frame = (i == Ydrip_s2 ? j + 8 :
8242 i == Ydrip_s2B ? j + 8 :
8243 i == Xsand_stonein_2 ? j + 8 :
8244 i == Xsand_stonein_3 ? j + 16 :
8245 i == Xsand_stonein_4 ? j + 24 :
8246 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8247 int step = (is_backside ? step_frame : num_steps - step_frame);
8249 if (is_backside) /* tile where movement starts */
8251 if (dx < 0 || dy < 0)
8253 g_em->src_offset_x = cx * step;
8254 g_em->src_offset_y = cy * step;
8258 g_em->dst_offset_x = cx * step;
8259 g_em->dst_offset_y = cy * step;
8262 else /* tile where movement ends */
8264 if (dx < 0 || dy < 0)
8266 g_em->dst_offset_x = cx * step;
8267 g_em->dst_offset_y = cy * step;
8271 g_em->src_offset_x = cx * step;
8272 g_em->src_offset_y = cy * step;
8276 g_em->width = TILEX - cx * step;
8277 g_em->height = TILEY - cy * step;
8280 /* create unique graphic identifier to decide if tile must be redrawn */
8281 /* bit 31 - 16 (16 bit): EM style graphic
8282 bit 15 - 12 ( 4 bit): EM style frame
8283 bit 11 - 6 ( 6 bit): graphic width
8284 bit 5 - 0 ( 6 bit): graphic height */
8285 g_em->unique_identifier =
8286 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8290 /* skip check for EMC elements not contained in original EMC artwork */
8291 if (element == EL_EMC_FAKE_ACID)
8294 if (g_em->bitmap != debug_bitmap ||
8295 g_em->src_x != debug_src_x ||
8296 g_em->src_y != debug_src_y ||
8297 g_em->src_offset_x != 0 ||
8298 g_em->src_offset_y != 0 ||
8299 g_em->dst_offset_x != 0 ||
8300 g_em->dst_offset_y != 0 ||
8301 g_em->width != TILEX ||
8302 g_em->height != TILEY)
8304 static int last_i = -1;
8312 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8313 i, element, element_info[element].token_name,
8314 element_action_info[effective_action].suffix, direction);
8316 if (element != effective_element)
8317 printf(" [%d ('%s')]",
8319 element_info[effective_element].token_name);
8323 if (g_em->bitmap != debug_bitmap)
8324 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8325 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8327 if (g_em->src_x != debug_src_x ||
8328 g_em->src_y != debug_src_y)
8329 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8330 j, (is_backside ? 'B' : 'F'),
8331 g_em->src_x, g_em->src_y,
8332 g_em->src_x / 32, g_em->src_y / 32,
8333 debug_src_x, debug_src_y,
8334 debug_src_x / 32, debug_src_y / 32);
8336 if (g_em->src_offset_x != 0 ||
8337 g_em->src_offset_y != 0 ||
8338 g_em->dst_offset_x != 0 ||
8339 g_em->dst_offset_y != 0)
8340 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8342 g_em->src_offset_x, g_em->src_offset_y,
8343 g_em->dst_offset_x, g_em->dst_offset_y);
8345 if (g_em->width != TILEX ||
8346 g_em->height != TILEY)
8347 printf(" %d (%d): size %d,%d should be %d,%d\n",
8349 g_em->width, g_em->height, TILEX, TILEY);
8351 num_em_gfx_errors++;
8358 for (i = 0; i < TILE_MAX; i++)
8360 for (j = 0; j < 8; j++)
8362 int element = object_mapping[i].element_rnd;
8363 int action = object_mapping[i].action;
8364 int direction = object_mapping[i].direction;
8365 boolean is_backside = object_mapping[i].is_backside;
8366 int graphic_action = el_act_dir2img(element, action, direction);
8367 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8369 if ((action == ACTION_SMASHED_BY_ROCK ||
8370 action == ACTION_SMASHED_BY_SPRING ||
8371 action == ACTION_EATING) &&
8372 graphic_action == graphic_default)
8374 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8375 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8376 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8377 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8380 /* no separate animation for "smashed by rock" -- use rock instead */
8381 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8382 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8384 g_em->bitmap = g_xx->bitmap;
8385 g_em->src_x = g_xx->src_x;
8386 g_em->src_y = g_xx->src_y;
8387 g_em->src_offset_x = g_xx->src_offset_x;
8388 g_em->src_offset_y = g_xx->src_offset_y;
8389 g_em->dst_offset_x = g_xx->dst_offset_x;
8390 g_em->dst_offset_y = g_xx->dst_offset_y;
8391 g_em->width = g_xx->width;
8392 g_em->height = g_xx->height;
8393 g_em->unique_identifier = g_xx->unique_identifier;
8396 g_em->preserve_background = TRUE;
8401 for (p = 0; p < MAX_PLAYERS; p++)
8403 for (i = 0; i < SPR_MAX; i++)
8405 int element = player_mapping[p][i].element_rnd;
8406 int action = player_mapping[p][i].action;
8407 int direction = player_mapping[p][i].direction;
8409 for (j = 0; j < 8; j++)
8411 int effective_element = element;
8412 int effective_action = action;
8413 int graphic = (direction == MV_NONE ?
8414 el_act2img(effective_element, effective_action) :
8415 el_act_dir2img(effective_element, effective_action,
8417 struct GraphicInfo *g = &graphic_info[graphic];
8418 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8424 Bitmap *debug_bitmap = g_em->bitmap;
8425 int debug_src_x = g_em->src_x;
8426 int debug_src_y = g_em->src_y;
8429 int frame = getAnimationFrame(g->anim_frames,
8432 g->anim_start_frame,
8435 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8437 g_em->bitmap = src_bitmap;
8438 g_em->src_x = src_x;
8439 g_em->src_y = src_y;
8440 g_em->src_offset_x = 0;
8441 g_em->src_offset_y = 0;
8442 g_em->dst_offset_x = 0;
8443 g_em->dst_offset_y = 0;
8444 g_em->width = TILEX;
8445 g_em->height = TILEY;
8449 /* skip check for EMC elements not contained in original EMC artwork */
8450 if (element == EL_PLAYER_3 ||
8451 element == EL_PLAYER_4)
8454 if (g_em->bitmap != debug_bitmap ||
8455 g_em->src_x != debug_src_x ||
8456 g_em->src_y != debug_src_y)
8458 static int last_i = -1;
8466 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
8467 p, i, element, element_info[element].token_name,
8468 element_action_info[effective_action].suffix, direction);
8470 if (element != effective_element)
8471 printf(" [%d ('%s')]",
8473 element_info[effective_element].token_name);
8477 if (g_em->bitmap != debug_bitmap)
8478 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
8479 j, (int)(g_em->bitmap), (int)(debug_bitmap));
8481 if (g_em->src_x != debug_src_x ||
8482 g_em->src_y != debug_src_y)
8483 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8485 g_em->src_x, g_em->src_y,
8486 g_em->src_x / 32, g_em->src_y / 32,
8487 debug_src_x, debug_src_y,
8488 debug_src_x / 32, debug_src_y / 32);
8490 num_em_gfx_errors++;
8500 printf("::: [%d errors found]\n", num_em_gfx_errors);
8506 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
8507 boolean any_player_moving,
8508 boolean any_player_snapping,
8509 boolean any_player_dropping)
8511 if (frame == 0 && !any_player_dropping)
8513 if (!local_player->was_waiting)
8515 if (!CheckSaveEngineSnapshotToList())
8518 local_player->was_waiting = TRUE;
8521 else if (any_player_moving || any_player_snapping || any_player_dropping)
8523 local_player->was_waiting = FALSE;
8527 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8528 boolean murphy_is_dropping)
8530 if (murphy_is_waiting)
8532 if (!local_player->was_waiting)
8534 if (!CheckSaveEngineSnapshotToList())
8537 local_player->was_waiting = TRUE;
8542 local_player->was_waiting = FALSE;
8546 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8547 boolean any_player_moving,
8548 boolean any_player_snapping,
8549 boolean any_player_dropping)
8551 if (tape.single_step && tape.recording && !tape.pausing)
8552 if (frame == 0 && !any_player_dropping)
8553 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8555 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8556 any_player_snapping, any_player_dropping);
8559 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8560 boolean murphy_is_dropping)
8562 boolean murphy_starts_dropping = FALSE;
8565 for (i = 0; i < MAX_PLAYERS; i++)
8566 if (stored_player[i].force_dropping)
8567 murphy_starts_dropping = TRUE;
8569 if (tape.single_step && tape.recording && !tape.pausing)
8570 if (murphy_is_waiting && !murphy_starts_dropping)
8571 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8573 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8576 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8577 int graphic, int sync_frame, int x, int y)
8579 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8581 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8584 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8586 return (IS_NEXT_FRAME(sync_frame, graphic));
8589 int getGraphicInfo_Delay(int graphic)
8591 return graphic_info[graphic].anim_delay;
8594 void PlayMenuSoundExt(int sound)
8596 if (sound == SND_UNDEFINED)
8599 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8600 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8603 if (IS_LOOP_SOUND(sound))
8604 PlaySoundLoop(sound);
8609 void PlayMenuSound()
8611 PlayMenuSoundExt(menu.sound[game_status]);
8614 void PlayMenuSoundStereo(int sound, int stereo_position)
8616 if (sound == SND_UNDEFINED)
8619 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8620 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8623 if (IS_LOOP_SOUND(sound))
8624 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8626 PlaySoundStereo(sound, stereo_position);
8629 void PlayMenuSoundIfLoopExt(int sound)
8631 if (sound == SND_UNDEFINED)
8634 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8635 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8638 if (IS_LOOP_SOUND(sound))
8639 PlaySoundLoop(sound);
8642 void PlayMenuSoundIfLoop()
8644 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8647 void PlayMenuMusicExt(int music)
8649 if (music == MUS_UNDEFINED)
8652 if (!setup.sound_music)
8658 void PlayMenuMusic()
8660 char *curr_music = getCurrentlyPlayingMusicFilename();
8661 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8663 if (!strEqual(curr_music, next_music))
8664 PlayMenuMusicExt(menu.music[game_status]);
8667 void PlayMenuSoundsAndMusic()
8673 static void FadeMenuSounds()
8678 static void FadeMenuMusic()
8680 char *curr_music = getCurrentlyPlayingMusicFilename();
8681 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8683 if (!strEqual(curr_music, next_music))
8687 void FadeMenuSoundsAndMusic()
8693 void PlaySoundActivating()
8696 PlaySound(SND_MENU_ITEM_ACTIVATING);
8700 void PlaySoundSelecting()
8703 PlaySound(SND_MENU_ITEM_SELECTING);
8707 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8709 boolean change_fullscreen = (setup.fullscreen !=
8710 video.fullscreen_enabled);
8711 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8712 setup.window_scaling_percent !=
8713 video.window_scaling_percent);
8715 if (change_window_scaling_percent && video.fullscreen_enabled)
8718 if (!change_window_scaling_percent && !video.fullscreen_available)
8721 #if defined(TARGET_SDL2)
8722 if (change_window_scaling_percent)
8724 SDLSetWindowScaling(setup.window_scaling_percent);
8728 else if (change_fullscreen)
8730 SDLSetWindowFullscreen(setup.fullscreen);
8732 /* set setup value according to successfully changed fullscreen mode */
8733 setup.fullscreen = video.fullscreen_enabled;
8739 if (change_fullscreen ||
8740 change_window_scaling_percent)
8742 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8744 /* save backbuffer content which gets lost when toggling fullscreen mode */
8745 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8747 if (change_window_scaling_percent)
8749 /* keep window mode, but change window scaling */
8750 video.fullscreen_enabled = TRUE; /* force new window scaling */
8753 /* toggle fullscreen */
8754 ChangeVideoModeIfNeeded(setup.fullscreen);
8756 /* set setup value according to successfully changed fullscreen mode */
8757 setup.fullscreen = video.fullscreen_enabled;
8759 /* restore backbuffer content from temporary backbuffer backup bitmap */
8760 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8762 FreeBitmap(tmp_backbuffer);
8764 /* update visible window/screen */
8765 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8769 void JoinRectangles(int *x, int *y, int *width, int *height,
8770 int x2, int y2, int width2, int height2)
8772 // do not join with "off-screen" rectangle
8773 if (x2 == -1 || y2 == -1)
8778 *width = MAX(*width, width2);
8779 *height = MAX(*height, height2);
8782 void SetAnimStatus(int anim_status_new)
8784 if (anim_status_new == GAME_MODE_MAIN)
8785 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
8786 else if (anim_status_new == GAME_MODE_SCORES)
8787 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
8789 global.anim_status_next = anim_status_new;
8791 // directly set screen modes that are entered without fading
8792 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
8793 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
8794 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
8795 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
8796 global.anim_status = global.anim_status_next;
8799 void SetGameStatus(int game_status_new)
8801 if (game_status_new != game_status)
8802 game_status_last_screen = game_status;
8804 game_status = game_status_new;
8806 SetAnimStatus(game_status_new);
8809 void SetFontStatus(int game_status_new)
8811 static int last_game_status = -1;
8813 if (game_status_new != -1)
8815 // set game status for font use after storing last game status
8816 last_game_status = game_status;
8817 game_status = game_status_new;
8821 // reset game status after font use from last stored game status
8822 game_status = last_game_status;
8826 void ResetFontStatus()
8831 void ChangeViewportPropertiesIfNeeded()
8833 int gfx_game_mode = game_status;
8834 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8836 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
8837 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8838 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8839 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8840 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8841 int new_win_xsize = vp_window->width;
8842 int new_win_ysize = vp_window->height;
8843 int border_size = vp_playfield->border_size;
8844 int new_sx = vp_playfield->x + border_size;
8845 int new_sy = vp_playfield->y + border_size;
8846 int new_sxsize = vp_playfield->width - 2 * border_size;
8847 int new_sysize = vp_playfield->height - 2 * border_size;
8848 int new_real_sx = vp_playfield->x;
8849 int new_real_sy = vp_playfield->y;
8850 int new_full_sxsize = vp_playfield->width;
8851 int new_full_sysize = vp_playfield->height;
8852 int new_dx = vp_door_1->x;
8853 int new_dy = vp_door_1->y;
8854 int new_dxsize = vp_door_1->width;
8855 int new_dysize = vp_door_1->height;
8856 int new_vx = vp_door_2->x;
8857 int new_vy = vp_door_2->y;
8858 int new_vxsize = vp_door_2->width;
8859 int new_vysize = vp_door_2->height;
8860 int new_ex = vp_door_3->x;
8861 int new_ey = vp_door_3->y;
8862 int new_exsize = vp_door_3->width;
8863 int new_eysize = vp_door_3->height;
8864 int new_tilesize_var =
8865 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8867 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8868 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8869 int new_scr_fieldx = new_sxsize / tilesize;
8870 int new_scr_fieldy = new_sysize / tilesize;
8871 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8872 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8873 boolean init_gfx_buffers = FALSE;
8874 boolean init_video_buffer = FALSE;
8875 boolean init_gadgets_and_anims = FALSE;
8876 boolean init_em_graphics = FALSE;
8878 if (new_win_xsize != WIN_XSIZE ||
8879 new_win_ysize != WIN_YSIZE)
8881 WIN_XSIZE = new_win_xsize;
8882 WIN_YSIZE = new_win_ysize;
8884 init_video_buffer = TRUE;
8885 init_gfx_buffers = TRUE;
8886 init_gadgets_and_anims = TRUE;
8888 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8891 if (new_scr_fieldx != SCR_FIELDX ||
8892 new_scr_fieldy != SCR_FIELDY)
8894 /* this always toggles between MAIN and GAME when using small tile size */
8896 SCR_FIELDX = new_scr_fieldx;
8897 SCR_FIELDY = new_scr_fieldy;
8899 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8910 new_sxsize != SXSIZE ||
8911 new_sysize != SYSIZE ||
8912 new_dxsize != DXSIZE ||
8913 new_dysize != DYSIZE ||
8914 new_vxsize != VXSIZE ||
8915 new_vysize != VYSIZE ||
8916 new_exsize != EXSIZE ||
8917 new_eysize != EYSIZE ||
8918 new_real_sx != REAL_SX ||
8919 new_real_sy != REAL_SY ||
8920 new_full_sxsize != FULL_SXSIZE ||
8921 new_full_sysize != FULL_SYSIZE ||
8922 new_tilesize_var != TILESIZE_VAR
8925 // ------------------------------------------------------------------------
8926 // determine next fading area for changed viewport definitions
8927 // ------------------------------------------------------------------------
8929 // start with current playfield area (default fading area)
8932 FADE_SXSIZE = FULL_SXSIZE;
8933 FADE_SYSIZE = FULL_SYSIZE;
8935 // add new playfield area if position or size has changed
8936 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8937 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8939 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8940 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8943 // add current and new door 1 area if position or size has changed
8944 if (new_dx != DX || new_dy != DY ||
8945 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8947 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8948 DX, DY, DXSIZE, DYSIZE);
8949 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8950 new_dx, new_dy, new_dxsize, new_dysize);
8953 // add current and new door 2 area if position or size has changed
8954 if (new_dx != VX || new_dy != VY ||
8955 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8957 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8958 VX, VY, VXSIZE, VYSIZE);
8959 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8960 new_vx, new_vy, new_vxsize, new_vysize);
8963 // ------------------------------------------------------------------------
8964 // handle changed tile size
8965 // ------------------------------------------------------------------------
8967 if (new_tilesize_var != TILESIZE_VAR)
8969 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8971 // changing tile size invalidates scroll values of engine snapshots
8972 FreeEngineSnapshotSingle();
8974 // changing tile size requires update of graphic mapping for EM engine
8975 init_em_graphics = TRUE;
8986 SXSIZE = new_sxsize;
8987 SYSIZE = new_sysize;
8988 DXSIZE = new_dxsize;
8989 DYSIZE = new_dysize;
8990 VXSIZE = new_vxsize;
8991 VYSIZE = new_vysize;
8992 EXSIZE = new_exsize;
8993 EYSIZE = new_eysize;
8994 REAL_SX = new_real_sx;
8995 REAL_SY = new_real_sy;
8996 FULL_SXSIZE = new_full_sxsize;
8997 FULL_SYSIZE = new_full_sysize;
8998 TILESIZE_VAR = new_tilesize_var;
9000 init_gfx_buffers = TRUE;
9001 init_gadgets_and_anims = TRUE;
9003 // printf("::: viewports: init_gfx_buffers\n");
9004 // printf("::: viewports: init_gadgets_and_anims\n");
9007 if (init_gfx_buffers)
9009 // printf("::: init_gfx_buffers\n");
9011 SCR_FIELDX = new_scr_fieldx_buffers;
9012 SCR_FIELDY = new_scr_fieldy_buffers;
9016 SCR_FIELDX = new_scr_fieldx;
9017 SCR_FIELDY = new_scr_fieldy;
9019 SetDrawDeactivationMask(REDRAW_NONE);
9020 SetDrawBackgroundMask(REDRAW_FIELD);
9023 if (init_video_buffer)
9025 // printf("::: init_video_buffer\n");
9027 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9028 InitImageTextures();
9031 if (init_gadgets_and_anims)
9033 // printf("::: init_gadgets_and_anims\n");
9036 InitGlobalAnimations();
9039 if (init_em_graphics)
9041 InitGraphicInfo_EM();