1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 /* select level set with EMC X11 graphics before activating EM GFX debugging */
27 #define DEBUG_EM_GFX FALSE
28 #define DEBUG_FRAME_TIME FALSE
30 /* tool button identifiers */
31 #define TOOL_CTRL_ID_YES 0
32 #define TOOL_CTRL_ID_NO 1
33 #define TOOL_CTRL_ID_CONFIRM 2
34 #define TOOL_CTRL_ID_PLAYER_1 3
35 #define TOOL_CTRL_ID_PLAYER_2 4
36 #define TOOL_CTRL_ID_PLAYER_3 5
37 #define TOOL_CTRL_ID_PLAYER_4 6
39 #define NUM_TOOL_BUTTONS 7
41 /* constants for number of doors and door parts */
43 #define NUM_PANELS NUM_DOORS
44 // #define NUM_PANELS 0
45 #define MAX_PARTS_PER_DOOR 8
46 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
47 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
50 struct DoorPartOrderInfo
56 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
58 struct DoorPartControlInfo
62 struct DoorPartPosInfo *pos;
65 static struct DoorPartControlInfo door_part_controls[] =
69 IMG_GFX_DOOR_1_PART_1,
74 IMG_GFX_DOOR_1_PART_2,
79 IMG_GFX_DOOR_1_PART_3,
84 IMG_GFX_DOOR_1_PART_4,
89 IMG_GFX_DOOR_1_PART_5,
94 IMG_GFX_DOOR_1_PART_6,
99 IMG_GFX_DOOR_1_PART_7,
104 IMG_GFX_DOOR_1_PART_8,
110 IMG_GFX_DOOR_2_PART_1,
115 IMG_GFX_DOOR_2_PART_2,
120 IMG_GFX_DOOR_2_PART_3,
125 IMG_GFX_DOOR_2_PART_4,
130 IMG_GFX_DOOR_2_PART_5,
135 IMG_GFX_DOOR_2_PART_6,
140 IMG_GFX_DOOR_2_PART_7,
145 IMG_GFX_DOOR_2_PART_8,
151 IMG_BACKGROUND_PANEL,
168 /* forward declaration for internal use */
169 static void UnmapToolButtons();
170 static void HandleToolButtons(struct GadgetInfo *);
171 static int el_act_dir2crm(int, int, int);
172 static int el_act2crm(int, int);
174 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
175 static int request_gadget_id = -1;
177 static char *print_if_not_empty(int element)
179 static char *s = NULL;
180 char *token_name = element_info[element].token_name;
185 s = checked_malloc(strlen(token_name) + 10 + 1);
187 if (element != EL_EMPTY)
188 sprintf(s, "%d\t['%s']", element, token_name);
190 sprintf(s, "%d", element);
195 int correctLevelPosX_EM(int lx)
198 lx -= (BorderElement != EL_EMPTY ? 1 : 0);
203 int correctLevelPosY_EM(int ly)
206 ly -= (BorderElement != EL_EMPTY ? 1 : 0);
211 static int getFieldbufferOffsetX_RND()
213 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
214 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
215 int dx_var = dx * TILESIZE_VAR / TILESIZE;
218 if (EVEN(SCR_FIELDX))
220 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
222 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
223 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
225 fx += (dx_var > 0 ? TILEX_VAR : 0);
232 if (full_lev_fieldx <= SCR_FIELDX)
234 if (EVEN(SCR_FIELDX))
235 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
237 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
243 static int getFieldbufferOffsetY_RND()
245 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
246 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
247 int dy_var = dy * TILESIZE_VAR / TILESIZE;
250 if (EVEN(SCR_FIELDY))
252 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
254 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
255 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
257 fy += (dy_var > 0 ? TILEY_VAR : 0);
264 if (full_lev_fieldy <= SCR_FIELDY)
266 if (EVEN(SCR_FIELDY))
267 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
269 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
275 static int getLevelFromScreenX_RND(int sx)
277 int fx = getFieldbufferOffsetX_RND();
280 int lx = LEVELX((px + dx) / TILESIZE_VAR);
285 static int getLevelFromScreenY_RND(int sy)
287 int fy = getFieldbufferOffsetY_RND();
290 int ly = LEVELY((py + dy) / TILESIZE_VAR);
295 static int getLevelFromScreenX_EM(int sx)
297 int level_xsize = level.native_em_level->lev->width;
298 int full_xsize = level_xsize * TILESIZE_VAR;
300 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
302 int fx = getFieldbufferOffsetX_EM();
305 int lx = LEVELX((px + dx) / TILESIZE_VAR);
307 lx = correctLevelPosX_EM(lx);
312 static int getLevelFromScreenY_EM(int sy)
314 int level_ysize = level.native_em_level->lev->height;
315 int full_ysize = level_ysize * TILESIZE_VAR;
317 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
319 int fy = getFieldbufferOffsetY_EM();
322 int ly = LEVELY((py + dy) / TILESIZE_VAR);
324 ly = correctLevelPosY_EM(ly);
329 static int getLevelFromScreenX_SP(int sx)
331 int menBorder = setup.sp_show_border_elements;
332 int level_xsize = level.native_sp_level->width;
333 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
335 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
337 int fx = getFieldbufferOffsetX_SP();
340 int lx = LEVELX((px + dx) / TILESIZE_VAR);
345 static int getLevelFromScreenY_SP(int sy)
347 int menBorder = setup.sp_show_border_elements;
348 int level_ysize = level.native_sp_level->height;
349 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
351 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
353 int fy = getFieldbufferOffsetY_SP();
356 int ly = LEVELY((py + dy) / TILESIZE_VAR);
361 static int getLevelFromScreenX_MM(int sx)
364 int level_xsize = level.native_mm_level->fieldx;
365 int full_xsize = level_xsize * TILESIZE_VAR;
367 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
371 int lx = px / TILESIZE_VAR;
376 static int getLevelFromScreenY_MM(int sy)
379 int level_ysize = level.native_mm_level->fieldy;
380 int full_ysize = level_ysize * TILESIZE_VAR;
382 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
386 int ly = py / TILESIZE_VAR;
391 int getLevelFromScreenX(int x)
393 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
394 return getLevelFromScreenX_EM(x);
395 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
396 return getLevelFromScreenX_SP(x);
397 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
398 return getLevelFromScreenX_MM(x);
400 return getLevelFromScreenX_RND(x);
403 int getLevelFromScreenY(int y)
405 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
406 return getLevelFromScreenY_EM(y);
407 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
408 return getLevelFromScreenY_SP(y);
409 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
410 return getLevelFromScreenY_MM(y);
412 return getLevelFromScreenY_RND(y);
415 void DumpTile(int x, int y)
421 printf_line("-", 79);
422 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
423 printf_line("-", 79);
425 if (!IN_LEV_FIELD(x, y))
427 printf("(not in level field)\n");
433 token_name = element_info[Feld[x][y]].token_name;
435 printf(" Feld: %d\t['%s']\n", Feld[x][y], token_name);
436 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
437 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
438 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
439 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
440 printf(" MovPos: %d\n", MovPos[x][y]);
441 printf(" MovDir: %d\n", MovDir[x][y]);
442 printf(" MovDelay: %d\n", MovDelay[x][y]);
443 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
444 printf(" CustomValue: %d\n", CustomValue[x][y]);
445 printf(" GfxElement: %d\n", GfxElement[x][y]);
446 printf(" GfxAction: %d\n", GfxAction[x][y]);
447 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
448 printf(" Player x/y: %d, %d\n", local_player->jx, local_player->jy);
452 void DumpTileFromScreen(int sx, int sy)
454 int lx = getLevelFromScreenX(sx);
455 int ly = getLevelFromScreenY(sy);
460 void SetDrawtoField(int mode)
462 if (mode == DRAW_TO_FIELDBUFFER)
468 BX2 = SCR_FIELDX + 1;
469 BY2 = SCR_FIELDY + 1;
471 drawto_field = fieldbuffer;
473 else /* DRAW_TO_BACKBUFFER */
479 BX2 = SCR_FIELDX - 1;
480 BY2 = SCR_FIELDY - 1;
482 drawto_field = backbuffer;
486 static void RedrawPlayfield_RND()
488 if (game.envelope_active)
491 DrawLevel(REDRAW_ALL);
495 void RedrawPlayfield()
497 if (game_status != GAME_MODE_PLAYING)
500 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
501 RedrawPlayfield_EM(TRUE);
502 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
503 RedrawPlayfield_SP(TRUE);
504 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
505 RedrawPlayfield_MM();
506 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
507 RedrawPlayfield_RND();
509 BlitScreenToBitmap(backbuffer);
511 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
515 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
518 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
519 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
521 if (x == -1 && y == -1)
524 if (draw_target == DRAW_TO_SCREEN)
525 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
527 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
530 static void DrawMaskedBorderExt_FIELD(int draw_target)
532 if (global.border_status >= GAME_MODE_MAIN &&
533 global.border_status <= GAME_MODE_PLAYING &&
534 border.draw_masked[global.border_status])
535 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
539 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
541 // when drawing to backbuffer, never draw border over open doors
542 if (draw_target == DRAW_TO_BACKBUFFER &&
543 (GetDoorState() & DOOR_OPEN_1))
546 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
547 (global.border_status != GAME_MODE_EDITOR ||
548 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
549 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
552 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
554 // when drawing to backbuffer, never draw border over open doors
555 if (draw_target == DRAW_TO_BACKBUFFER &&
556 (GetDoorState() & DOOR_OPEN_2))
559 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
560 global.border_status != GAME_MODE_EDITOR)
561 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
564 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
566 /* currently not available */
569 static void DrawMaskedBorderExt_ALL(int draw_target)
571 DrawMaskedBorderExt_FIELD(draw_target);
572 DrawMaskedBorderExt_DOOR_1(draw_target);
573 DrawMaskedBorderExt_DOOR_2(draw_target);
574 DrawMaskedBorderExt_DOOR_3(draw_target);
577 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
579 /* never draw masked screen borders on borderless screens */
580 if (global.border_status == GAME_MODE_LOADING ||
581 global.border_status == GAME_MODE_TITLE)
584 if (redraw_mask & REDRAW_ALL)
585 DrawMaskedBorderExt_ALL(draw_target);
588 if (redraw_mask & REDRAW_FIELD)
589 DrawMaskedBorderExt_FIELD(draw_target);
590 if (redraw_mask & REDRAW_DOOR_1)
591 DrawMaskedBorderExt_DOOR_1(draw_target);
592 if (redraw_mask & REDRAW_DOOR_2)
593 DrawMaskedBorderExt_DOOR_2(draw_target);
594 if (redraw_mask & REDRAW_DOOR_3)
595 DrawMaskedBorderExt_DOOR_3(draw_target);
599 void DrawMaskedBorder_FIELD()
601 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
604 void DrawMaskedBorder(int redraw_mask)
606 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
609 void DrawMaskedBorderToTarget(int draw_target)
611 if (draw_target == DRAW_TO_BACKBUFFER ||
612 draw_target == DRAW_TO_SCREEN)
614 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
618 int last_border_status = global.border_status;
620 if (draw_target == DRAW_TO_FADE_SOURCE)
622 global.border_status = gfx.fade_border_source_status;
623 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
625 else if (draw_target == DRAW_TO_FADE_TARGET)
627 global.border_status = gfx.fade_border_target_status;
628 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
631 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
633 global.border_status = last_border_status;
634 gfx.masked_border_bitmap_ptr = backbuffer;
638 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
640 int fx = getFieldbufferOffsetX_RND();
641 int fy = getFieldbufferOffsetY_RND();
643 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
646 void BlitScreenToBitmap(Bitmap *target_bitmap)
648 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
649 BlitScreenToBitmap_EM(target_bitmap);
650 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
651 BlitScreenToBitmap_SP(target_bitmap);
652 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
653 BlitScreenToBitmap_MM(target_bitmap);
654 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
655 BlitScreenToBitmap_RND(target_bitmap);
657 redraw_mask |= REDRAW_FIELD;
660 void DrawFramesPerSecond()
663 int font_nr = FONT_TEXT_2;
664 int font_width = getFontWidth(font_nr);
665 int draw_deactivation_mask = GetDrawDeactivationMask();
666 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
668 /* draw FPS with leading space (needed if field buffer deactivated) */
669 sprintf(text, " %04.1f fps", global.frames_per_second);
671 /* override draw deactivation mask (required for invisible warp mode) */
672 SetDrawDeactivationMask(REDRAW_NONE);
674 /* draw opaque FPS if field buffer deactivated, else draw masked FPS */
675 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
676 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
678 /* set draw deactivation mask to previous value */
679 SetDrawDeactivationMask(draw_deactivation_mask);
681 /* force full-screen redraw in this frame */
682 redraw_mask = REDRAW_ALL;
686 static void PrintFrameTimeDebugging()
688 static unsigned int last_counter = 0;
689 unsigned int counter = Counter();
690 int diff_1 = counter - last_counter;
691 int diff_2 = diff_1 - GAME_FRAME_DELAY;
693 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
694 char diff_bar[2 * diff_2_max + 5];
698 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
700 for (i = 0; i < diff_2_max; i++)
701 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
702 i >= diff_2_max - diff_2_cut ? '-' : ' ');
704 diff_bar[pos++] = '|';
706 for (i = 0; i < diff_2_max; i++)
707 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
709 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
711 diff_bar[pos++] = '\0';
713 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
716 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
719 last_counter = counter;
723 static int unifiedRedrawMask(int mask)
725 if (mask & REDRAW_ALL)
728 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
734 static boolean equalRedrawMasks(int mask_1, int mask_2)
736 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
741 static int last_redraw_mask = REDRAW_NONE;
743 // force screen redraw in every frame to continue drawing global animations
744 // (but always use the last redraw mask to prevent unwanted side effects)
745 if (redraw_mask == REDRAW_NONE)
746 redraw_mask = last_redraw_mask;
748 last_redraw_mask = redraw_mask;
751 // masked border now drawn immediately when blitting backbuffer to window
753 // draw masked border to all viewports, if defined
754 DrawMaskedBorder(redraw_mask);
757 // draw frames per second (only if debug mode is enabled)
758 if (redraw_mask & REDRAW_FPS)
759 DrawFramesPerSecond();
761 // remove playfield redraw before potentially merging with doors redraw
762 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
763 redraw_mask &= ~REDRAW_FIELD;
765 // redraw complete window if both playfield and (some) doors need redraw
766 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
767 redraw_mask = REDRAW_ALL;
769 /* although redrawing the whole window would be fine for normal gameplay,
770 being able to only redraw the playfield is required for deactivating
771 certain drawing areas (mainly playfield) to work, which is needed for
772 warp-forward to be fast enough (by skipping redraw of most frames) */
774 if (redraw_mask & REDRAW_ALL)
776 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
778 else if (redraw_mask & REDRAW_FIELD)
780 BlitBitmap(backbuffer, window,
781 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
783 else if (redraw_mask & REDRAW_DOORS)
785 // merge door areas to prevent calling screen redraw more than once
791 if (redraw_mask & REDRAW_DOOR_1)
795 x2 = MAX(x2, DX + DXSIZE);
796 y2 = MAX(y2, DY + DYSIZE);
799 if (redraw_mask & REDRAW_DOOR_2)
803 x2 = MAX(x2, VX + VXSIZE);
804 y2 = MAX(y2, VY + VYSIZE);
807 if (redraw_mask & REDRAW_DOOR_3)
811 x2 = MAX(x2, EX + EXSIZE);
812 y2 = MAX(y2, EY + EYSIZE);
815 // make sure that at least one pixel is blitted, and inside the screen
816 // (else nothing is blitted, causing the animations not to be updated)
817 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
818 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
819 x2 = MIN(MAX(1, x2), WIN_XSIZE);
820 y2 = MIN(MAX(1, y2), WIN_YSIZE);
822 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
825 redraw_mask = REDRAW_NONE;
828 PrintFrameTimeDebugging();
832 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
834 unsigned int frame_delay_value_old = GetVideoFrameDelay();
836 SetVideoFrameDelay(frame_delay_value);
840 SetVideoFrameDelay(frame_delay_value_old);
843 static int fade_type_skip = FADE_TYPE_NONE;
845 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
847 void (*draw_border_function)(void) = NULL;
848 int x, y, width, height;
849 int fade_delay, post_delay;
851 if (fade_type == FADE_TYPE_FADE_OUT)
853 if (fade_type_skip != FADE_TYPE_NONE)
855 /* skip all fade operations until specified fade operation */
856 if (fade_type & fade_type_skip)
857 fade_type_skip = FADE_TYPE_NONE;
862 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
866 redraw_mask |= fade_mask;
868 if (fade_type == FADE_TYPE_SKIP)
870 fade_type_skip = fade_mode;
875 fade_delay = fading.fade_delay;
876 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
878 if (fade_type_skip != FADE_TYPE_NONE)
880 /* skip all fade operations until specified fade operation */
881 if (fade_type & fade_type_skip)
882 fade_type_skip = FADE_TYPE_NONE;
887 if (global.autoplay_leveldir)
892 if (fade_mask == REDRAW_FIELD)
897 height = FADE_SYSIZE;
899 if (border.draw_masked_when_fading)
900 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
902 DrawMaskedBorder_FIELD(); /* draw once */
904 else /* REDRAW_ALL */
912 if (!setup.fade_screens ||
914 fading.fade_mode == FADE_MODE_NONE)
916 if (fade_mode == FADE_MODE_FADE_OUT)
919 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
921 redraw_mask &= ~fade_mask;
926 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
927 draw_border_function);
929 redraw_mask &= ~fade_mask;
932 static void SetScreenStates_BeforeFadingIn()
934 // temporarily set screen mode for animations to screen after fading in
935 global.anim_status = global.anim_status_next;
937 // store backbuffer with all animations that will be started after fading in
938 if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
939 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
941 // set screen mode for animations back to fading
942 global.anim_status = GAME_MODE_PSEUDO_FADING;
945 static void SetScreenStates_AfterFadingIn()
947 // store new source screen (to use correct masked border for fading)
948 gfx.fade_border_source_status = global.border_status;
950 global.anim_status = global.anim_status_next;
953 static void SetScreenStates_BeforeFadingOut()
955 // store new target screen (to use correct masked border for fading)
956 gfx.fade_border_target_status = game_status;
958 // set screen mode for animations to fading
959 global.anim_status = GAME_MODE_PSEUDO_FADING;
961 // store backbuffer with all animations that will be stopped for fading out
962 if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
963 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
966 static void SetScreenStates_AfterFadingOut()
968 global.border_status = game_status;
971 void FadeIn(int fade_mask)
973 SetScreenStates_BeforeFadingIn();
976 DrawMaskedBorder(REDRAW_ALL);
979 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
980 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
982 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
986 FADE_SXSIZE = FULL_SXSIZE;
987 FADE_SYSIZE = FULL_SYSIZE;
989 if (game_status == GAME_MODE_PLAYING &&
990 strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
991 SetOverlayActive(TRUE);
993 SetScreenStates_AfterFadingIn();
995 // force update of global animation status in case of rapid screen changes
996 redraw_mask = REDRAW_ALL;
1000 void FadeOut(int fade_mask)
1002 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1003 if (!equalRedrawMasks(fade_mask, redraw_mask))
1006 SetScreenStates_BeforeFadingOut();
1008 SetOverlayActive(FALSE);
1011 DrawMaskedBorder(REDRAW_ALL);
1014 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1015 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1017 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1019 SetScreenStates_AfterFadingOut();
1022 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1024 static struct TitleFadingInfo fading_leave_stored;
1027 fading_leave_stored = fading_leave;
1029 fading = fading_leave_stored;
1032 void FadeSetEnterMenu()
1034 fading = menu.enter_menu;
1036 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1039 void FadeSetLeaveMenu()
1041 fading = menu.leave_menu;
1043 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1046 void FadeSetEnterScreen()
1048 fading = menu.enter_screen[game_status];
1050 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
1053 void FadeSetNextScreen()
1055 fading = menu.next_screen[game_status];
1057 // (do not overwrite fade mode set by FadeSetEnterScreen)
1058 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1061 void FadeSetLeaveScreen()
1063 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
1066 void FadeSetFromType(int type)
1068 if (type & TYPE_ENTER_SCREEN)
1069 FadeSetEnterScreen();
1070 else if (type & TYPE_ENTER)
1072 else if (type & TYPE_LEAVE)
1076 void FadeSetDisabled()
1078 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1080 fading = fading_none;
1083 void FadeSkipNextFadeIn()
1085 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1088 void FadeSkipNextFadeOut()
1090 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1093 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1095 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1097 return (graphic == IMG_UNDEFINED ? NULL :
1098 graphic_info[graphic].bitmap != NULL || redefined ?
1099 graphic_info[graphic].bitmap :
1100 graphic_info[default_graphic].bitmap);
1103 Bitmap *getBackgroundBitmap(int graphic)
1105 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1108 Bitmap *getGlobalBorderBitmap(int graphic)
1110 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1113 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1116 (status == GAME_MODE_MAIN ||
1117 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1118 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1119 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1120 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1123 return getGlobalBorderBitmap(graphic);
1126 void SetWindowBackgroundImageIfDefined(int graphic)
1128 if (graphic_info[graphic].bitmap)
1129 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1132 void SetMainBackgroundImageIfDefined(int graphic)
1134 if (graphic_info[graphic].bitmap)
1135 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1138 void SetDoorBackgroundImageIfDefined(int graphic)
1140 if (graphic_info[graphic].bitmap)
1141 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1144 void SetWindowBackgroundImage(int graphic)
1146 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1149 void SetMainBackgroundImage(int graphic)
1151 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1154 void SetDoorBackgroundImage(int graphic)
1156 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1159 void SetPanelBackground()
1161 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1163 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1164 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1166 SetDoorBackgroundBitmap(bitmap_db_panel);
1169 void DrawBackground(int x, int y, int width, int height)
1171 /* "drawto" might still point to playfield buffer here (hall of fame) */
1172 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1174 if (IN_GFX_FIELD_FULL(x, y))
1175 redraw_mask |= REDRAW_FIELD;
1176 else if (IN_GFX_DOOR_1(x, y))
1177 redraw_mask |= REDRAW_DOOR_1;
1178 else if (IN_GFX_DOOR_2(x, y))
1179 redraw_mask |= REDRAW_DOOR_2;
1180 else if (IN_GFX_DOOR_3(x, y))
1181 redraw_mask |= REDRAW_DOOR_3;
1184 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1186 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1188 if (font->bitmap == NULL)
1191 DrawBackground(x, y, width, height);
1194 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1196 struct GraphicInfo *g = &graphic_info[graphic];
1198 if (g->bitmap == NULL)
1201 DrawBackground(x, y, width, height);
1204 static int game_status_last = -1;
1205 static Bitmap *global_border_bitmap_last = NULL;
1206 static Bitmap *global_border_bitmap = NULL;
1207 static int real_sx_last = -1, real_sy_last = -1;
1208 static int full_sxsize_last = -1, full_sysize_last = -1;
1209 static int dx_last = -1, dy_last = -1;
1210 static int dxsize_last = -1, dysize_last = -1;
1211 static int vx_last = -1, vy_last = -1;
1212 static int vxsize_last = -1, vysize_last = -1;
1214 boolean CheckIfGlobalBorderHasChanged()
1216 // if game status has not changed, global border has not changed either
1217 if (game_status == game_status_last)
1220 // determine and store new global border bitmap for current game status
1221 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1223 return (global_border_bitmap_last != global_border_bitmap);
1226 boolean CheckIfGlobalBorderRedrawIsNeeded()
1228 // if game status has not changed, nothing has to be redrawn
1229 if (game_status == game_status_last)
1232 // redraw if last screen was title screen
1233 if (game_status_last == GAME_MODE_TITLE)
1236 // redraw if global screen border has changed
1237 if (CheckIfGlobalBorderHasChanged())
1240 // redraw if position or size of playfield area has changed
1241 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1242 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1245 // redraw if position or size of door area has changed
1246 if (dx_last != DX || dy_last != DY ||
1247 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1250 // redraw if position or size of tape area has changed
1251 if (vx_last != VX || vy_last != VY ||
1252 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1258 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1261 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1263 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1266 void RedrawGlobalBorder()
1268 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1270 RedrawGlobalBorderFromBitmap(bitmap);
1272 redraw_mask = REDRAW_ALL;
1275 static void RedrawGlobalBorderIfNeeded()
1277 if (game_status == game_status_last)
1280 // copy current draw buffer to later copy back areas that have not changed
1281 if (game_status_last != GAME_MODE_TITLE)
1282 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1284 if (CheckIfGlobalBorderRedrawIsNeeded())
1286 // redraw global screen border (or clear, if defined to be empty)
1287 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1289 // copy previous playfield and door areas, if they are defined on both
1290 // previous and current screen and if they still have the same size
1292 if (real_sx_last != -1 && real_sy_last != -1 &&
1293 REAL_SX != -1 && REAL_SY != -1 &&
1294 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1295 BlitBitmap(bitmap_db_store_1, backbuffer,
1296 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1299 if (dx_last != -1 && dy_last != -1 &&
1300 DX != -1 && DY != -1 &&
1301 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1302 BlitBitmap(bitmap_db_store_1, backbuffer,
1303 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1305 if (vx_last != -1 && vy_last != -1 &&
1306 VX != -1 && VY != -1 &&
1307 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1308 BlitBitmap(bitmap_db_store_1, backbuffer,
1309 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1311 redraw_mask = REDRAW_ALL;
1314 game_status_last = game_status;
1316 global_border_bitmap_last = global_border_bitmap;
1318 real_sx_last = REAL_SX;
1319 real_sy_last = REAL_SY;
1320 full_sxsize_last = FULL_SXSIZE;
1321 full_sysize_last = FULL_SYSIZE;
1324 dxsize_last = DXSIZE;
1325 dysize_last = DYSIZE;
1328 vxsize_last = VXSIZE;
1329 vysize_last = VYSIZE;
1334 RedrawGlobalBorderIfNeeded();
1336 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1337 /* (when entering hall of fame after playing) */
1338 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1340 /* !!! maybe this should be done before clearing the background !!! */
1341 if (game_status == GAME_MODE_PLAYING)
1343 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1344 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1348 SetDrawtoField(DRAW_TO_BACKBUFFER);
1352 void MarkTileDirty(int x, int y)
1354 redraw_mask |= REDRAW_FIELD;
1357 void SetBorderElement()
1361 BorderElement = EL_EMPTY;
1363 /* the MM game engine does not use a visible border element */
1364 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1367 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1369 for (x = 0; x < lev_fieldx; x++)
1371 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1372 BorderElement = EL_STEELWALL;
1374 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1380 void FloodFillLevel(int from_x, int from_y, int fill_element,
1381 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1382 int max_fieldx, int max_fieldy)
1386 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1387 static int safety = 0;
1389 /* check if starting field still has the desired content */
1390 if (field[from_x][from_y] == fill_element)
1395 if (safety > max_fieldx * max_fieldy)
1396 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1398 old_element = field[from_x][from_y];
1399 field[from_x][from_y] = fill_element;
1401 for (i = 0; i < 4; i++)
1403 x = from_x + check[i][0];
1404 y = from_y + check[i][1];
1406 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1407 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1413 void SetRandomAnimationValue(int x, int y)
1415 gfx.anim_random_frame = GfxRandom[x][y];
1418 int getGraphicAnimationFrame(int graphic, int sync_frame)
1420 /* animation synchronized with global frame counter, not move position */
1421 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1422 sync_frame = FrameCounter;
1424 return getAnimationFrame(graphic_info[graphic].anim_frames,
1425 graphic_info[graphic].anim_delay,
1426 graphic_info[graphic].anim_mode,
1427 graphic_info[graphic].anim_start_frame,
1431 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1433 struct GraphicInfo *g = &graphic_info[graphic];
1434 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1436 if (tilesize == gfx.standard_tile_size)
1437 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1438 else if (tilesize == game.tile_size)
1439 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1441 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1444 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1445 boolean get_backside)
1447 struct GraphicInfo *g = &graphic_info[graphic];
1448 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1449 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1451 if (g->offset_y == 0) /* frames are ordered horizontally */
1453 int max_width = g->anim_frames_per_line * g->width;
1454 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1456 *x = pos % max_width;
1457 *y = src_y % g->height + pos / max_width * g->height;
1459 else if (g->offset_x == 0) /* frames are ordered vertically */
1461 int max_height = g->anim_frames_per_line * g->height;
1462 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1464 *x = src_x % g->width + pos / max_height * g->width;
1465 *y = pos % max_height;
1467 else /* frames are ordered diagonally */
1469 *x = src_x + frame * g->offset_x;
1470 *y = src_y + frame * g->offset_y;
1474 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1475 Bitmap **bitmap, int *x, int *y,
1476 boolean get_backside)
1478 struct GraphicInfo *g = &graphic_info[graphic];
1480 // if no in-game graphics defined, always use standard graphic size
1481 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1482 tilesize = TILESIZE;
1484 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1485 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1487 *x = *x * tilesize / g->tile_size;
1488 *y = *y * tilesize / g->tile_size;
1491 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1492 Bitmap **bitmap, int *x, int *y)
1494 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1497 void getFixedGraphicSource(int graphic, int frame,
1498 Bitmap **bitmap, int *x, int *y)
1500 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1503 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1505 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1508 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1509 int *x, int *y, boolean get_backside)
1511 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1515 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1517 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1520 void DrawGraphic(int x, int y, int graphic, int frame)
1523 if (!IN_SCR_FIELD(x, y))
1525 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1526 printf("DrawGraphic(): This should never happen!\n");
1531 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1534 MarkTileDirty(x, y);
1537 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1540 if (!IN_SCR_FIELD(x, y))
1542 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1543 printf("DrawGraphic(): This should never happen!\n");
1548 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1550 MarkTileDirty(x, y);
1553 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1559 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1561 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1564 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1570 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1571 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1574 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1577 if (!IN_SCR_FIELD(x, y))
1579 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1580 printf("DrawGraphicThruMask(): This should never happen!\n");
1585 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1588 MarkTileDirty(x, y);
1591 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1594 if (!IN_SCR_FIELD(x, y))
1596 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1597 printf("DrawGraphicThruMask(): This should never happen!\n");
1602 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1604 MarkTileDirty(x, y);
1607 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1613 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1615 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1619 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1620 int graphic, int frame)
1625 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1627 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1631 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1633 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1635 MarkTileDirty(x / tilesize, y / tilesize);
1638 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1641 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1642 graphic, frame, tilesize);
1643 MarkTileDirty(x / tilesize, y / tilesize);
1646 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1652 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1653 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1656 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1657 int frame, int tilesize)
1662 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1663 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1666 void DrawMiniGraphic(int x, int y, int graphic)
1668 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1669 MarkTileDirty(x / 2, y / 2);
1672 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1677 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1678 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1681 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1682 int graphic, int frame,
1683 int cut_mode, int mask_mode)
1688 int width = TILEX, height = TILEY;
1691 if (dx || dy) /* shifted graphic */
1693 if (x < BX1) /* object enters playfield from the left */
1700 else if (x > BX2) /* object enters playfield from the right */
1706 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1712 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1714 else if (dx) /* general horizontal movement */
1715 MarkTileDirty(x + SIGN(dx), y);
1717 if (y < BY1) /* object enters playfield from the top */
1719 if (cut_mode == CUT_BELOW) /* object completely above top border */
1727 else if (y > BY2) /* object enters playfield from the bottom */
1733 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1739 else if (dy > 0 && cut_mode == CUT_ABOVE)
1741 if (y == BY2) /* object completely above bottom border */
1747 MarkTileDirty(x, y + 1);
1748 } /* object leaves playfield to the bottom */
1749 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1751 else if (dy) /* general vertical movement */
1752 MarkTileDirty(x, y + SIGN(dy));
1756 if (!IN_SCR_FIELD(x, y))
1758 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1759 printf("DrawGraphicShifted(): This should never happen!\n");
1764 width = width * TILESIZE_VAR / TILESIZE;
1765 height = height * TILESIZE_VAR / TILESIZE;
1766 cx = cx * TILESIZE_VAR / TILESIZE;
1767 cy = cy * TILESIZE_VAR / TILESIZE;
1768 dx = dx * TILESIZE_VAR / TILESIZE;
1769 dy = dy * TILESIZE_VAR / TILESIZE;
1771 if (width > 0 && height > 0)
1773 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1778 dst_x = FX + x * TILEX_VAR + dx;
1779 dst_y = FY + y * TILEY_VAR + dy;
1781 if (mask_mode == USE_MASKING)
1782 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1785 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1788 MarkTileDirty(x, y);
1792 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1793 int graphic, int frame,
1794 int cut_mode, int mask_mode)
1799 int width = TILEX_VAR, height = TILEY_VAR;
1802 int x2 = x + SIGN(dx);
1803 int y2 = y + SIGN(dy);
1805 /* movement with two-tile animations must be sync'ed with movement position,
1806 not with current GfxFrame (which can be higher when using slow movement) */
1807 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1808 int anim_frames = graphic_info[graphic].anim_frames;
1810 /* (we also need anim_delay here for movement animations with less frames) */
1811 int anim_delay = graphic_info[graphic].anim_delay;
1812 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1814 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1815 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1817 /* re-calculate animation frame for two-tile movement animation */
1818 frame = getGraphicAnimationFrame(graphic, sync_frame);
1820 /* check if movement start graphic inside screen area and should be drawn */
1821 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1823 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1825 dst_x = FX + x1 * TILEX_VAR;
1826 dst_y = FY + y1 * TILEY_VAR;
1828 if (mask_mode == USE_MASKING)
1829 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1832 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1835 MarkTileDirty(x1, y1);
1838 /* check if movement end graphic inside screen area and should be drawn */
1839 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1841 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1843 dst_x = FX + x2 * TILEX_VAR;
1844 dst_y = FY + y2 * TILEY_VAR;
1846 if (mask_mode == USE_MASKING)
1847 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1850 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1853 MarkTileDirty(x2, y2);
1857 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1858 int graphic, int frame,
1859 int cut_mode, int mask_mode)
1863 DrawGraphic(x, y, graphic, frame);
1868 if (graphic_info[graphic].double_movement) /* EM style movement images */
1869 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1871 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1874 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1875 int frame, int cut_mode)
1877 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1880 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1881 int cut_mode, int mask_mode)
1883 int lx = LEVELX(x), ly = LEVELY(y);
1887 if (IN_LEV_FIELD(lx, ly))
1889 SetRandomAnimationValue(lx, ly);
1891 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1892 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1894 /* do not use double (EM style) movement graphic when not moving */
1895 if (graphic_info[graphic].double_movement && !dx && !dy)
1897 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1898 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1901 else /* border element */
1903 graphic = el2img(element);
1904 frame = getGraphicAnimationFrame(graphic, -1);
1907 if (element == EL_EXPANDABLE_WALL)
1909 boolean left_stopped = FALSE, right_stopped = FALSE;
1911 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1912 left_stopped = TRUE;
1913 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1914 right_stopped = TRUE;
1916 if (left_stopped && right_stopped)
1918 else if (left_stopped)
1920 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1921 frame = graphic_info[graphic].anim_frames - 1;
1923 else if (right_stopped)
1925 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1926 frame = graphic_info[graphic].anim_frames - 1;
1931 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1932 else if (mask_mode == USE_MASKING)
1933 DrawGraphicThruMask(x, y, graphic, frame);
1935 DrawGraphic(x, y, graphic, frame);
1938 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1939 int cut_mode, int mask_mode)
1941 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1942 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1943 cut_mode, mask_mode);
1946 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1949 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1952 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1955 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1958 void DrawLevelElementThruMask(int x, int y, int element)
1960 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1963 void DrawLevelFieldThruMask(int x, int y)
1965 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1968 /* !!! implementation of quicksand is totally broken !!! */
1969 #define IS_CRUMBLED_TILE(x, y, e) \
1970 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1971 !IS_MOVING(x, y) || \
1972 (e) == EL_QUICKSAND_EMPTYING || \
1973 (e) == EL_QUICKSAND_FAST_EMPTYING))
1975 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1980 int width, height, cx, cy;
1981 int sx = SCREENX(x), sy = SCREENY(y);
1982 int crumbled_border_size = graphic_info[graphic].border_size;
1983 int crumbled_tile_size = graphic_info[graphic].tile_size;
1984 int crumbled_border_size_var =
1985 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
1988 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1990 for (i = 1; i < 4; i++)
1992 int dxx = (i & 1 ? dx : 0);
1993 int dyy = (i & 2 ? dy : 0);
1996 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1999 /* check if neighbour field is of same crumble type */
2000 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2001 graphic_info[graphic].class ==
2002 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2004 /* return if check prevents inner corner */
2005 if (same == (dxx == dx && dyy == dy))
2009 /* if we reach this point, we have an inner corner */
2011 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2013 width = crumbled_border_size_var;
2014 height = crumbled_border_size_var;
2015 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2016 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2018 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2019 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2022 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2027 int width, height, bx, by, cx, cy;
2028 int sx = SCREENX(x), sy = SCREENY(y);
2029 int crumbled_border_size = graphic_info[graphic].border_size;
2030 int crumbled_tile_size = graphic_info[graphic].tile_size;
2031 int crumbled_border_size_var =
2032 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2033 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2036 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2038 /* draw simple, sloppy, non-corner-accurate crumbled border */
2040 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2041 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2042 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2043 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2045 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2046 FX + sx * TILEX_VAR + cx,
2047 FY + sy * TILEY_VAR + cy);
2049 /* (remaining middle border part must be at least as big as corner part) */
2050 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2051 crumbled_border_size_var >= TILESIZE_VAR / 3)
2054 /* correct corners of crumbled border, if needed */
2056 for (i = -1; i <= 1; i += 2)
2058 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2059 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2060 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2063 /* check if neighbour field is of same crumble type */
2064 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2065 graphic_info[graphic].class ==
2066 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2068 /* no crumbled corner, but continued crumbled border */
2070 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2071 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2072 int b1 = (i == 1 ? crumbled_border_size_var :
2073 TILESIZE_VAR - 2 * crumbled_border_size_var);
2075 width = crumbled_border_size_var;
2076 height = crumbled_border_size_var;
2078 if (dir == 1 || dir == 2)
2093 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2095 FX + sx * TILEX_VAR + cx,
2096 FY + sy * TILEY_VAR + cy);
2101 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2103 int sx = SCREENX(x), sy = SCREENY(y);
2106 static int xy[4][2] =
2114 if (!IN_LEV_FIELD(x, y))
2117 element = TILE_GFX_ELEMENT(x, y);
2119 if (IS_CRUMBLED_TILE(x, y, element)) /* crumble field itself */
2121 if (!IN_SCR_FIELD(sx, sy))
2124 /* crumble field borders towards direct neighbour fields */
2125 for (i = 0; i < 4; i++)
2127 int xx = x + xy[i][0];
2128 int yy = y + xy[i][1];
2130 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2133 /* check if neighbour field is of same crumble type */
2134 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2135 graphic_info[graphic].class ==
2136 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2139 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2142 /* crumble inner field corners towards corner neighbour fields */
2143 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2144 graphic_info[graphic].anim_frames == 2)
2146 for (i = 0; i < 4; i++)
2148 int dx = (i & 1 ? +1 : -1);
2149 int dy = (i & 2 ? +1 : -1);
2151 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2155 MarkTileDirty(sx, sy);
2157 else /* center field is not crumbled -- crumble neighbour fields */
2159 /* crumble field borders of direct neighbour fields */
2160 for (i = 0; i < 4; i++)
2162 int xx = x + xy[i][0];
2163 int yy = y + xy[i][1];
2164 int sxx = sx + xy[i][0];
2165 int syy = sy + xy[i][1];
2167 if (!IN_LEV_FIELD(xx, yy) ||
2168 !IN_SCR_FIELD(sxx, syy))
2171 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2174 element = TILE_GFX_ELEMENT(xx, yy);
2176 if (!IS_CRUMBLED_TILE(xx, yy, element))
2179 graphic = el_act2crm(element, ACTION_DEFAULT);
2181 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2183 MarkTileDirty(sxx, syy);
2186 /* crumble inner field corners of corner neighbour fields */
2187 for (i = 0; i < 4; i++)
2189 int dx = (i & 1 ? +1 : -1);
2190 int dy = (i & 2 ? +1 : -1);
2196 if (!IN_LEV_FIELD(xx, yy) ||
2197 !IN_SCR_FIELD(sxx, syy))
2200 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2203 element = TILE_GFX_ELEMENT(xx, yy);
2205 if (!IS_CRUMBLED_TILE(xx, yy, element))
2208 graphic = el_act2crm(element, ACTION_DEFAULT);
2210 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2211 graphic_info[graphic].anim_frames == 2)
2212 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2214 MarkTileDirty(sxx, syy);
2219 void DrawLevelFieldCrumbled(int x, int y)
2223 if (!IN_LEV_FIELD(x, y))
2226 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2227 GfxElement[x][y] != EL_UNDEFINED &&
2228 GFX_CRUMBLED(GfxElement[x][y]))
2230 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2235 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2237 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2240 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2243 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2244 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2245 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2246 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2247 int sx = SCREENX(x), sy = SCREENY(y);
2249 DrawGraphic(sx, sy, graphic1, frame1);
2250 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2253 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2255 int sx = SCREENX(x), sy = SCREENY(y);
2256 static int xy[4][2] =
2265 /* crumble direct neighbour fields (required for field borders) */
2266 for (i = 0; i < 4; i++)
2268 int xx = x + xy[i][0];
2269 int yy = y + xy[i][1];
2270 int sxx = sx + xy[i][0];
2271 int syy = sy + xy[i][1];
2273 if (!IN_LEV_FIELD(xx, yy) ||
2274 !IN_SCR_FIELD(sxx, syy) ||
2275 !GFX_CRUMBLED(Feld[xx][yy]) ||
2279 DrawLevelField(xx, yy);
2282 /* crumble corner neighbour fields (required for inner field corners) */
2283 for (i = 0; i < 4; i++)
2285 int dx = (i & 1 ? +1 : -1);
2286 int dy = (i & 2 ? +1 : -1);
2292 if (!IN_LEV_FIELD(xx, yy) ||
2293 !IN_SCR_FIELD(sxx, syy) ||
2294 !GFX_CRUMBLED(Feld[xx][yy]) ||
2298 int element = TILE_GFX_ELEMENT(xx, yy);
2299 int graphic = el_act2crm(element, ACTION_DEFAULT);
2301 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2302 graphic_info[graphic].anim_frames == 2)
2303 DrawLevelField(xx, yy);
2307 static int getBorderElement(int x, int y)
2311 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2312 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2313 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2314 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2315 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2316 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2317 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2319 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2320 int steel_position = (x == -1 && y == -1 ? 0 :
2321 x == lev_fieldx && y == -1 ? 1 :
2322 x == -1 && y == lev_fieldy ? 2 :
2323 x == lev_fieldx && y == lev_fieldy ? 3 :
2324 x == -1 || x == lev_fieldx ? 4 :
2325 y == -1 || y == lev_fieldy ? 5 : 6);
2327 return border[steel_position][steel_type];
2330 void DrawScreenElement(int x, int y, int element)
2332 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2333 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2336 void DrawLevelElement(int x, int y, int element)
2338 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2339 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2342 void DrawScreenField(int x, int y)
2344 int lx = LEVELX(x), ly = LEVELY(y);
2345 int element, content;
2347 if (!IN_LEV_FIELD(lx, ly))
2349 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2352 element = getBorderElement(lx, ly);
2354 DrawScreenElement(x, y, element);
2359 element = Feld[lx][ly];
2360 content = Store[lx][ly];
2362 if (IS_MOVING(lx, ly))
2364 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2365 boolean cut_mode = NO_CUTTING;
2367 if (element == EL_QUICKSAND_EMPTYING ||
2368 element == EL_QUICKSAND_FAST_EMPTYING ||
2369 element == EL_MAGIC_WALL_EMPTYING ||
2370 element == EL_BD_MAGIC_WALL_EMPTYING ||
2371 element == EL_DC_MAGIC_WALL_EMPTYING ||
2372 element == EL_AMOEBA_DROPPING)
2373 cut_mode = CUT_ABOVE;
2374 else if (element == EL_QUICKSAND_FILLING ||
2375 element == EL_QUICKSAND_FAST_FILLING ||
2376 element == EL_MAGIC_WALL_FILLING ||
2377 element == EL_BD_MAGIC_WALL_FILLING ||
2378 element == EL_DC_MAGIC_WALL_FILLING)
2379 cut_mode = CUT_BELOW;
2381 if (cut_mode == CUT_ABOVE)
2382 DrawScreenElement(x, y, element);
2384 DrawScreenElement(x, y, EL_EMPTY);
2387 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2388 else if (cut_mode == NO_CUTTING)
2389 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2392 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2394 if (cut_mode == CUT_BELOW &&
2395 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2396 DrawLevelElement(lx, ly + 1, element);
2399 if (content == EL_ACID)
2401 int dir = MovDir[lx][ly];
2402 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2403 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2405 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2407 // prevent target field from being drawn again (but without masking)
2408 // (this would happen if target field is scanned after moving element)
2409 Stop[newlx][newly] = TRUE;
2412 else if (IS_BLOCKED(lx, ly))
2417 boolean cut_mode = NO_CUTTING;
2418 int element_old, content_old;
2420 Blocked2Moving(lx, ly, &oldx, &oldy);
2423 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2424 MovDir[oldx][oldy] == MV_RIGHT);
2426 element_old = Feld[oldx][oldy];
2427 content_old = Store[oldx][oldy];
2429 if (element_old == EL_QUICKSAND_EMPTYING ||
2430 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2431 element_old == EL_MAGIC_WALL_EMPTYING ||
2432 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2433 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2434 element_old == EL_AMOEBA_DROPPING)
2435 cut_mode = CUT_ABOVE;
2437 DrawScreenElement(x, y, EL_EMPTY);
2440 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2442 else if (cut_mode == NO_CUTTING)
2443 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2446 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2449 else if (IS_DRAWABLE(element))
2450 DrawScreenElement(x, y, element);
2452 DrawScreenElement(x, y, EL_EMPTY);
2455 void DrawLevelField(int x, int y)
2457 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2458 DrawScreenField(SCREENX(x), SCREENY(y));
2459 else if (IS_MOVING(x, y))
2463 Moving2Blocked(x, y, &newx, &newy);
2464 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2465 DrawScreenField(SCREENX(newx), SCREENY(newy));
2467 else if (IS_BLOCKED(x, y))
2471 Blocked2Moving(x, y, &oldx, &oldy);
2472 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2473 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2477 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2478 int (*el2img_function)(int), boolean masked)
2480 int element_base = map_mm_wall_element(element);
2481 int element_bits = (IS_DF_WALL(element) ?
2482 element - EL_DF_WALL_START :
2483 element - EL_MM_WALL_START) & 0x000f;
2484 int graphic = el2img_function(element_base);
2485 int tilesize_draw = tilesize / 2;
2490 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2492 for (i = 0; i < 4; i++)
2494 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2495 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2497 if (element_bits & (1 << i))
2500 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2501 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2503 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2504 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2509 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2510 tilesize_draw, tilesize_draw);
2515 void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2516 int (*el2img_function)(int))
2518 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE);
2521 void DrawSizedElementExt(int x, int y, int element, int tilesize,
2524 if (IS_MM_WALL(element))
2526 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2527 element, tilesize, el2edimg, masked);
2531 int graphic = el2edimg(element);
2534 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2536 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2540 void DrawSizedElement(int x, int y, int element, int tilesize)
2542 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2545 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2547 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2550 void DrawMiniElement(int x, int y, int element)
2554 graphic = el2edimg(element);
2555 DrawMiniGraphic(x, y, graphic);
2558 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2561 int x = sx + scroll_x, y = sy + scroll_y;
2563 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2564 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2565 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2566 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2568 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2571 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2573 int x = sx + scroll_x, y = sy + scroll_y;
2575 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2576 DrawMiniElement(sx, sy, EL_EMPTY);
2577 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2578 DrawMiniElement(sx, sy, Feld[x][y]);
2580 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2583 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2584 int x, int y, int xsize, int ysize,
2585 int tile_width, int tile_height)
2589 int dst_x = startx + x * tile_width;
2590 int dst_y = starty + y * tile_height;
2591 int width = graphic_info[graphic].width;
2592 int height = graphic_info[graphic].height;
2593 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2594 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2595 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2596 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2597 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2598 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2599 boolean draw_masked = graphic_info[graphic].draw_masked;
2601 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2603 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2605 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2609 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2610 inner_sx + (x - 1) * tile_width % inner_width);
2611 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2612 inner_sy + (y - 1) * tile_height % inner_height);
2615 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2618 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2622 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2623 int x, int y, int xsize, int ysize, int font_nr)
2625 int font_width = getFontWidth(font_nr);
2626 int font_height = getFontHeight(font_nr);
2628 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2629 font_width, font_height);
2632 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2634 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2635 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2636 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2637 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2638 boolean no_delay = (tape.warp_forward);
2639 unsigned int anim_delay = 0;
2640 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2641 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2642 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2643 int font_width = getFontWidth(font_nr);
2644 int font_height = getFontHeight(font_nr);
2645 int max_xsize = level.envelope[envelope_nr].xsize;
2646 int max_ysize = level.envelope[envelope_nr].ysize;
2647 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2648 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2649 int xend = max_xsize;
2650 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2651 int xstep = (xstart < xend ? 1 : 0);
2652 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2654 int end = MAX(xend - xstart, yend - ystart);
2657 for (i = start; i <= end; i++)
2659 int last_frame = end; // last frame of this "for" loop
2660 int x = xstart + i * xstep;
2661 int y = ystart + i * ystep;
2662 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2663 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2664 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2665 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2668 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2670 BlitScreenToBitmap(backbuffer);
2672 SetDrawtoField(DRAW_TO_BACKBUFFER);
2674 for (yy = 0; yy < ysize; yy++)
2675 for (xx = 0; xx < xsize; xx++)
2676 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2678 DrawTextBuffer(sx + font_width, sy + font_height,
2679 level.envelope[envelope_nr].text, font_nr, max_xsize,
2680 xsize - 2, ysize - 2, 0, mask_mode,
2681 level.envelope[envelope_nr].autowrap,
2682 level.envelope[envelope_nr].centered, FALSE);
2684 redraw_mask |= REDRAW_FIELD;
2687 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2691 void ShowEnvelope(int envelope_nr)
2693 int element = EL_ENVELOPE_1 + envelope_nr;
2694 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2695 int sound_opening = element_info[element].sound[ACTION_OPENING];
2696 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2697 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2698 boolean no_delay = (tape.warp_forward);
2699 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2700 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2701 int anim_mode = graphic_info[graphic].anim_mode;
2702 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2703 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2705 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2707 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2709 if (anim_mode == ANIM_DEFAULT)
2710 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2712 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2715 Delay(wait_delay_value);
2717 WaitForEventToContinue();
2719 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2721 if (anim_mode != ANIM_NONE)
2722 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2724 if (anim_mode == ANIM_DEFAULT)
2725 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2727 game.envelope_active = FALSE;
2729 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2731 redraw_mask |= REDRAW_FIELD;
2735 static void setRequestBasePosition(int *x, int *y)
2737 int sx_base, sy_base;
2739 if (request.x != -1)
2740 sx_base = request.x;
2741 else if (request.align == ALIGN_LEFT)
2743 else if (request.align == ALIGN_RIGHT)
2744 sx_base = SX + SXSIZE;
2746 sx_base = SX + SXSIZE / 2;
2748 if (request.y != -1)
2749 sy_base = request.y;
2750 else if (request.valign == VALIGN_TOP)
2752 else if (request.valign == VALIGN_BOTTOM)
2753 sy_base = SY + SYSIZE;
2755 sy_base = SY + SYSIZE / 2;
2761 static void setRequestPositionExt(int *x, int *y, int width, int height,
2762 boolean add_border_size)
2764 int border_size = request.border_size;
2765 int sx_base, sy_base;
2768 setRequestBasePosition(&sx_base, &sy_base);
2770 if (request.align == ALIGN_LEFT)
2772 else if (request.align == ALIGN_RIGHT)
2773 sx = sx_base - width;
2775 sx = sx_base - width / 2;
2777 if (request.valign == VALIGN_TOP)
2779 else if (request.valign == VALIGN_BOTTOM)
2780 sy = sy_base - height;
2782 sy = sy_base - height / 2;
2784 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2785 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2787 if (add_border_size)
2797 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2799 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2802 void DrawEnvelopeRequest(char *text)
2804 char *text_final = text;
2805 char *text_door_style = NULL;
2806 int graphic = IMG_BACKGROUND_REQUEST;
2807 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2808 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2809 int font_nr = FONT_REQUEST;
2810 int font_width = getFontWidth(font_nr);
2811 int font_height = getFontHeight(font_nr);
2812 int border_size = request.border_size;
2813 int line_spacing = request.line_spacing;
2814 int line_height = font_height + line_spacing;
2815 int max_text_width = request.width - 2 * border_size;
2816 int max_text_height = request.height - 2 * border_size;
2817 int line_length = max_text_width / font_width;
2818 int max_lines = max_text_height / line_height;
2819 int text_width = line_length * font_width;
2820 int width = request.width;
2821 int height = request.height;
2822 int tile_size = MAX(request.step_offset, 1);
2823 int x_steps = width / tile_size;
2824 int y_steps = height / tile_size;
2825 int sx_offset = border_size;
2826 int sy_offset = border_size;
2830 if (request.centered)
2831 sx_offset = (request.width - text_width) / 2;
2833 if (request.wrap_single_words && !request.autowrap)
2835 char *src_text_ptr, *dst_text_ptr;
2837 text_door_style = checked_malloc(2 * strlen(text) + 1);
2839 src_text_ptr = text;
2840 dst_text_ptr = text_door_style;
2842 while (*src_text_ptr)
2844 if (*src_text_ptr == ' ' ||
2845 *src_text_ptr == '?' ||
2846 *src_text_ptr == '!')
2847 *dst_text_ptr++ = '\n';
2849 if (*src_text_ptr != ' ')
2850 *dst_text_ptr++ = *src_text_ptr;
2855 *dst_text_ptr = '\0';
2857 text_final = text_door_style;
2860 setRequestPosition(&sx, &sy, FALSE);
2862 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2864 for (y = 0; y < y_steps; y++)
2865 for (x = 0; x < x_steps; x++)
2866 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2867 x, y, x_steps, y_steps,
2868 tile_size, tile_size);
2870 /* force DOOR font inside door area */
2871 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2873 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2874 line_length, -1, max_lines, line_spacing, mask_mode,
2875 request.autowrap, request.centered, FALSE);
2879 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2880 RedrawGadget(tool_gadget[i]);
2882 // store readily prepared envelope request for later use when animating
2883 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2885 if (text_door_style)
2886 free(text_door_style);
2889 void AnimateEnvelopeRequest(int anim_mode, int action)
2891 int graphic = IMG_BACKGROUND_REQUEST;
2892 boolean draw_masked = graphic_info[graphic].draw_masked;
2893 int delay_value_normal = request.step_delay;
2894 int delay_value_fast = delay_value_normal / 2;
2895 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2896 boolean no_delay = (tape.warp_forward);
2897 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2898 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
2899 unsigned int anim_delay = 0;
2901 int tile_size = MAX(request.step_offset, 1);
2902 int max_xsize = request.width / tile_size;
2903 int max_ysize = request.height / tile_size;
2904 int max_xsize_inner = max_xsize - 2;
2905 int max_ysize_inner = max_ysize - 2;
2907 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2908 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2909 int xend = max_xsize_inner;
2910 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2911 int xstep = (xstart < xend ? 1 : 0);
2912 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2914 int end = MAX(xend - xstart, yend - ystart);
2917 if (setup.quick_doors)
2924 for (i = start; i <= end; i++)
2926 int last_frame = end; // last frame of this "for" loop
2927 int x = xstart + i * xstep;
2928 int y = ystart + i * ystep;
2929 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2930 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2931 int xsize_size_left = (xsize - 1) * tile_size;
2932 int ysize_size_top = (ysize - 1) * tile_size;
2933 int max_xsize_pos = (max_xsize - 1) * tile_size;
2934 int max_ysize_pos = (max_ysize - 1) * tile_size;
2935 int width = xsize * tile_size;
2936 int height = ysize * tile_size;
2941 setRequestPosition(&src_x, &src_y, FALSE);
2942 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2944 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2946 for (yy = 0; yy < 2; yy++)
2948 for (xx = 0; xx < 2; xx++)
2950 int src_xx = src_x + xx * max_xsize_pos;
2951 int src_yy = src_y + yy * max_ysize_pos;
2952 int dst_xx = dst_x + xx * xsize_size_left;
2953 int dst_yy = dst_y + yy * ysize_size_top;
2954 int xx_size = (xx ? tile_size : xsize_size_left);
2955 int yy_size = (yy ? tile_size : ysize_size_top);
2958 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
2959 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2961 BlitBitmap(bitmap_db_store_2, backbuffer,
2962 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2966 redraw_mask |= REDRAW_FIELD;
2970 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2974 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2976 int graphic = IMG_BACKGROUND_REQUEST;
2977 int sound_opening = SND_REQUEST_OPENING;
2978 int sound_closing = SND_REQUEST_CLOSING;
2979 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2980 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2981 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2982 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2983 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2985 if (game_status == GAME_MODE_PLAYING)
2986 BlitScreenToBitmap(backbuffer);
2988 SetDrawtoField(DRAW_TO_BACKBUFFER);
2990 // SetDrawBackgroundMask(REDRAW_NONE);
2992 if (action == ACTION_OPENING)
2994 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2996 if (req_state & REQ_ASK)
2998 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2999 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3001 else if (req_state & REQ_CONFIRM)
3003 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3005 else if (req_state & REQ_PLAYER)
3007 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3008 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3009 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3010 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3013 DrawEnvelopeRequest(text);
3016 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
3018 if (action == ACTION_OPENING)
3020 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3022 if (anim_mode == ANIM_DEFAULT)
3023 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3025 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3029 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3031 if (anim_mode != ANIM_NONE)
3032 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3034 if (anim_mode == ANIM_DEFAULT)
3035 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3038 game.envelope_active = FALSE;
3040 if (action == ACTION_CLOSING)
3041 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3043 // SetDrawBackgroundMask(last_draw_background_mask);
3045 redraw_mask |= REDRAW_FIELD;
3049 if (action == ACTION_CLOSING &&
3050 game_status == GAME_MODE_PLAYING &&
3051 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3052 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3055 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3057 if (IS_MM_WALL(element))
3059 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3065 int graphic = el2preimg(element);
3067 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3068 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3073 void DrawLevel(int draw_background_mask)
3077 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3078 SetDrawBackgroundMask(draw_background_mask);
3082 for (x = BX1; x <= BX2; x++)
3083 for (y = BY1; y <= BY2; y++)
3084 DrawScreenField(x, y);
3086 redraw_mask |= REDRAW_FIELD;
3089 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3094 for (x = 0; x < size_x; x++)
3095 for (y = 0; y < size_y; y++)
3096 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3098 redraw_mask |= REDRAW_FIELD;
3101 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3105 for (x = 0; x < size_x; x++)
3106 for (y = 0; y < size_y; y++)
3107 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3109 redraw_mask |= REDRAW_FIELD;
3112 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3114 boolean show_level_border = (BorderElement != EL_EMPTY);
3115 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3116 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3117 int tile_size = preview.tile_size;
3118 int preview_width = preview.xsize * tile_size;
3119 int preview_height = preview.ysize * tile_size;
3120 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3121 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3122 int real_preview_width = real_preview_xsize * tile_size;
3123 int real_preview_height = real_preview_ysize * tile_size;
3124 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3125 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3128 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3131 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3133 dst_x += (preview_width - real_preview_width) / 2;
3134 dst_y += (preview_height - real_preview_height) / 2;
3136 for (x = 0; x < real_preview_xsize; x++)
3138 for (y = 0; y < real_preview_ysize; y++)
3140 int lx = from_x + x + (show_level_border ? -1 : 0);
3141 int ly = from_y + y + (show_level_border ? -1 : 0);
3142 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3143 getBorderElement(lx, ly));
3145 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3146 element, tile_size);
3150 redraw_mask |= REDRAW_FIELD;
3153 #define MICROLABEL_EMPTY 0
3154 #define MICROLABEL_LEVEL_NAME 1
3155 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3156 #define MICROLABEL_LEVEL_AUTHOR 3
3157 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3158 #define MICROLABEL_IMPORTED_FROM 5
3159 #define MICROLABEL_IMPORTED_BY_HEAD 6
3160 #define MICROLABEL_IMPORTED_BY 7
3162 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3164 int max_text_width = SXSIZE;
3165 int font_width = getFontWidth(font_nr);
3167 if (pos->align == ALIGN_CENTER)
3168 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3169 else if (pos->align == ALIGN_RIGHT)
3170 max_text_width = pos->x;
3172 max_text_width = SXSIZE - pos->x;
3174 return max_text_width / font_width;
3177 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3179 char label_text[MAX_OUTPUT_LINESIZE + 1];
3180 int max_len_label_text;
3181 int font_nr = pos->font;
3184 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3187 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3188 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3189 mode == MICROLABEL_IMPORTED_BY_HEAD)
3190 font_nr = pos->font_alt;
3192 max_len_label_text = getMaxTextLength(pos, font_nr);
3194 if (pos->size != -1)
3195 max_len_label_text = pos->size;
3197 for (i = 0; i < max_len_label_text; i++)
3198 label_text[i] = ' ';
3199 label_text[max_len_label_text] = '\0';
3201 if (strlen(label_text) > 0)
3202 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3205 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3206 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3207 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3208 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3209 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3210 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3211 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3212 max_len_label_text);
3213 label_text[max_len_label_text] = '\0';
3215 if (strlen(label_text) > 0)
3216 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3218 redraw_mask |= REDRAW_FIELD;
3221 static void DrawPreviewLevelLabel(int mode)
3223 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3226 static void DrawPreviewLevelInfo(int mode)
3228 if (mode == MICROLABEL_LEVEL_NAME)
3229 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3230 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3231 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3234 static void DrawPreviewLevelExt(boolean restart)
3236 static unsigned int scroll_delay = 0;
3237 static unsigned int label_delay = 0;
3238 static int from_x, from_y, scroll_direction;
3239 static int label_state, label_counter;
3240 unsigned int scroll_delay_value = preview.step_delay;
3241 boolean show_level_border = (BorderElement != EL_EMPTY);
3242 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3243 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3250 if (preview.anim_mode == ANIM_CENTERED)
3252 if (level_xsize > preview.xsize)
3253 from_x = (level_xsize - preview.xsize) / 2;
3254 if (level_ysize > preview.ysize)
3255 from_y = (level_ysize - preview.ysize) / 2;
3258 from_x += preview.xoffset;
3259 from_y += preview.yoffset;
3261 scroll_direction = MV_RIGHT;
3265 DrawPreviewLevelPlayfield(from_x, from_y);
3266 DrawPreviewLevelLabel(label_state);
3268 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3269 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3271 /* initialize delay counters */
3272 DelayReached(&scroll_delay, 0);
3273 DelayReached(&label_delay, 0);
3275 if (leveldir_current->name)
3277 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3278 char label_text[MAX_OUTPUT_LINESIZE + 1];
3279 int font_nr = pos->font;
3280 int max_len_label_text = getMaxTextLength(pos, font_nr);
3282 if (pos->size != -1)
3283 max_len_label_text = pos->size;
3285 strncpy(label_text, leveldir_current->name, max_len_label_text);
3286 label_text[max_len_label_text] = '\0';
3288 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3289 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3295 /* scroll preview level, if needed */
3296 if (preview.anim_mode != ANIM_NONE &&
3297 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3298 DelayReached(&scroll_delay, scroll_delay_value))
3300 switch (scroll_direction)
3305 from_x -= preview.step_offset;
3306 from_x = (from_x < 0 ? 0 : from_x);
3309 scroll_direction = MV_UP;
3313 if (from_x < level_xsize - preview.xsize)
3315 from_x += preview.step_offset;
3316 from_x = (from_x > level_xsize - preview.xsize ?
3317 level_xsize - preview.xsize : from_x);
3320 scroll_direction = MV_DOWN;
3326 from_y -= preview.step_offset;
3327 from_y = (from_y < 0 ? 0 : from_y);
3330 scroll_direction = MV_RIGHT;
3334 if (from_y < level_ysize - preview.ysize)
3336 from_y += preview.step_offset;
3337 from_y = (from_y > level_ysize - preview.ysize ?
3338 level_ysize - preview.ysize : from_y);
3341 scroll_direction = MV_LEFT;
3348 DrawPreviewLevelPlayfield(from_x, from_y);
3351 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
3352 /* redraw micro level label, if needed */
3353 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3354 !strEqual(level.author, ANONYMOUS_NAME) &&
3355 !strEqual(level.author, leveldir_current->name) &&
3356 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3358 int max_label_counter = 23;
3360 if (leveldir_current->imported_from != NULL &&
3361 strlen(leveldir_current->imported_from) > 0)
3362 max_label_counter += 14;
3363 if (leveldir_current->imported_by != NULL &&
3364 strlen(leveldir_current->imported_by) > 0)
3365 max_label_counter += 14;
3367 label_counter = (label_counter + 1) % max_label_counter;
3368 label_state = (label_counter >= 0 && label_counter <= 7 ?
3369 MICROLABEL_LEVEL_NAME :
3370 label_counter >= 9 && label_counter <= 12 ?
3371 MICROLABEL_LEVEL_AUTHOR_HEAD :
3372 label_counter >= 14 && label_counter <= 21 ?
3373 MICROLABEL_LEVEL_AUTHOR :
3374 label_counter >= 23 && label_counter <= 26 ?
3375 MICROLABEL_IMPORTED_FROM_HEAD :
3376 label_counter >= 28 && label_counter <= 35 ?
3377 MICROLABEL_IMPORTED_FROM :
3378 label_counter >= 37 && label_counter <= 40 ?
3379 MICROLABEL_IMPORTED_BY_HEAD :
3380 label_counter >= 42 && label_counter <= 49 ?
3381 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3383 if (leveldir_current->imported_from == NULL &&
3384 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3385 label_state == MICROLABEL_IMPORTED_FROM))
3386 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3387 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3389 DrawPreviewLevelLabel(label_state);
3393 void DrawPreviewLevelInitial()
3395 DrawPreviewLevelExt(TRUE);
3398 void DrawPreviewLevelAnimation()
3400 DrawPreviewLevelExt(FALSE);
3403 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3404 int graphic, int sync_frame,
3407 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3409 if (mask_mode == USE_MASKING)
3410 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3412 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3415 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3416 int graphic, int sync_frame, int mask_mode)
3418 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3420 if (mask_mode == USE_MASKING)
3421 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3423 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3426 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3428 int lx = LEVELX(x), ly = LEVELY(y);
3430 if (!IN_SCR_FIELD(x, y))
3433 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3434 graphic, GfxFrame[lx][ly], NO_MASKING);
3436 MarkTileDirty(x, y);
3439 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3441 int lx = LEVELX(x), ly = LEVELY(y);
3443 if (!IN_SCR_FIELD(x, y))
3446 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3447 graphic, GfxFrame[lx][ly], NO_MASKING);
3448 MarkTileDirty(x, y);
3451 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3453 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3456 void DrawLevelElementAnimation(int x, int y, int element)
3458 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3460 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3463 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3465 int sx = SCREENX(x), sy = SCREENY(y);
3467 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3470 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3473 DrawGraphicAnimation(sx, sy, graphic);
3476 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3477 DrawLevelFieldCrumbled(x, y);
3479 if (GFX_CRUMBLED(Feld[x][y]))
3480 DrawLevelFieldCrumbled(x, y);
3484 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3486 int sx = SCREENX(x), sy = SCREENY(y);
3489 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3492 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3494 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3497 DrawGraphicAnimation(sx, sy, graphic);
3499 if (GFX_CRUMBLED(element))
3500 DrawLevelFieldCrumbled(x, y);
3503 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3505 if (player->use_murphy)
3507 /* this works only because currently only one player can be "murphy" ... */
3508 static int last_horizontal_dir = MV_LEFT;
3509 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3511 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3512 last_horizontal_dir = move_dir;
3514 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3516 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3518 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3524 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3527 static boolean equalGraphics(int graphic1, int graphic2)
3529 struct GraphicInfo *g1 = &graphic_info[graphic1];
3530 struct GraphicInfo *g2 = &graphic_info[graphic2];
3532 return (g1->bitmap == g2->bitmap &&
3533 g1->src_x == g2->src_x &&
3534 g1->src_y == g2->src_y &&
3535 g1->anim_frames == g2->anim_frames &&
3536 g1->anim_delay == g2->anim_delay &&
3537 g1->anim_mode == g2->anim_mode);
3540 void DrawAllPlayers()
3544 for (i = 0; i < MAX_PLAYERS; i++)
3545 if (stored_player[i].active)
3546 DrawPlayer(&stored_player[i]);
3549 void DrawPlayerField(int x, int y)
3551 if (!IS_PLAYER(x, y))
3554 DrawPlayer(PLAYERINFO(x, y));
3557 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3559 void DrawPlayer(struct PlayerInfo *player)
3561 int jx = player->jx;
3562 int jy = player->jy;
3563 int move_dir = player->MovDir;
3564 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3565 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3566 int last_jx = (player->is_moving ? jx - dx : jx);
3567 int last_jy = (player->is_moving ? jy - dy : jy);
3568 int next_jx = jx + dx;
3569 int next_jy = jy + dy;
3570 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3571 boolean player_is_opaque = FALSE;
3572 int sx = SCREENX(jx), sy = SCREENY(jy);
3573 int sxx = 0, syy = 0;
3574 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3576 int action = ACTION_DEFAULT;
3577 int last_player_graphic = getPlayerGraphic(player, move_dir);
3578 int last_player_frame = player->Frame;
3581 /* GfxElement[][] is set to the element the player is digging or collecting;
3582 remove also for off-screen player if the player is not moving anymore */
3583 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3584 GfxElement[jx][jy] = EL_UNDEFINED;
3586 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3590 if (!IN_LEV_FIELD(jx, jy))
3592 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3593 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3594 printf("DrawPlayerField(): This should never happen!\n");
3599 if (element == EL_EXPLOSION)
3602 action = (player->is_pushing ? ACTION_PUSHING :
3603 player->is_digging ? ACTION_DIGGING :
3604 player->is_collecting ? ACTION_COLLECTING :
3605 player->is_moving ? ACTION_MOVING :
3606 player->is_snapping ? ACTION_SNAPPING :
3607 player->is_dropping ? ACTION_DROPPING :
3608 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3610 if (player->is_waiting)
3611 move_dir = player->dir_waiting;
3613 InitPlayerGfxAnimation(player, action, move_dir);
3615 /* ----------------------------------------------------------------------- */
3616 /* draw things in the field the player is leaving, if needed */
3617 /* ----------------------------------------------------------------------- */
3619 if (player->is_moving)
3621 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3623 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3625 if (last_element == EL_DYNAMITE_ACTIVE ||
3626 last_element == EL_EM_DYNAMITE_ACTIVE ||
3627 last_element == EL_SP_DISK_RED_ACTIVE)
3628 DrawDynamite(last_jx, last_jy);
3630 DrawLevelFieldThruMask(last_jx, last_jy);
3632 else if (last_element == EL_DYNAMITE_ACTIVE ||
3633 last_element == EL_EM_DYNAMITE_ACTIVE ||
3634 last_element == EL_SP_DISK_RED_ACTIVE)
3635 DrawDynamite(last_jx, last_jy);
3637 /* !!! this is not enough to prevent flickering of players which are
3638 moving next to each others without a free tile between them -- this
3639 can only be solved by drawing all players layer by layer (first the
3640 background, then the foreground etc.) !!! => TODO */
3641 else if (!IS_PLAYER(last_jx, last_jy))
3642 DrawLevelField(last_jx, last_jy);
3645 DrawLevelField(last_jx, last_jy);
3648 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3649 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3652 if (!IN_SCR_FIELD(sx, sy))
3655 /* ----------------------------------------------------------------------- */
3656 /* draw things behind the player, if needed */
3657 /* ----------------------------------------------------------------------- */
3660 DrawLevelElement(jx, jy, Back[jx][jy]);
3661 else if (IS_ACTIVE_BOMB(element))
3662 DrawLevelElement(jx, jy, EL_EMPTY);
3665 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3667 int old_element = GfxElement[jx][jy];
3668 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3669 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3671 if (GFX_CRUMBLED(old_element))
3672 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3674 DrawGraphic(sx, sy, old_graphic, frame);
3676 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3677 player_is_opaque = TRUE;
3681 GfxElement[jx][jy] = EL_UNDEFINED;
3683 /* make sure that pushed elements are drawn with correct frame rate */
3684 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3686 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3687 GfxFrame[jx][jy] = player->StepFrame;
3689 DrawLevelField(jx, jy);
3693 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3694 /* ----------------------------------------------------------------------- */
3695 /* draw player himself */
3696 /* ----------------------------------------------------------------------- */
3698 graphic = getPlayerGraphic(player, move_dir);
3700 /* in the case of changed player action or direction, prevent the current
3701 animation frame from being restarted for identical animations */
3702 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3703 player->Frame = last_player_frame;
3705 frame = getGraphicAnimationFrame(graphic, player->Frame);
3709 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3710 sxx = player->GfxPos;
3712 syy = player->GfxPos;
3715 if (player_is_opaque)
3716 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3718 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3720 if (SHIELD_ON(player))
3722 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3723 IMG_SHIELD_NORMAL_ACTIVE);
3724 int frame = getGraphicAnimationFrame(graphic, -1);
3726 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3730 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3733 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3734 sxx = player->GfxPos;
3736 syy = player->GfxPos;
3740 /* ----------------------------------------------------------------------- */
3741 /* draw things the player is pushing, if needed */
3742 /* ----------------------------------------------------------------------- */
3744 if (player->is_pushing && player->is_moving)
3746 int px = SCREENX(jx), py = SCREENY(jy);
3747 int pxx = (TILEX - ABS(sxx)) * dx;
3748 int pyy = (TILEY - ABS(syy)) * dy;
3749 int gfx_frame = GfxFrame[jx][jy];
3755 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3757 element = Feld[next_jx][next_jy];
3758 gfx_frame = GfxFrame[next_jx][next_jy];
3761 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3763 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3764 frame = getGraphicAnimationFrame(graphic, sync_frame);
3766 /* draw background element under pushed element (like the Sokoban field) */
3767 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3769 /* this allows transparent pushing animation over non-black background */
3772 DrawLevelElement(jx, jy, Back[jx][jy]);
3774 DrawLevelElement(jx, jy, EL_EMPTY);
3776 if (Back[next_jx][next_jy])
3777 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3779 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3781 else if (Back[next_jx][next_jy])
3782 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3785 /* do not draw (EM style) pushing animation when pushing is finished */
3786 /* (two-tile animations usually do not contain start and end frame) */
3787 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3788 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3790 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3792 /* masked drawing is needed for EMC style (double) movement graphics */
3793 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3794 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3798 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3799 /* ----------------------------------------------------------------------- */
3800 /* draw player himself */
3801 /* ----------------------------------------------------------------------- */
3803 graphic = getPlayerGraphic(player, move_dir);
3805 /* in the case of changed player action or direction, prevent the current
3806 animation frame from being restarted for identical animations */
3807 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3808 player->Frame = last_player_frame;
3810 frame = getGraphicAnimationFrame(graphic, player->Frame);
3814 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3815 sxx = player->GfxPos;
3817 syy = player->GfxPos;
3820 if (player_is_opaque)
3821 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3823 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3825 if (SHIELD_ON(player))
3827 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3828 IMG_SHIELD_NORMAL_ACTIVE);
3829 int frame = getGraphicAnimationFrame(graphic, -1);
3831 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3835 /* ----------------------------------------------------------------------- */
3836 /* draw things in front of player (active dynamite or dynabombs) */
3837 /* ----------------------------------------------------------------------- */
3839 if (IS_ACTIVE_BOMB(element))
3841 graphic = el2img(element);
3842 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3844 if (game.emulation == EMU_SUPAPLEX)
3845 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3847 DrawGraphicThruMask(sx, sy, graphic, frame);
3850 if (player_is_moving && last_element == EL_EXPLOSION)
3852 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3853 GfxElement[last_jx][last_jy] : EL_EMPTY);
3854 int graphic = el_act2img(element, ACTION_EXPLODING);
3855 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3856 int phase = ExplodePhase[last_jx][last_jy] - 1;
3857 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3860 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3863 /* ----------------------------------------------------------------------- */
3864 /* draw elements the player is just walking/passing through/under */
3865 /* ----------------------------------------------------------------------- */
3867 if (player_is_moving)
3869 /* handle the field the player is leaving ... */
3870 if (IS_ACCESSIBLE_INSIDE(last_element))
3871 DrawLevelField(last_jx, last_jy);
3872 else if (IS_ACCESSIBLE_UNDER(last_element))
3873 DrawLevelFieldThruMask(last_jx, last_jy);
3876 /* do not redraw accessible elements if the player is just pushing them */
3877 if (!player_is_moving || !player->is_pushing)
3879 /* ... and the field the player is entering */
3880 if (IS_ACCESSIBLE_INSIDE(element))
3881 DrawLevelField(jx, jy);
3882 else if (IS_ACCESSIBLE_UNDER(element))
3883 DrawLevelFieldThruMask(jx, jy);
3886 MarkTileDirty(sx, sy);
3889 /* ------------------------------------------------------------------------- */
3891 void WaitForEventToContinue()
3893 boolean still_wait = TRUE;
3895 if (program.headless)
3898 /* simulate releasing mouse button over last gadget, if still pressed */
3900 HandleGadgets(-1, -1, 0);
3902 button_status = MB_RELEASED;
3910 if (NextValidEvent(&event))
3914 case EVENT_BUTTONPRESS:
3915 case EVENT_KEYPRESS:
3916 #if defined(TARGET_SDL2)
3917 case SDL_CONTROLLERBUTTONDOWN:
3919 case SDL_JOYBUTTONDOWN:
3923 case EVENT_KEYRELEASE:
3924 ClearPlayerAction();
3928 HandleOtherEvents(&event);
3932 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3941 #define MAX_REQUEST_LINES 13
3942 #define MAX_REQUEST_LINE_FONT1_LEN 7
3943 #define MAX_REQUEST_LINE_FONT2_LEN 10
3945 static int RequestHandleEvents(unsigned int req_state)
3947 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3948 local_player->LevelSolved_GameEnd);
3949 int width = request.width;
3950 int height = request.height;
3954 setRequestPosition(&sx, &sy, FALSE);
3956 button_status = MB_RELEASED;
3958 request_gadget_id = -1;
3965 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3967 HandleGameActions();
3969 SetDrawtoField(DRAW_TO_BACKBUFFER);
3971 if (global.use_envelope_request)
3973 /* copy current state of request area to middle of playfield area */
3974 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
3982 while (NextValidEvent(&event))
3986 case EVENT_BUTTONPRESS:
3987 case EVENT_BUTTONRELEASE:
3988 case EVENT_MOTIONNOTIFY:
3992 if (event.type == EVENT_MOTIONNOTIFY)
3997 motion_status = TRUE;
3998 mx = ((MotionEvent *) &event)->x;
3999 my = ((MotionEvent *) &event)->y;
4003 motion_status = FALSE;
4004 mx = ((ButtonEvent *) &event)->x;
4005 my = ((ButtonEvent *) &event)->y;
4006 if (event.type == EVENT_BUTTONPRESS)
4007 button_status = ((ButtonEvent *) &event)->button;
4009 button_status = MB_RELEASED;
4012 /* this sets 'request_gadget_id' */
4013 HandleGadgets(mx, my, button_status);
4015 switch (request_gadget_id)
4017 case TOOL_CTRL_ID_YES:
4020 case TOOL_CTRL_ID_NO:
4023 case TOOL_CTRL_ID_CONFIRM:
4024 result = TRUE | FALSE;
4027 case TOOL_CTRL_ID_PLAYER_1:
4030 case TOOL_CTRL_ID_PLAYER_2:
4033 case TOOL_CTRL_ID_PLAYER_3:
4036 case TOOL_CTRL_ID_PLAYER_4:
4047 #if defined(TARGET_SDL2)
4048 case SDL_WINDOWEVENT:
4049 HandleWindowEvent((WindowEvent *) &event);
4052 case SDL_APP_WILLENTERBACKGROUND:
4053 case SDL_APP_DIDENTERBACKGROUND:
4054 case SDL_APP_WILLENTERFOREGROUND:
4055 case SDL_APP_DIDENTERFOREGROUND:
4056 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4060 case EVENT_KEYPRESS:
4062 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4067 if (req_state & REQ_CONFIRM)
4072 #if defined(TARGET_SDL2)
4075 #if defined(KSYM_Rewind)
4076 case KSYM_Rewind: /* for Amazon Fire TV remote */
4083 #if defined(TARGET_SDL2)
4085 #if defined(KSYM_FastForward)
4086 case KSYM_FastForward: /* for Amazon Fire TV remote */
4093 HandleKeysDebug(key);
4097 if (req_state & REQ_PLAYER)
4103 case EVENT_KEYRELEASE:
4104 ClearPlayerAction();
4107 #if defined(TARGET_SDL2)
4108 case SDL_CONTROLLERBUTTONDOWN:
4109 switch (event.cbutton.button)
4111 case SDL_CONTROLLER_BUTTON_A:
4112 case SDL_CONTROLLER_BUTTON_X:
4113 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4117 case SDL_CONTROLLER_BUTTON_B:
4118 case SDL_CONTROLLER_BUTTON_Y:
4119 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4120 case SDL_CONTROLLER_BUTTON_BACK:
4125 if (req_state & REQ_PLAYER)
4130 case SDL_CONTROLLERBUTTONUP:
4131 HandleJoystickEvent(&event);
4132 ClearPlayerAction();
4137 HandleOtherEvents(&event);
4142 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4144 int joy = AnyJoystick();
4146 if (joy & JOY_BUTTON_1)
4148 else if (joy & JOY_BUTTON_2)
4154 if (global.use_envelope_request)
4156 /* copy back current state of pressed buttons inside request area */
4157 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4167 static boolean RequestDoor(char *text, unsigned int req_state)
4169 unsigned int old_door_state;
4170 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4171 int font_nr = FONT_TEXT_2;
4176 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4178 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4179 font_nr = FONT_TEXT_1;
4182 if (game_status == GAME_MODE_PLAYING)
4183 BlitScreenToBitmap(backbuffer);
4185 /* disable deactivated drawing when quick-loading level tape recording */
4186 if (tape.playing && tape.deactivate_display)
4187 TapeDeactivateDisplayOff(TRUE);
4189 SetMouseCursor(CURSOR_DEFAULT);
4191 #if defined(NETWORK_AVALIABLE)
4192 /* pause network game while waiting for request to answer */
4193 if (options.network &&
4194 game_status == GAME_MODE_PLAYING &&
4195 req_state & REQUEST_WAIT_FOR_INPUT)
4196 SendToServer_PausePlaying();
4199 old_door_state = GetDoorState();
4201 /* simulate releasing mouse button over last gadget, if still pressed */
4203 HandleGadgets(-1, -1, 0);
4207 /* draw released gadget before proceeding */
4210 if (old_door_state & DOOR_OPEN_1)
4212 CloseDoor(DOOR_CLOSE_1);
4214 /* save old door content */
4215 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4216 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4219 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4220 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4222 /* clear door drawing field */
4223 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4225 /* force DOOR font inside door area */
4226 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4228 /* write text for request */
4229 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4231 char text_line[max_request_line_len + 1];
4237 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4239 tc = *(text_ptr + tx);
4240 // if (!tc || tc == ' ')
4241 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4245 if ((tc == '?' || tc == '!') && tl == 0)
4255 strncpy(text_line, text_ptr, tl);
4258 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4259 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4260 text_line, font_nr);
4262 text_ptr += tl + (tc == ' ' ? 1 : 0);
4263 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4268 if (req_state & REQ_ASK)
4270 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4271 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4273 else if (req_state & REQ_CONFIRM)
4275 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4277 else if (req_state & REQ_PLAYER)
4279 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4280 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4281 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4282 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4285 /* copy request gadgets to door backbuffer */
4286 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4288 OpenDoor(DOOR_OPEN_1);
4290 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4292 if (game_status == GAME_MODE_PLAYING)
4294 SetPanelBackground();
4295 SetDrawBackgroundMask(REDRAW_DOOR_1);
4299 SetDrawBackgroundMask(REDRAW_FIELD);
4305 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4307 // ---------- handle request buttons ----------
4308 result = RequestHandleEvents(req_state);
4312 if (!(req_state & REQ_STAY_OPEN))
4314 CloseDoor(DOOR_CLOSE_1);
4316 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4317 (req_state & REQ_REOPEN))
4318 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4323 if (game_status == GAME_MODE_PLAYING)
4325 SetPanelBackground();
4326 SetDrawBackgroundMask(REDRAW_DOOR_1);
4330 SetDrawBackgroundMask(REDRAW_FIELD);
4333 #if defined(NETWORK_AVALIABLE)
4334 /* continue network game after request */
4335 if (options.network &&
4336 game_status == GAME_MODE_PLAYING &&
4337 req_state & REQUEST_WAIT_FOR_INPUT)
4338 SendToServer_ContinuePlaying();
4341 /* restore deactivated drawing when quick-loading level tape recording */
4342 if (tape.playing && tape.deactivate_display)
4343 TapeDeactivateDisplayOn();
4348 static boolean RequestEnvelope(char *text, unsigned int req_state)
4352 if (game_status == GAME_MODE_PLAYING)
4353 BlitScreenToBitmap(backbuffer);
4355 /* disable deactivated drawing when quick-loading level tape recording */
4356 if (tape.playing && tape.deactivate_display)
4357 TapeDeactivateDisplayOff(TRUE);
4359 SetMouseCursor(CURSOR_DEFAULT);
4361 #if defined(NETWORK_AVALIABLE)
4362 /* pause network game while waiting for request to answer */
4363 if (options.network &&
4364 game_status == GAME_MODE_PLAYING &&
4365 req_state & REQUEST_WAIT_FOR_INPUT)
4366 SendToServer_PausePlaying();
4369 /* simulate releasing mouse button over last gadget, if still pressed */
4371 HandleGadgets(-1, -1, 0);
4375 // (replace with setting corresponding request background)
4376 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4377 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4379 /* clear door drawing field */
4380 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4382 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4384 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4386 if (game_status == GAME_MODE_PLAYING)
4388 SetPanelBackground();
4389 SetDrawBackgroundMask(REDRAW_DOOR_1);
4393 SetDrawBackgroundMask(REDRAW_FIELD);
4399 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4401 // ---------- handle request buttons ----------
4402 result = RequestHandleEvents(req_state);
4406 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4410 if (game_status == GAME_MODE_PLAYING)
4412 SetPanelBackground();
4413 SetDrawBackgroundMask(REDRAW_DOOR_1);
4417 SetDrawBackgroundMask(REDRAW_FIELD);
4420 #if defined(NETWORK_AVALIABLE)
4421 /* continue network game after request */
4422 if (options.network &&
4423 game_status == GAME_MODE_PLAYING &&
4424 req_state & REQUEST_WAIT_FOR_INPUT)
4425 SendToServer_ContinuePlaying();
4428 /* restore deactivated drawing when quick-loading level tape recording */
4429 if (tape.playing && tape.deactivate_display)
4430 TapeDeactivateDisplayOn();
4435 boolean Request(char *text, unsigned int req_state)
4437 boolean overlay_active = GetOverlayActive();
4440 SetOverlayActive(FALSE);
4442 if (global.use_envelope_request)
4443 result = RequestEnvelope(text, req_state);
4445 result = RequestDoor(text, req_state);
4447 SetOverlayActive(overlay_active);
4452 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4454 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4455 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4458 if (dpo1->sort_priority != dpo2->sort_priority)
4459 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4461 compare_result = dpo1->nr - dpo2->nr;
4463 return compare_result;
4466 void InitGraphicCompatibilityInfo_Doors()
4472 struct DoorInfo *door;
4476 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4477 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4479 { -1, -1, -1, NULL }
4481 struct Rect door_rect_list[] =
4483 { DX, DY, DXSIZE, DYSIZE },
4484 { VX, VY, VXSIZE, VYSIZE }
4488 for (i = 0; doors[i].door_token != -1; i++)
4490 int door_token = doors[i].door_token;
4491 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4492 int part_1 = doors[i].part_1;
4493 int part_8 = doors[i].part_8;
4494 int part_2 = part_1 + 1;
4495 int part_3 = part_1 + 2;
4496 struct DoorInfo *door = doors[i].door;
4497 struct Rect *door_rect = &door_rect_list[door_index];
4498 boolean door_gfx_redefined = FALSE;
4500 /* check if any door part graphic definitions have been redefined */
4502 for (j = 0; door_part_controls[j].door_token != -1; j++)
4504 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4505 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4507 if (dpc->door_token == door_token && fi->redefined)
4508 door_gfx_redefined = TRUE;
4511 /* check for old-style door graphic/animation modifications */
4513 if (!door_gfx_redefined)
4515 if (door->anim_mode & ANIM_STATIC_PANEL)
4517 door->panel.step_xoffset = 0;
4518 door->panel.step_yoffset = 0;
4521 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4523 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4524 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4525 int num_door_steps, num_panel_steps;
4527 /* remove door part graphics other than the two default wings */
4529 for (j = 0; door_part_controls[j].door_token != -1; j++)
4531 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4532 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4534 if (dpc->graphic >= part_3 &&
4535 dpc->graphic <= part_8)
4539 /* set graphics and screen positions of the default wings */
4541 g_part_1->width = door_rect->width;
4542 g_part_1->height = door_rect->height;
4543 g_part_2->width = door_rect->width;
4544 g_part_2->height = door_rect->height;
4545 g_part_2->src_x = door_rect->width;
4546 g_part_2->src_y = g_part_1->src_y;
4548 door->part_2.x = door->part_1.x;
4549 door->part_2.y = door->part_1.y;
4551 if (door->width != -1)
4553 g_part_1->width = door->width;
4554 g_part_2->width = door->width;
4556 // special treatment for graphics and screen position of right wing
4557 g_part_2->src_x += door_rect->width - door->width;
4558 door->part_2.x += door_rect->width - door->width;
4561 if (door->height != -1)
4563 g_part_1->height = door->height;
4564 g_part_2->height = door->height;
4566 // special treatment for graphics and screen position of bottom wing
4567 g_part_2->src_y += door_rect->height - door->height;
4568 door->part_2.y += door_rect->height - door->height;
4571 /* set animation delays for the default wings and panels */
4573 door->part_1.step_delay = door->step_delay;
4574 door->part_2.step_delay = door->step_delay;
4575 door->panel.step_delay = door->step_delay;
4577 /* set animation draw order for the default wings */
4579 door->part_1.sort_priority = 2; /* draw left wing over ... */
4580 door->part_2.sort_priority = 1; /* ... right wing */
4582 /* set animation draw offset for the default wings */
4584 if (door->anim_mode & ANIM_HORIZONTAL)
4586 door->part_1.step_xoffset = door->step_offset;
4587 door->part_1.step_yoffset = 0;
4588 door->part_2.step_xoffset = door->step_offset * -1;
4589 door->part_2.step_yoffset = 0;
4591 num_door_steps = g_part_1->width / door->step_offset;
4593 else // ANIM_VERTICAL
4595 door->part_1.step_xoffset = 0;
4596 door->part_1.step_yoffset = door->step_offset;
4597 door->part_2.step_xoffset = 0;
4598 door->part_2.step_yoffset = door->step_offset * -1;
4600 num_door_steps = g_part_1->height / door->step_offset;
4603 /* set animation draw offset for the default panels */
4605 if (door->step_offset > 1)
4607 num_panel_steps = 2 * door_rect->height / door->step_offset;
4608 door->panel.start_step = num_panel_steps - num_door_steps;
4609 door->panel.start_step_closing = door->panel.start_step;
4613 num_panel_steps = door_rect->height / door->step_offset;
4614 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4615 door->panel.start_step_closing = door->panel.start_step;
4616 door->panel.step_delay *= 2;
4627 for (i = 0; door_part_controls[i].door_token != -1; i++)
4629 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4630 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4632 /* initialize "start_step_opening" and "start_step_closing", if needed */
4633 if (dpc->pos->start_step_opening == 0 &&
4634 dpc->pos->start_step_closing == 0)
4636 // dpc->pos->start_step_opening = dpc->pos->start_step;
4637 dpc->pos->start_step_closing = dpc->pos->start_step;
4640 /* fill structure for door part draw order (sorted below) */
4642 dpo->sort_priority = dpc->pos->sort_priority;
4645 /* sort door part controls according to sort_priority and graphic number */
4646 qsort(door_part_order, MAX_DOOR_PARTS,
4647 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4650 unsigned int OpenDoor(unsigned int door_state)
4652 if (door_state & DOOR_COPY_BACK)
4654 if (door_state & DOOR_OPEN_1)
4655 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4656 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4658 if (door_state & DOOR_OPEN_2)
4659 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4660 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4662 door_state &= ~DOOR_COPY_BACK;
4665 return MoveDoor(door_state);
4668 unsigned int CloseDoor(unsigned int door_state)
4670 unsigned int old_door_state = GetDoorState();
4672 if (!(door_state & DOOR_NO_COPY_BACK))
4674 if (old_door_state & DOOR_OPEN_1)
4675 BlitBitmap(backbuffer, bitmap_db_door_1,
4676 DX, DY, DXSIZE, DYSIZE, 0, 0);
4678 if (old_door_state & DOOR_OPEN_2)
4679 BlitBitmap(backbuffer, bitmap_db_door_2,
4680 VX, VY, VXSIZE, VYSIZE, 0, 0);
4682 door_state &= ~DOOR_NO_COPY_BACK;
4685 return MoveDoor(door_state);
4688 unsigned int GetDoorState()
4690 return MoveDoor(DOOR_GET_STATE);
4693 unsigned int SetDoorState(unsigned int door_state)
4695 return MoveDoor(door_state | DOOR_SET_STATE);
4698 int euclid(int a, int b)
4700 return (b ? euclid(b, a % b) : a);
4703 unsigned int MoveDoor(unsigned int door_state)
4705 struct Rect door_rect_list[] =
4707 { DX, DY, DXSIZE, DYSIZE },
4708 { VX, VY, VXSIZE, VYSIZE }
4710 static int door1 = DOOR_CLOSE_1;
4711 static int door2 = DOOR_CLOSE_2;
4712 unsigned int door_delay = 0;
4713 unsigned int door_delay_value;
4716 if (door_state == DOOR_GET_STATE)
4717 return (door1 | door2);
4719 if (door_state & DOOR_SET_STATE)
4721 if (door_state & DOOR_ACTION_1)
4722 door1 = door_state & DOOR_ACTION_1;
4723 if (door_state & DOOR_ACTION_2)
4724 door2 = door_state & DOOR_ACTION_2;
4726 return (door1 | door2);
4729 if (!(door_state & DOOR_FORCE_REDRAW))
4731 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4732 door_state &= ~DOOR_OPEN_1;
4733 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4734 door_state &= ~DOOR_CLOSE_1;
4735 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4736 door_state &= ~DOOR_OPEN_2;
4737 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4738 door_state &= ~DOOR_CLOSE_2;
4741 if (global.autoplay_leveldir)
4743 door_state |= DOOR_NO_DELAY;
4744 door_state &= ~DOOR_CLOSE_ALL;
4747 if (game_status == GAME_MODE_EDITOR)
4748 door_state |= DOOR_NO_DELAY;
4750 if (door_state & DOOR_ACTION)
4752 boolean door_panel_drawn[NUM_DOORS];
4753 boolean panel_has_doors[NUM_DOORS];
4754 boolean door_part_skip[MAX_DOOR_PARTS];
4755 boolean door_part_done[MAX_DOOR_PARTS];
4756 boolean door_part_done_all;
4757 int num_steps[MAX_DOOR_PARTS];
4758 int max_move_delay = 0; // delay for complete animations of all doors
4759 int max_step_delay = 0; // delay (ms) between two animation frames
4760 int num_move_steps = 0; // number of animation steps for all doors
4761 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4762 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4763 int current_move_delay = 0;
4767 for (i = 0; i < NUM_DOORS; i++)
4768 panel_has_doors[i] = FALSE;
4770 for (i = 0; i < MAX_DOOR_PARTS; i++)
4772 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4773 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4774 int door_token = dpc->door_token;
4776 door_part_done[i] = FALSE;
4777 door_part_skip[i] = (!(door_state & door_token) ||
4781 for (i = 0; i < MAX_DOOR_PARTS; i++)
4783 int nr = door_part_order[i].nr;
4784 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4785 struct DoorPartPosInfo *pos = dpc->pos;
4786 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4787 int door_token = dpc->door_token;
4788 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4789 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4790 int step_xoffset = ABS(pos->step_xoffset);
4791 int step_yoffset = ABS(pos->step_yoffset);
4792 int step_delay = pos->step_delay;
4793 int current_door_state = door_state & door_token;
4794 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4795 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4796 boolean part_opening = (is_panel ? door_closing : door_opening);
4797 int start_step = (part_opening ? pos->start_step_opening :
4798 pos->start_step_closing);
4799 float move_xsize = (step_xoffset ? g->width : 0);
4800 float move_ysize = (step_yoffset ? g->height : 0);
4801 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4802 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4803 int move_steps = (move_xsteps && move_ysteps ?
4804 MIN(move_xsteps, move_ysteps) :
4805 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4806 int move_delay = move_steps * step_delay;
4808 if (door_part_skip[nr])
4811 max_move_delay = MAX(max_move_delay, move_delay);
4812 max_step_delay = (max_step_delay == 0 ? step_delay :
4813 euclid(max_step_delay, step_delay));
4814 num_steps[nr] = move_steps;
4818 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4820 panel_has_doors[door_index] = TRUE;
4824 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4826 num_move_steps = max_move_delay / max_step_delay;
4827 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4829 door_delay_value = max_step_delay;
4831 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4833 start = num_move_steps - 1;
4837 /* opening door sound has priority over simultaneously closing door */
4838 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4839 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4840 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4841 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4844 for (k = start; k < num_move_steps; k++)
4846 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4848 door_part_done_all = TRUE;
4850 for (i = 0; i < NUM_DOORS; i++)
4851 door_panel_drawn[i] = FALSE;
4853 for (i = 0; i < MAX_DOOR_PARTS; i++)
4855 int nr = door_part_order[i].nr;
4856 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4857 struct DoorPartPosInfo *pos = dpc->pos;
4858 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4859 int door_token = dpc->door_token;
4860 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4861 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4862 boolean is_panel_and_door_has_closed = FALSE;
4863 struct Rect *door_rect = &door_rect_list[door_index];
4864 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4866 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4867 int current_door_state = door_state & door_token;
4868 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4869 boolean door_closing = !door_opening;
4870 boolean part_opening = (is_panel ? door_closing : door_opening);
4871 boolean part_closing = !part_opening;
4872 int start_step = (part_opening ? pos->start_step_opening :
4873 pos->start_step_closing);
4874 int step_delay = pos->step_delay;
4875 int step_factor = step_delay / max_step_delay;
4876 int k1 = (step_factor ? k / step_factor + 1 : k);
4877 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4878 int kk = MAX(0, k2);
4881 int src_x, src_y, src_xx, src_yy;
4882 int dst_x, dst_y, dst_xx, dst_yy;
4885 if (door_part_skip[nr])
4888 if (!(door_state & door_token))
4896 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4897 int kk_door = MAX(0, k2_door);
4898 int sync_frame = kk_door * door_delay_value;
4899 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4901 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
4902 &g_src_x, &g_src_y);
4907 if (!door_panel_drawn[door_index])
4909 ClearRectangle(drawto, door_rect->x, door_rect->y,
4910 door_rect->width, door_rect->height);
4912 door_panel_drawn[door_index] = TRUE;
4915 // draw opening or closing door parts
4917 if (pos->step_xoffset < 0) // door part on right side
4920 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4923 if (dst_xx + width > door_rect->width)
4924 width = door_rect->width - dst_xx;
4926 else // door part on left side
4929 dst_xx = pos->x - kk * pos->step_xoffset;
4933 src_xx = ABS(dst_xx);
4937 width = g->width - src_xx;
4939 if (width > door_rect->width)
4940 width = door_rect->width;
4942 // printf("::: k == %d [%d] \n", k, start_step);
4945 if (pos->step_yoffset < 0) // door part on bottom side
4948 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4951 if (dst_yy + height > door_rect->height)
4952 height = door_rect->height - dst_yy;
4954 else // door part on top side
4957 dst_yy = pos->y - kk * pos->step_yoffset;
4961 src_yy = ABS(dst_yy);
4965 height = g->height - src_yy;
4968 src_x = g_src_x + src_xx;
4969 src_y = g_src_y + src_yy;
4971 dst_x = door_rect->x + dst_xx;
4972 dst_y = door_rect->y + dst_yy;
4974 is_panel_and_door_has_closed =
4977 panel_has_doors[door_index] &&
4978 k >= num_move_steps_doors_only - 1);
4980 if (width >= 0 && width <= g->width &&
4981 height >= 0 && height <= g->height &&
4982 !is_panel_and_door_has_closed)
4984 if (is_panel || !pos->draw_masked)
4985 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4988 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4992 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4994 if ((part_opening && (width < 0 || height < 0)) ||
4995 (part_closing && (width >= g->width && height >= g->height)))
4996 door_part_done[nr] = TRUE;
4998 // continue door part animations, but not panel after door has closed
4999 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5000 door_part_done_all = FALSE;
5003 if (!(door_state & DOOR_NO_DELAY))
5007 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5009 current_move_delay += max_step_delay;
5011 /* prevent OS (Windows) from complaining about program not responding */
5015 if (door_part_done_all)
5020 if (door_state & DOOR_ACTION_1)
5021 door1 = door_state & DOOR_ACTION_1;
5022 if (door_state & DOOR_ACTION_2)
5023 door2 = door_state & DOOR_ACTION_2;
5025 // draw masked border over door area
5026 DrawMaskedBorder(REDRAW_DOOR_1);
5027 DrawMaskedBorder(REDRAW_DOOR_2);
5029 return (door1 | door2);
5032 static boolean useSpecialEditorDoor()
5034 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5035 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5037 // do not draw special editor door if editor border defined or redefined
5038 if (graphic_info[graphic].bitmap != NULL || redefined)
5041 // do not draw special editor door if global border defined to be empty
5042 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5045 // do not draw special editor door if viewport definitions do not match
5049 EY + EYSIZE != VY + VYSIZE)
5055 void DrawSpecialEditorDoor()
5057 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5058 int top_border_width = gfx1->width;
5059 int top_border_height = gfx1->height;
5060 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5061 int ex = EX - outer_border;
5062 int ey = EY - outer_border;
5063 int vy = VY - outer_border;
5064 int exsize = EXSIZE + 2 * outer_border;
5066 if (!useSpecialEditorDoor())
5069 /* draw bigger level editor toolbox window */
5070 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5071 top_border_width, top_border_height, ex, ey - top_border_height);
5072 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5073 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5075 redraw_mask |= REDRAW_ALL;
5078 void UndrawSpecialEditorDoor()
5080 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5081 int top_border_width = gfx1->width;
5082 int top_border_height = gfx1->height;
5083 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5084 int ex = EX - outer_border;
5085 int ey = EY - outer_border;
5086 int ey_top = ey - top_border_height;
5087 int exsize = EXSIZE + 2 * outer_border;
5088 int eysize = EYSIZE + 2 * outer_border;
5090 if (!useSpecialEditorDoor())
5093 /* draw normal tape recorder window */
5094 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5096 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5097 ex, ey_top, top_border_width, top_border_height,
5099 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5100 ex, ey, exsize, eysize, ex, ey);
5104 // if screen background is set to "[NONE]", clear editor toolbox window
5105 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5106 ClearRectangle(drawto, ex, ey, exsize, eysize);
5109 redraw_mask |= REDRAW_ALL;
5113 /* ---------- new tool button stuff ---------------------------------------- */
5118 struct TextPosInfo *pos;
5121 } toolbutton_info[NUM_TOOL_BUTTONS] =
5124 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5125 TOOL_CTRL_ID_YES, "yes"
5128 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5129 TOOL_CTRL_ID_NO, "no"
5132 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5133 TOOL_CTRL_ID_CONFIRM, "confirm"
5136 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5137 TOOL_CTRL_ID_PLAYER_1, "player 1"
5140 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5141 TOOL_CTRL_ID_PLAYER_2, "player 2"
5144 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5145 TOOL_CTRL_ID_PLAYER_3, "player 3"
5148 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5149 TOOL_CTRL_ID_PLAYER_4, "player 4"
5153 void CreateToolButtons()
5157 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5159 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
5160 struct TextPosInfo *pos = toolbutton_info[i].pos;
5161 struct GadgetInfo *gi;
5162 Bitmap *deco_bitmap = None;
5163 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5164 unsigned int event_mask = GD_EVENT_RELEASED;
5167 int gd_x = gfx->src_x;
5168 int gd_y = gfx->src_y;
5169 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5170 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5173 if (global.use_envelope_request)
5174 setRequestPosition(&dx, &dy, TRUE);
5176 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5178 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5180 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5181 pos->size, &deco_bitmap, &deco_x, &deco_y);
5182 deco_xpos = (gfx->width - pos->size) / 2;
5183 deco_ypos = (gfx->height - pos->size) / 2;
5186 gi = CreateGadget(GDI_CUSTOM_ID, id,
5187 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5188 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
5189 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
5190 GDI_WIDTH, gfx->width,
5191 GDI_HEIGHT, gfx->height,
5192 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5193 GDI_STATE, GD_BUTTON_UNPRESSED,
5194 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5195 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5196 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5197 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5198 GDI_DECORATION_SIZE, pos->size, pos->size,
5199 GDI_DECORATION_SHIFTING, 1, 1,
5200 GDI_DIRECT_DRAW, FALSE,
5201 GDI_EVENT_MASK, event_mask,
5202 GDI_CALLBACK_ACTION, HandleToolButtons,
5206 Error(ERR_EXIT, "cannot create gadget");
5208 tool_gadget[id] = gi;
5212 void FreeToolButtons()
5216 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5217 FreeGadget(tool_gadget[i]);
5220 static void UnmapToolButtons()
5224 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5225 UnmapGadget(tool_gadget[i]);
5228 static void HandleToolButtons(struct GadgetInfo *gi)
5230 request_gadget_id = gi->custom_id;
5233 static struct Mapping_EM_to_RND_object
5236 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
5237 boolean is_backside; /* backside of moving element */
5243 em_object_mapping_list[] =
5246 Xblank, TRUE, FALSE,
5250 Yacid_splash_eB, FALSE, FALSE,
5251 EL_ACID_SPLASH_RIGHT, -1, -1
5254 Yacid_splash_wB, FALSE, FALSE,
5255 EL_ACID_SPLASH_LEFT, -1, -1
5258 #ifdef EM_ENGINE_BAD_ROLL
5260 Xstone_force_e, FALSE, FALSE,
5261 EL_ROCK, -1, MV_BIT_RIGHT
5264 Xstone_force_w, FALSE, FALSE,
5265 EL_ROCK, -1, MV_BIT_LEFT
5268 Xnut_force_e, FALSE, FALSE,
5269 EL_NUT, -1, MV_BIT_RIGHT
5272 Xnut_force_w, FALSE, FALSE,
5273 EL_NUT, -1, MV_BIT_LEFT
5276 Xspring_force_e, FALSE, FALSE,
5277 EL_SPRING, -1, MV_BIT_RIGHT
5280 Xspring_force_w, FALSE, FALSE,
5281 EL_SPRING, -1, MV_BIT_LEFT
5284 Xemerald_force_e, FALSE, FALSE,
5285 EL_EMERALD, -1, MV_BIT_RIGHT
5288 Xemerald_force_w, FALSE, FALSE,
5289 EL_EMERALD, -1, MV_BIT_LEFT
5292 Xdiamond_force_e, FALSE, FALSE,
5293 EL_DIAMOND, -1, MV_BIT_RIGHT
5296 Xdiamond_force_w, FALSE, FALSE,
5297 EL_DIAMOND, -1, MV_BIT_LEFT
5300 Xbomb_force_e, FALSE, FALSE,
5301 EL_BOMB, -1, MV_BIT_RIGHT
5304 Xbomb_force_w, FALSE, FALSE,
5305 EL_BOMB, -1, MV_BIT_LEFT
5307 #endif /* EM_ENGINE_BAD_ROLL */
5310 Xstone, TRUE, FALSE,
5314 Xstone_pause, FALSE, FALSE,
5318 Xstone_fall, FALSE, FALSE,
5322 Ystone_s, FALSE, FALSE,
5323 EL_ROCK, ACTION_FALLING, -1
5326 Ystone_sB, FALSE, TRUE,
5327 EL_ROCK, ACTION_FALLING, -1
5330 Ystone_e, FALSE, FALSE,
5331 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5334 Ystone_eB, FALSE, TRUE,
5335 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5338 Ystone_w, FALSE, FALSE,
5339 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5342 Ystone_wB, FALSE, TRUE,
5343 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5350 Xnut_pause, FALSE, FALSE,
5354 Xnut_fall, FALSE, FALSE,
5358 Ynut_s, FALSE, FALSE,
5359 EL_NUT, ACTION_FALLING, -1
5362 Ynut_sB, FALSE, TRUE,
5363 EL_NUT, ACTION_FALLING, -1
5366 Ynut_e, FALSE, FALSE,
5367 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5370 Ynut_eB, FALSE, TRUE,
5371 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5374 Ynut_w, FALSE, FALSE,
5375 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5378 Ynut_wB, FALSE, TRUE,
5379 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5382 Xbug_n, TRUE, FALSE,
5386 Xbug_e, TRUE, FALSE,
5387 EL_BUG_RIGHT, -1, -1
5390 Xbug_s, TRUE, FALSE,
5394 Xbug_w, TRUE, FALSE,
5398 Xbug_gon, FALSE, FALSE,
5402 Xbug_goe, FALSE, FALSE,
5403 EL_BUG_RIGHT, -1, -1
5406 Xbug_gos, FALSE, FALSE,
5410 Xbug_gow, FALSE, FALSE,
5414 Ybug_n, FALSE, FALSE,
5415 EL_BUG, ACTION_MOVING, MV_BIT_UP
5418 Ybug_nB, FALSE, TRUE,
5419 EL_BUG, ACTION_MOVING, MV_BIT_UP
5422 Ybug_e, FALSE, FALSE,
5423 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5426 Ybug_eB, FALSE, TRUE,
5427 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5430 Ybug_s, FALSE, FALSE,
5431 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5434 Ybug_sB, FALSE, TRUE,
5435 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5438 Ybug_w, FALSE, FALSE,
5439 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5442 Ybug_wB, FALSE, TRUE,
5443 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5446 Ybug_w_n, FALSE, FALSE,
5447 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5450 Ybug_n_e, FALSE, FALSE,
5451 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5454 Ybug_e_s, FALSE, FALSE,
5455 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5458 Ybug_s_w, FALSE, FALSE,
5459 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5462 Ybug_e_n, FALSE, FALSE,
5463 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5466 Ybug_s_e, FALSE, FALSE,
5467 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5470 Ybug_w_s, FALSE, FALSE,
5471 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5474 Ybug_n_w, FALSE, FALSE,
5475 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5478 Ybug_stone, FALSE, FALSE,
5479 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5482 Ybug_spring, FALSE, FALSE,
5483 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5486 Xtank_n, TRUE, FALSE,
5487 EL_SPACESHIP_UP, -1, -1
5490 Xtank_e, TRUE, FALSE,
5491 EL_SPACESHIP_RIGHT, -1, -1
5494 Xtank_s, TRUE, FALSE,
5495 EL_SPACESHIP_DOWN, -1, -1
5498 Xtank_w, TRUE, FALSE,
5499 EL_SPACESHIP_LEFT, -1, -1
5502 Xtank_gon, FALSE, FALSE,
5503 EL_SPACESHIP_UP, -1, -1
5506 Xtank_goe, FALSE, FALSE,
5507 EL_SPACESHIP_RIGHT, -1, -1
5510 Xtank_gos, FALSE, FALSE,
5511 EL_SPACESHIP_DOWN, -1, -1
5514 Xtank_gow, FALSE, FALSE,
5515 EL_SPACESHIP_LEFT, -1, -1
5518 Ytank_n, FALSE, FALSE,
5519 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5522 Ytank_nB, FALSE, TRUE,
5523 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5526 Ytank_e, FALSE, FALSE,
5527 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5530 Ytank_eB, FALSE, TRUE,
5531 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5534 Ytank_s, FALSE, FALSE,
5535 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5538 Ytank_sB, FALSE, TRUE,
5539 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5542 Ytank_w, FALSE, FALSE,
5543 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5546 Ytank_wB, FALSE, TRUE,
5547 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5550 Ytank_w_n, FALSE, FALSE,
5551 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5554 Ytank_n_e, FALSE, FALSE,
5555 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5558 Ytank_e_s, FALSE, FALSE,
5559 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5562 Ytank_s_w, FALSE, FALSE,
5563 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5566 Ytank_e_n, FALSE, FALSE,
5567 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5570 Ytank_s_e, FALSE, FALSE,
5571 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5574 Ytank_w_s, FALSE, FALSE,
5575 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5578 Ytank_n_w, FALSE, FALSE,
5579 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5582 Ytank_stone, FALSE, FALSE,
5583 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5586 Ytank_spring, FALSE, FALSE,
5587 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5590 Xandroid, TRUE, FALSE,
5591 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5594 Xandroid_1_n, FALSE, FALSE,
5595 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5598 Xandroid_2_n, FALSE, FALSE,
5599 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5602 Xandroid_1_e, FALSE, FALSE,
5603 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5606 Xandroid_2_e, FALSE, FALSE,
5607 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5610 Xandroid_1_w, FALSE, FALSE,
5611 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5614 Xandroid_2_w, FALSE, FALSE,
5615 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5618 Xandroid_1_s, FALSE, FALSE,
5619 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5622 Xandroid_2_s, FALSE, FALSE,
5623 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5626 Yandroid_n, FALSE, FALSE,
5627 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5630 Yandroid_nB, FALSE, TRUE,
5631 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5634 Yandroid_ne, FALSE, FALSE,
5635 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5638 Yandroid_neB, FALSE, TRUE,
5639 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5642 Yandroid_e, FALSE, FALSE,
5643 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5646 Yandroid_eB, FALSE, TRUE,
5647 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5650 Yandroid_se, FALSE, FALSE,
5651 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5654 Yandroid_seB, FALSE, TRUE,
5655 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5658 Yandroid_s, FALSE, FALSE,
5659 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5662 Yandroid_sB, FALSE, TRUE,
5663 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5666 Yandroid_sw, FALSE, FALSE,
5667 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5670 Yandroid_swB, FALSE, TRUE,
5671 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5674 Yandroid_w, FALSE, FALSE,
5675 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5678 Yandroid_wB, FALSE, TRUE,
5679 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5682 Yandroid_nw, FALSE, FALSE,
5683 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5686 Yandroid_nwB, FALSE, TRUE,
5687 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5690 Xspring, TRUE, FALSE,
5694 Xspring_pause, FALSE, FALSE,
5698 Xspring_e, FALSE, FALSE,
5702 Xspring_w, FALSE, FALSE,
5706 Xspring_fall, FALSE, FALSE,
5710 Yspring_s, FALSE, FALSE,
5711 EL_SPRING, ACTION_FALLING, -1
5714 Yspring_sB, FALSE, TRUE,
5715 EL_SPRING, ACTION_FALLING, -1
5718 Yspring_e, FALSE, FALSE,
5719 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5722 Yspring_eB, FALSE, TRUE,
5723 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5726 Yspring_w, FALSE, FALSE,
5727 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5730 Yspring_wB, FALSE, TRUE,
5731 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5734 Yspring_kill_e, FALSE, FALSE,
5735 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5738 Yspring_kill_eB, FALSE, TRUE,
5739 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5742 Yspring_kill_w, FALSE, FALSE,
5743 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5746 Yspring_kill_wB, FALSE, TRUE,
5747 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5750 Xeater_n, TRUE, FALSE,
5751 EL_YAMYAM_UP, -1, -1
5754 Xeater_e, TRUE, FALSE,
5755 EL_YAMYAM_RIGHT, -1, -1
5758 Xeater_w, TRUE, FALSE,
5759 EL_YAMYAM_LEFT, -1, -1
5762 Xeater_s, TRUE, FALSE,
5763 EL_YAMYAM_DOWN, -1, -1
5766 Yeater_n, FALSE, FALSE,
5767 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5770 Yeater_nB, FALSE, TRUE,
5771 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5774 Yeater_e, FALSE, FALSE,
5775 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5778 Yeater_eB, FALSE, TRUE,
5779 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5782 Yeater_s, FALSE, FALSE,
5783 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5786 Yeater_sB, FALSE, TRUE,
5787 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5790 Yeater_w, FALSE, FALSE,
5791 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5794 Yeater_wB, FALSE, TRUE,
5795 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5798 Yeater_stone, FALSE, FALSE,
5799 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5802 Yeater_spring, FALSE, FALSE,
5803 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5806 Xalien, TRUE, FALSE,
5810 Xalien_pause, FALSE, FALSE,
5814 Yalien_n, FALSE, FALSE,
5815 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5818 Yalien_nB, FALSE, TRUE,
5819 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5822 Yalien_e, FALSE, FALSE,
5823 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5826 Yalien_eB, FALSE, TRUE,
5827 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5830 Yalien_s, FALSE, FALSE,
5831 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5834 Yalien_sB, FALSE, TRUE,
5835 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5838 Yalien_w, FALSE, FALSE,
5839 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5842 Yalien_wB, FALSE, TRUE,
5843 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5846 Yalien_stone, FALSE, FALSE,
5847 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5850 Yalien_spring, FALSE, FALSE,
5851 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5854 Xemerald, TRUE, FALSE,
5858 Xemerald_pause, FALSE, FALSE,
5862 Xemerald_fall, FALSE, FALSE,
5866 Xemerald_shine, FALSE, FALSE,
5867 EL_EMERALD, ACTION_TWINKLING, -1
5870 Yemerald_s, FALSE, FALSE,
5871 EL_EMERALD, ACTION_FALLING, -1
5874 Yemerald_sB, FALSE, TRUE,
5875 EL_EMERALD, ACTION_FALLING, -1
5878 Yemerald_e, FALSE, FALSE,
5879 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5882 Yemerald_eB, FALSE, TRUE,
5883 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5886 Yemerald_w, FALSE, FALSE,
5887 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5890 Yemerald_wB, FALSE, TRUE,
5891 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5894 Yemerald_eat, FALSE, FALSE,
5895 EL_EMERALD, ACTION_COLLECTING, -1
5898 Yemerald_stone, FALSE, FALSE,
5899 EL_NUT, ACTION_BREAKING, -1
5902 Xdiamond, TRUE, FALSE,
5906 Xdiamond_pause, FALSE, FALSE,
5910 Xdiamond_fall, FALSE, FALSE,
5914 Xdiamond_shine, FALSE, FALSE,
5915 EL_DIAMOND, ACTION_TWINKLING, -1
5918 Ydiamond_s, FALSE, FALSE,
5919 EL_DIAMOND, ACTION_FALLING, -1
5922 Ydiamond_sB, FALSE, TRUE,
5923 EL_DIAMOND, ACTION_FALLING, -1
5926 Ydiamond_e, FALSE, FALSE,
5927 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5930 Ydiamond_eB, FALSE, TRUE,
5931 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5934 Ydiamond_w, FALSE, FALSE,
5935 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5938 Ydiamond_wB, FALSE, TRUE,
5939 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5942 Ydiamond_eat, FALSE, FALSE,
5943 EL_DIAMOND, ACTION_COLLECTING, -1
5946 Ydiamond_stone, FALSE, FALSE,
5947 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5950 Xdrip_fall, TRUE, FALSE,
5951 EL_AMOEBA_DROP, -1, -1
5954 Xdrip_stretch, FALSE, FALSE,
5955 EL_AMOEBA_DROP, ACTION_FALLING, -1
5958 Xdrip_stretchB, FALSE, TRUE,
5959 EL_AMOEBA_DROP, ACTION_FALLING, -1
5962 Xdrip_eat, FALSE, FALSE,
5963 EL_AMOEBA_DROP, ACTION_GROWING, -1
5966 Ydrip_s1, FALSE, FALSE,
5967 EL_AMOEBA_DROP, ACTION_FALLING, -1
5970 Ydrip_s1B, FALSE, TRUE,
5971 EL_AMOEBA_DROP, ACTION_FALLING, -1
5974 Ydrip_s2, FALSE, FALSE,
5975 EL_AMOEBA_DROP, ACTION_FALLING, -1
5978 Ydrip_s2B, FALSE, TRUE,
5979 EL_AMOEBA_DROP, ACTION_FALLING, -1
5986 Xbomb_pause, FALSE, FALSE,
5990 Xbomb_fall, FALSE, FALSE,
5994 Ybomb_s, FALSE, FALSE,
5995 EL_BOMB, ACTION_FALLING, -1
5998 Ybomb_sB, FALSE, TRUE,
5999 EL_BOMB, ACTION_FALLING, -1
6002 Ybomb_e, FALSE, FALSE,
6003 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6006 Ybomb_eB, FALSE, TRUE,
6007 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6010 Ybomb_w, FALSE, FALSE,
6011 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6014 Ybomb_wB, FALSE, TRUE,
6015 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6018 Ybomb_eat, FALSE, FALSE,
6019 EL_BOMB, ACTION_ACTIVATING, -1
6022 Xballoon, TRUE, FALSE,
6026 Yballoon_n, FALSE, FALSE,
6027 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6030 Yballoon_nB, FALSE, TRUE,
6031 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6034 Yballoon_e, FALSE, FALSE,
6035 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6038 Yballoon_eB, FALSE, TRUE,
6039 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6042 Yballoon_s, FALSE, FALSE,
6043 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6046 Yballoon_sB, FALSE, TRUE,
6047 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6050 Yballoon_w, FALSE, FALSE,
6051 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6054 Yballoon_wB, FALSE, TRUE,
6055 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6058 Xgrass, TRUE, FALSE,
6059 EL_EMC_GRASS, -1, -1
6062 Ygrass_nB, FALSE, FALSE,
6063 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6066 Ygrass_eB, FALSE, FALSE,
6067 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6070 Ygrass_sB, FALSE, FALSE,
6071 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6074 Ygrass_wB, FALSE, FALSE,
6075 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6082 Ydirt_nB, FALSE, FALSE,
6083 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6086 Ydirt_eB, FALSE, FALSE,
6087 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6090 Ydirt_sB, FALSE, FALSE,
6091 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6094 Ydirt_wB, FALSE, FALSE,
6095 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6098 Xacid_ne, TRUE, FALSE,
6099 EL_ACID_POOL_TOPRIGHT, -1, -1
6102 Xacid_se, TRUE, FALSE,
6103 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6106 Xacid_s, TRUE, FALSE,
6107 EL_ACID_POOL_BOTTOM, -1, -1
6110 Xacid_sw, TRUE, FALSE,
6111 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6114 Xacid_nw, TRUE, FALSE,
6115 EL_ACID_POOL_TOPLEFT, -1, -1
6118 Xacid_1, TRUE, FALSE,
6122 Xacid_2, FALSE, FALSE,
6126 Xacid_3, FALSE, FALSE,
6130 Xacid_4, FALSE, FALSE,
6134 Xacid_5, FALSE, FALSE,
6138 Xacid_6, FALSE, FALSE,
6142 Xacid_7, FALSE, FALSE,
6146 Xacid_8, FALSE, FALSE,
6150 Xball_1, TRUE, FALSE,
6151 EL_EMC_MAGIC_BALL, -1, -1
6154 Xball_1B, FALSE, FALSE,
6155 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6158 Xball_2, FALSE, FALSE,
6159 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6162 Xball_2B, FALSE, FALSE,
6163 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6166 Yball_eat, FALSE, FALSE,
6167 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6170 Ykey_1_eat, FALSE, FALSE,
6171 EL_EM_KEY_1, ACTION_COLLECTING, -1
6174 Ykey_2_eat, FALSE, FALSE,
6175 EL_EM_KEY_2, ACTION_COLLECTING, -1
6178 Ykey_3_eat, FALSE, FALSE,
6179 EL_EM_KEY_3, ACTION_COLLECTING, -1
6182 Ykey_4_eat, FALSE, FALSE,
6183 EL_EM_KEY_4, ACTION_COLLECTING, -1
6186 Ykey_5_eat, FALSE, FALSE,
6187 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6190 Ykey_6_eat, FALSE, FALSE,
6191 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6194 Ykey_7_eat, FALSE, FALSE,
6195 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6198 Ykey_8_eat, FALSE, FALSE,
6199 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6202 Ylenses_eat, FALSE, FALSE,
6203 EL_EMC_LENSES, ACTION_COLLECTING, -1
6206 Ymagnify_eat, FALSE, FALSE,
6207 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6210 Ygrass_eat, FALSE, FALSE,
6211 EL_EMC_GRASS, ACTION_SNAPPING, -1
6214 Ydirt_eat, FALSE, FALSE,
6215 EL_SAND, ACTION_SNAPPING, -1
6218 Xgrow_ns, TRUE, FALSE,
6219 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6222 Ygrow_ns_eat, FALSE, FALSE,
6223 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6226 Xgrow_ew, TRUE, FALSE,
6227 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6230 Ygrow_ew_eat, FALSE, FALSE,
6231 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6234 Xwonderwall, TRUE, FALSE,
6235 EL_MAGIC_WALL, -1, -1
6238 XwonderwallB, FALSE, FALSE,
6239 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6242 Xamoeba_1, TRUE, FALSE,
6243 EL_AMOEBA_DRY, ACTION_OTHER, -1
6246 Xamoeba_2, FALSE, FALSE,
6247 EL_AMOEBA_DRY, ACTION_OTHER, -1
6250 Xamoeba_3, FALSE, FALSE,
6251 EL_AMOEBA_DRY, ACTION_OTHER, -1
6254 Xamoeba_4, FALSE, FALSE,
6255 EL_AMOEBA_DRY, ACTION_OTHER, -1
6258 Xamoeba_5, TRUE, FALSE,
6259 EL_AMOEBA_WET, ACTION_OTHER, -1
6262 Xamoeba_6, FALSE, FALSE,
6263 EL_AMOEBA_WET, ACTION_OTHER, -1
6266 Xamoeba_7, FALSE, FALSE,
6267 EL_AMOEBA_WET, ACTION_OTHER, -1
6270 Xamoeba_8, FALSE, FALSE,
6271 EL_AMOEBA_WET, ACTION_OTHER, -1
6274 Xdoor_1, TRUE, FALSE,
6275 EL_EM_GATE_1, -1, -1
6278 Xdoor_2, TRUE, FALSE,
6279 EL_EM_GATE_2, -1, -1
6282 Xdoor_3, TRUE, FALSE,
6283 EL_EM_GATE_3, -1, -1
6286 Xdoor_4, TRUE, FALSE,
6287 EL_EM_GATE_4, -1, -1
6290 Xdoor_5, TRUE, FALSE,
6291 EL_EMC_GATE_5, -1, -1
6294 Xdoor_6, TRUE, FALSE,
6295 EL_EMC_GATE_6, -1, -1
6298 Xdoor_7, TRUE, FALSE,
6299 EL_EMC_GATE_7, -1, -1
6302 Xdoor_8, TRUE, FALSE,
6303 EL_EMC_GATE_8, -1, -1
6306 Xkey_1, TRUE, FALSE,
6310 Xkey_2, TRUE, FALSE,
6314 Xkey_3, TRUE, FALSE,
6318 Xkey_4, TRUE, FALSE,
6322 Xkey_5, TRUE, FALSE,
6323 EL_EMC_KEY_5, -1, -1
6326 Xkey_6, TRUE, FALSE,
6327 EL_EMC_KEY_6, -1, -1
6330 Xkey_7, TRUE, FALSE,
6331 EL_EMC_KEY_7, -1, -1
6334 Xkey_8, TRUE, FALSE,
6335 EL_EMC_KEY_8, -1, -1
6338 Xwind_n, TRUE, FALSE,
6339 EL_BALLOON_SWITCH_UP, -1, -1
6342 Xwind_e, TRUE, FALSE,
6343 EL_BALLOON_SWITCH_RIGHT, -1, -1
6346 Xwind_s, TRUE, FALSE,
6347 EL_BALLOON_SWITCH_DOWN, -1, -1
6350 Xwind_w, TRUE, FALSE,
6351 EL_BALLOON_SWITCH_LEFT, -1, -1
6354 Xwind_nesw, TRUE, FALSE,
6355 EL_BALLOON_SWITCH_ANY, -1, -1
6358 Xwind_stop, TRUE, FALSE,
6359 EL_BALLOON_SWITCH_NONE, -1, -1
6363 EL_EM_EXIT_CLOSED, -1, -1
6366 Xexit_1, TRUE, FALSE,
6367 EL_EM_EXIT_OPEN, -1, -1
6370 Xexit_2, FALSE, FALSE,
6371 EL_EM_EXIT_OPEN, -1, -1
6374 Xexit_3, FALSE, FALSE,
6375 EL_EM_EXIT_OPEN, -1, -1
6378 Xdynamite, TRUE, FALSE,
6379 EL_EM_DYNAMITE, -1, -1
6382 Ydynamite_eat, FALSE, FALSE,
6383 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6386 Xdynamite_1, TRUE, FALSE,
6387 EL_EM_DYNAMITE_ACTIVE, -1, -1
6390 Xdynamite_2, FALSE, FALSE,
6391 EL_EM_DYNAMITE_ACTIVE, -1, -1
6394 Xdynamite_3, FALSE, FALSE,
6395 EL_EM_DYNAMITE_ACTIVE, -1, -1
6398 Xdynamite_4, FALSE, FALSE,
6399 EL_EM_DYNAMITE_ACTIVE, -1, -1
6402 Xbumper, TRUE, FALSE,
6403 EL_EMC_SPRING_BUMPER, -1, -1
6406 XbumperB, FALSE, FALSE,
6407 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6410 Xwheel, TRUE, FALSE,
6411 EL_ROBOT_WHEEL, -1, -1
6414 XwheelB, FALSE, FALSE,
6415 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6418 Xswitch, TRUE, FALSE,
6419 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6422 XswitchB, FALSE, FALSE,
6423 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6427 EL_QUICKSAND_EMPTY, -1, -1
6430 Xsand_stone, TRUE, FALSE,
6431 EL_QUICKSAND_FULL, -1, -1
6434 Xsand_stonein_1, FALSE, TRUE,
6435 EL_ROCK, ACTION_FILLING, -1
6438 Xsand_stonein_2, FALSE, TRUE,
6439 EL_ROCK, ACTION_FILLING, -1
6442 Xsand_stonein_3, FALSE, TRUE,
6443 EL_ROCK, ACTION_FILLING, -1
6446 Xsand_stonein_4, FALSE, TRUE,
6447 EL_ROCK, ACTION_FILLING, -1
6450 Xsand_stonesand_1, FALSE, FALSE,
6451 EL_QUICKSAND_EMPTYING, -1, -1
6454 Xsand_stonesand_2, FALSE, FALSE,
6455 EL_QUICKSAND_EMPTYING, -1, -1
6458 Xsand_stonesand_3, FALSE, FALSE,
6459 EL_QUICKSAND_EMPTYING, -1, -1
6462 Xsand_stonesand_4, FALSE, FALSE,
6463 EL_QUICKSAND_EMPTYING, -1, -1
6466 Xsand_stonesand_quickout_1, FALSE, FALSE,
6467 EL_QUICKSAND_EMPTYING, -1, -1
6470 Xsand_stonesand_quickout_2, FALSE, FALSE,
6471 EL_QUICKSAND_EMPTYING, -1, -1
6474 Xsand_stoneout_1, FALSE, FALSE,
6475 EL_ROCK, ACTION_EMPTYING, -1
6478 Xsand_stoneout_2, FALSE, FALSE,
6479 EL_ROCK, ACTION_EMPTYING, -1
6482 Xsand_sandstone_1, FALSE, FALSE,
6483 EL_QUICKSAND_FILLING, -1, -1
6486 Xsand_sandstone_2, FALSE, FALSE,
6487 EL_QUICKSAND_FILLING, -1, -1
6490 Xsand_sandstone_3, FALSE, FALSE,
6491 EL_QUICKSAND_FILLING, -1, -1
6494 Xsand_sandstone_4, FALSE, FALSE,
6495 EL_QUICKSAND_FILLING, -1, -1
6498 Xplant, TRUE, FALSE,
6499 EL_EMC_PLANT, -1, -1
6502 Yplant, FALSE, FALSE,
6503 EL_EMC_PLANT, -1, -1
6506 Xlenses, TRUE, FALSE,
6507 EL_EMC_LENSES, -1, -1
6510 Xmagnify, TRUE, FALSE,
6511 EL_EMC_MAGNIFIER, -1, -1
6514 Xdripper, TRUE, FALSE,
6515 EL_EMC_DRIPPER, -1, -1
6518 XdripperB, FALSE, FALSE,
6519 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6522 Xfake_blank, TRUE, FALSE,
6523 EL_INVISIBLE_WALL, -1, -1
6526 Xfake_blankB, FALSE, FALSE,
6527 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6530 Xfake_grass, TRUE, FALSE,
6531 EL_EMC_FAKE_GRASS, -1, -1
6534 Xfake_grassB, FALSE, FALSE,
6535 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6538 Xfake_door_1, TRUE, FALSE,
6539 EL_EM_GATE_1_GRAY, -1, -1
6542 Xfake_door_2, TRUE, FALSE,
6543 EL_EM_GATE_2_GRAY, -1, -1
6546 Xfake_door_3, TRUE, FALSE,
6547 EL_EM_GATE_3_GRAY, -1, -1
6550 Xfake_door_4, TRUE, FALSE,
6551 EL_EM_GATE_4_GRAY, -1, -1
6554 Xfake_door_5, TRUE, FALSE,
6555 EL_EMC_GATE_5_GRAY, -1, -1
6558 Xfake_door_6, TRUE, FALSE,
6559 EL_EMC_GATE_6_GRAY, -1, -1
6562 Xfake_door_7, TRUE, FALSE,
6563 EL_EMC_GATE_7_GRAY, -1, -1
6566 Xfake_door_8, TRUE, FALSE,
6567 EL_EMC_GATE_8_GRAY, -1, -1
6570 Xfake_acid_1, TRUE, FALSE,
6571 EL_EMC_FAKE_ACID, -1, -1
6574 Xfake_acid_2, FALSE, FALSE,
6575 EL_EMC_FAKE_ACID, -1, -1
6578 Xfake_acid_3, FALSE, FALSE,
6579 EL_EMC_FAKE_ACID, -1, -1
6582 Xfake_acid_4, FALSE, FALSE,
6583 EL_EMC_FAKE_ACID, -1, -1
6586 Xfake_acid_5, FALSE, FALSE,
6587 EL_EMC_FAKE_ACID, -1, -1
6590 Xfake_acid_6, FALSE, FALSE,
6591 EL_EMC_FAKE_ACID, -1, -1
6594 Xfake_acid_7, FALSE, FALSE,
6595 EL_EMC_FAKE_ACID, -1, -1
6598 Xfake_acid_8, FALSE, FALSE,
6599 EL_EMC_FAKE_ACID, -1, -1
6602 Xsteel_1, TRUE, FALSE,
6603 EL_STEELWALL, -1, -1
6606 Xsteel_2, TRUE, FALSE,
6607 EL_EMC_STEELWALL_2, -1, -1
6610 Xsteel_3, TRUE, FALSE,
6611 EL_EMC_STEELWALL_3, -1, -1
6614 Xsteel_4, TRUE, FALSE,
6615 EL_EMC_STEELWALL_4, -1, -1
6618 Xwall_1, TRUE, FALSE,
6622 Xwall_2, TRUE, FALSE,
6623 EL_EMC_WALL_14, -1, -1
6626 Xwall_3, TRUE, FALSE,
6627 EL_EMC_WALL_15, -1, -1
6630 Xwall_4, TRUE, FALSE,
6631 EL_EMC_WALL_16, -1, -1
6634 Xround_wall_1, TRUE, FALSE,
6635 EL_WALL_SLIPPERY, -1, -1
6638 Xround_wall_2, TRUE, FALSE,
6639 EL_EMC_WALL_SLIPPERY_2, -1, -1
6642 Xround_wall_3, TRUE, FALSE,
6643 EL_EMC_WALL_SLIPPERY_3, -1, -1
6646 Xround_wall_4, TRUE, FALSE,
6647 EL_EMC_WALL_SLIPPERY_4, -1, -1
6650 Xdecor_1, TRUE, FALSE,
6651 EL_EMC_WALL_8, -1, -1
6654 Xdecor_2, TRUE, FALSE,
6655 EL_EMC_WALL_6, -1, -1
6658 Xdecor_3, TRUE, FALSE,
6659 EL_EMC_WALL_4, -1, -1
6662 Xdecor_4, TRUE, FALSE,
6663 EL_EMC_WALL_7, -1, -1
6666 Xdecor_5, TRUE, FALSE,
6667 EL_EMC_WALL_5, -1, -1
6670 Xdecor_6, TRUE, FALSE,
6671 EL_EMC_WALL_9, -1, -1
6674 Xdecor_7, TRUE, FALSE,
6675 EL_EMC_WALL_10, -1, -1
6678 Xdecor_8, TRUE, FALSE,
6679 EL_EMC_WALL_1, -1, -1
6682 Xdecor_9, TRUE, FALSE,
6683 EL_EMC_WALL_2, -1, -1
6686 Xdecor_10, TRUE, FALSE,
6687 EL_EMC_WALL_3, -1, -1
6690 Xdecor_11, TRUE, FALSE,
6691 EL_EMC_WALL_11, -1, -1
6694 Xdecor_12, TRUE, FALSE,
6695 EL_EMC_WALL_12, -1, -1
6698 Xalpha_0, TRUE, FALSE,
6699 EL_CHAR('0'), -1, -1
6702 Xalpha_1, TRUE, FALSE,
6703 EL_CHAR('1'), -1, -1
6706 Xalpha_2, TRUE, FALSE,
6707 EL_CHAR('2'), -1, -1
6710 Xalpha_3, TRUE, FALSE,
6711 EL_CHAR('3'), -1, -1
6714 Xalpha_4, TRUE, FALSE,
6715 EL_CHAR('4'), -1, -1
6718 Xalpha_5, TRUE, FALSE,
6719 EL_CHAR('5'), -1, -1
6722 Xalpha_6, TRUE, FALSE,
6723 EL_CHAR('6'), -1, -1
6726 Xalpha_7, TRUE, FALSE,
6727 EL_CHAR('7'), -1, -1
6730 Xalpha_8, TRUE, FALSE,
6731 EL_CHAR('8'), -1, -1
6734 Xalpha_9, TRUE, FALSE,
6735 EL_CHAR('9'), -1, -1
6738 Xalpha_excla, TRUE, FALSE,
6739 EL_CHAR('!'), -1, -1
6742 Xalpha_quote, TRUE, FALSE,
6743 EL_CHAR('"'), -1, -1
6746 Xalpha_comma, TRUE, FALSE,
6747 EL_CHAR(','), -1, -1
6750 Xalpha_minus, TRUE, FALSE,
6751 EL_CHAR('-'), -1, -1
6754 Xalpha_perio, TRUE, FALSE,
6755 EL_CHAR('.'), -1, -1
6758 Xalpha_colon, TRUE, FALSE,
6759 EL_CHAR(':'), -1, -1
6762 Xalpha_quest, TRUE, FALSE,
6763 EL_CHAR('?'), -1, -1
6766 Xalpha_a, TRUE, FALSE,
6767 EL_CHAR('A'), -1, -1
6770 Xalpha_b, TRUE, FALSE,
6771 EL_CHAR('B'), -1, -1
6774 Xalpha_c, TRUE, FALSE,
6775 EL_CHAR('C'), -1, -1
6778 Xalpha_d, TRUE, FALSE,
6779 EL_CHAR('D'), -1, -1
6782 Xalpha_e, TRUE, FALSE,
6783 EL_CHAR('E'), -1, -1
6786 Xalpha_f, TRUE, FALSE,
6787 EL_CHAR('F'), -1, -1
6790 Xalpha_g, TRUE, FALSE,
6791 EL_CHAR('G'), -1, -1
6794 Xalpha_h, TRUE, FALSE,
6795 EL_CHAR('H'), -1, -1
6798 Xalpha_i, TRUE, FALSE,
6799 EL_CHAR('I'), -1, -1
6802 Xalpha_j, TRUE, FALSE,
6803 EL_CHAR('J'), -1, -1
6806 Xalpha_k, TRUE, FALSE,
6807 EL_CHAR('K'), -1, -1
6810 Xalpha_l, TRUE, FALSE,
6811 EL_CHAR('L'), -1, -1
6814 Xalpha_m, TRUE, FALSE,
6815 EL_CHAR('M'), -1, -1
6818 Xalpha_n, TRUE, FALSE,
6819 EL_CHAR('N'), -1, -1
6822 Xalpha_o, TRUE, FALSE,
6823 EL_CHAR('O'), -1, -1
6826 Xalpha_p, TRUE, FALSE,
6827 EL_CHAR('P'), -1, -1
6830 Xalpha_q, TRUE, FALSE,
6831 EL_CHAR('Q'), -1, -1
6834 Xalpha_r, TRUE, FALSE,
6835 EL_CHAR('R'), -1, -1
6838 Xalpha_s, TRUE, FALSE,
6839 EL_CHAR('S'), -1, -1
6842 Xalpha_t, TRUE, FALSE,
6843 EL_CHAR('T'), -1, -1
6846 Xalpha_u, TRUE, FALSE,
6847 EL_CHAR('U'), -1, -1
6850 Xalpha_v, TRUE, FALSE,
6851 EL_CHAR('V'), -1, -1
6854 Xalpha_w, TRUE, FALSE,
6855 EL_CHAR('W'), -1, -1
6858 Xalpha_x, TRUE, FALSE,
6859 EL_CHAR('X'), -1, -1
6862 Xalpha_y, TRUE, FALSE,
6863 EL_CHAR('Y'), -1, -1
6866 Xalpha_z, TRUE, FALSE,
6867 EL_CHAR('Z'), -1, -1
6870 Xalpha_arrow_e, TRUE, FALSE,
6871 EL_CHAR('>'), -1, -1
6874 Xalpha_arrow_w, TRUE, FALSE,
6875 EL_CHAR('<'), -1, -1
6878 Xalpha_copyr, TRUE, FALSE,
6879 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6883 Xboom_bug, FALSE, FALSE,
6884 EL_BUG, ACTION_EXPLODING, -1
6887 Xboom_bomb, FALSE, FALSE,
6888 EL_BOMB, ACTION_EXPLODING, -1
6891 Xboom_android, FALSE, FALSE,
6892 EL_EMC_ANDROID, ACTION_OTHER, -1
6895 Xboom_1, FALSE, FALSE,
6896 EL_DEFAULT, ACTION_EXPLODING, -1
6899 Xboom_2, FALSE, FALSE,
6900 EL_DEFAULT, ACTION_EXPLODING, -1
6903 Znormal, FALSE, FALSE,
6907 Zdynamite, FALSE, FALSE,
6911 Zplayer, FALSE, FALSE,
6915 ZBORDER, FALSE, FALSE,
6925 static struct Mapping_EM_to_RND_player
6934 em_player_mapping_list[] =
6938 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6942 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6946 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6950 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6954 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6958 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6962 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6966 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6970 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6974 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6978 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6982 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6986 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6990 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6994 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6998 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7002 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7006 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7010 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7014 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7018 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7022 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7026 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7030 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7034 EL_PLAYER_1, ACTION_DEFAULT, -1,
7038 EL_PLAYER_2, ACTION_DEFAULT, -1,
7042 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7046 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7050 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7054 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7058 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7062 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7066 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7070 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7074 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7078 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7082 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7086 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7090 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7094 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7098 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7102 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7106 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7110 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7114 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7118 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7122 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7126 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7130 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7134 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7138 EL_PLAYER_3, ACTION_DEFAULT, -1,
7142 EL_PLAYER_4, ACTION_DEFAULT, -1,
7151 int map_element_RND_to_EM(int element_rnd)
7153 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7154 static boolean mapping_initialized = FALSE;
7156 if (!mapping_initialized)
7160 /* return "Xalpha_quest" for all undefined elements in mapping array */
7161 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7162 mapping_RND_to_EM[i] = Xalpha_quest;
7164 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7165 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7166 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7167 em_object_mapping_list[i].element_em;
7169 mapping_initialized = TRUE;
7172 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7173 return mapping_RND_to_EM[element_rnd];
7175 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7180 int map_element_EM_to_RND(int element_em)
7182 static unsigned short mapping_EM_to_RND[TILE_MAX];
7183 static boolean mapping_initialized = FALSE;
7185 if (!mapping_initialized)
7189 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
7190 for (i = 0; i < TILE_MAX; i++)
7191 mapping_EM_to_RND[i] = EL_UNKNOWN;
7193 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7194 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7195 em_object_mapping_list[i].element_rnd;
7197 mapping_initialized = TRUE;
7200 if (element_em >= 0 && element_em < TILE_MAX)
7201 return mapping_EM_to_RND[element_em];
7203 Error(ERR_WARN, "invalid EM level element %d", element_em);
7208 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7210 struct LevelInfo_EM *level_em = level->native_em_level;
7211 struct LEVEL *lev = level_em->lev;
7214 for (i = 0; i < TILE_MAX; i++)
7215 lev->android_array[i] = Xblank;
7217 for (i = 0; i < level->num_android_clone_elements; i++)
7219 int element_rnd = level->android_clone_element[i];
7220 int element_em = map_element_RND_to_EM(element_rnd);
7222 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7223 if (em_object_mapping_list[j].element_rnd == element_rnd)
7224 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7228 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7230 struct LevelInfo_EM *level_em = level->native_em_level;
7231 struct LEVEL *lev = level_em->lev;
7234 level->num_android_clone_elements = 0;
7236 for (i = 0; i < TILE_MAX; i++)
7238 int element_em = lev->android_array[i];
7240 boolean element_found = FALSE;
7242 if (element_em == Xblank)
7245 element_rnd = map_element_EM_to_RND(element_em);
7247 for (j = 0; j < level->num_android_clone_elements; j++)
7248 if (level->android_clone_element[j] == element_rnd)
7249 element_found = TRUE;
7253 level->android_clone_element[level->num_android_clone_elements++] =
7256 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7261 if (level->num_android_clone_elements == 0)
7263 level->num_android_clone_elements = 1;
7264 level->android_clone_element[0] = EL_EMPTY;
7268 int map_direction_RND_to_EM(int direction)
7270 return (direction == MV_UP ? 0 :
7271 direction == MV_RIGHT ? 1 :
7272 direction == MV_DOWN ? 2 :
7273 direction == MV_LEFT ? 3 :
7277 int map_direction_EM_to_RND(int direction)
7279 return (direction == 0 ? MV_UP :
7280 direction == 1 ? MV_RIGHT :
7281 direction == 2 ? MV_DOWN :
7282 direction == 3 ? MV_LEFT :
7286 int map_element_RND_to_SP(int element_rnd)
7288 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
7290 if (element_rnd >= EL_SP_START &&
7291 element_rnd <= EL_SP_END)
7292 element_sp = element_rnd - EL_SP_START;
7293 else if (element_rnd == EL_EMPTY_SPACE)
7295 else if (element_rnd == EL_INVISIBLE_WALL)
7301 int map_element_SP_to_RND(int element_sp)
7303 int element_rnd = EL_UNKNOWN;
7305 if (element_sp >= 0x00 &&
7307 element_rnd = EL_SP_START + element_sp;
7308 else if (element_sp == 0x28)
7309 element_rnd = EL_INVISIBLE_WALL;
7314 int map_action_SP_to_RND(int action_sp)
7318 case actActive: return ACTION_ACTIVE;
7319 case actImpact: return ACTION_IMPACT;
7320 case actExploding: return ACTION_EXPLODING;
7321 case actDigging: return ACTION_DIGGING;
7322 case actSnapping: return ACTION_SNAPPING;
7323 case actCollecting: return ACTION_COLLECTING;
7324 case actPassing: return ACTION_PASSING;
7325 case actPushing: return ACTION_PUSHING;
7326 case actDropping: return ACTION_DROPPING;
7328 default: return ACTION_DEFAULT;
7332 int map_element_RND_to_MM(int element_rnd)
7334 return (element_rnd >= EL_MM_START_1 &&
7335 element_rnd <= EL_MM_END_1 ?
7336 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7338 element_rnd >= EL_MM_START_2 &&
7339 element_rnd <= EL_MM_END_2 ?
7340 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7342 element_rnd >= EL_CHAR_START &&
7343 element_rnd <= EL_CHAR_END ?
7344 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7346 element_rnd >= EL_MM_RUNTIME_START &&
7347 element_rnd <= EL_MM_RUNTIME_END ?
7348 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7350 element_rnd >= EL_MM_DUMMY_START &&
7351 element_rnd <= EL_MM_DUMMY_END ?
7352 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7354 EL_MM_EMPTY_NATIVE);
7357 int map_element_MM_to_RND(int element_mm)
7359 return (element_mm == EL_MM_EMPTY_NATIVE ||
7360 element_mm == EL_DF_EMPTY_NATIVE ?
7363 element_mm >= EL_MM_START_1_NATIVE &&
7364 element_mm <= EL_MM_END_1_NATIVE ?
7365 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7367 element_mm >= EL_MM_START_2_NATIVE &&
7368 element_mm <= EL_MM_END_2_NATIVE ?
7369 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7371 element_mm >= EL_MM_CHAR_START_NATIVE &&
7372 element_mm <= EL_MM_CHAR_END_NATIVE ?
7373 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7375 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7376 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7377 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7379 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7380 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7381 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7386 int map_mm_wall_element(int element)
7388 return (element >= EL_MM_STEEL_WALL_START &&
7389 element <= EL_MM_STEEL_WALL_END ?
7392 element >= EL_MM_WOODEN_WALL_START &&
7393 element <= EL_MM_WOODEN_WALL_END ?
7396 element >= EL_MM_ICE_WALL_START &&
7397 element <= EL_MM_ICE_WALL_END ?
7400 element >= EL_MM_AMOEBA_WALL_START &&
7401 element <= EL_MM_AMOEBA_WALL_END ?
7404 element >= EL_DF_STEEL_WALL_START &&
7405 element <= EL_DF_STEEL_WALL_END ?
7408 element >= EL_DF_WOODEN_WALL_START &&
7409 element <= EL_DF_WOODEN_WALL_END ?
7415 int map_mm_wall_element_editor(int element)
7419 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
7420 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
7421 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
7422 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
7423 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
7424 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
7426 default: return element;
7430 int get_next_element(int element)
7434 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7435 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7436 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7437 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7438 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7439 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7440 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7441 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7442 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7443 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7444 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7446 default: return element;
7450 int el2img_mm(int element_mm)
7452 return el2img(map_element_MM_to_RND(element_mm));
7455 int el_act_dir2img(int element, int action, int direction)
7457 element = GFX_ELEMENT(element);
7458 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7460 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7461 return element_info[element].direction_graphic[action][direction];
7464 static int el_act_dir2crm(int element, int action, int direction)
7466 element = GFX_ELEMENT(element);
7467 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7469 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7470 return element_info[element].direction_crumbled[action][direction];
7473 int el_act2img(int element, int action)
7475 element = GFX_ELEMENT(element);
7477 return element_info[element].graphic[action];
7480 int el_act2crm(int element, int action)
7482 element = GFX_ELEMENT(element);
7484 return element_info[element].crumbled[action];
7487 int el_dir2img(int element, int direction)
7489 element = GFX_ELEMENT(element);
7491 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7494 int el2baseimg(int element)
7496 return element_info[element].graphic[ACTION_DEFAULT];
7499 int el2img(int element)
7501 element = GFX_ELEMENT(element);
7503 return element_info[element].graphic[ACTION_DEFAULT];
7506 int el2edimg(int element)
7508 element = GFX_ELEMENT(element);
7510 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7513 int el2preimg(int element)
7515 element = GFX_ELEMENT(element);
7517 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7520 int el2panelimg(int element)
7522 element = GFX_ELEMENT(element);
7524 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7527 int font2baseimg(int font_nr)
7529 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7532 int getBeltNrFromBeltElement(int element)
7534 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7535 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7536 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
7539 int getBeltNrFromBeltActiveElement(int element)
7541 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
7542 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
7543 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
7546 int getBeltNrFromBeltSwitchElement(int element)
7548 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
7549 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
7550 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
7553 int getBeltDirNrFromBeltElement(int element)
7555 static int belt_base_element[4] =
7557 EL_CONVEYOR_BELT_1_LEFT,
7558 EL_CONVEYOR_BELT_2_LEFT,
7559 EL_CONVEYOR_BELT_3_LEFT,
7560 EL_CONVEYOR_BELT_4_LEFT
7563 int belt_nr = getBeltNrFromBeltElement(element);
7564 int belt_dir_nr = element - belt_base_element[belt_nr];
7566 return (belt_dir_nr % 3);
7569 int getBeltDirNrFromBeltSwitchElement(int element)
7571 static int belt_base_element[4] =
7573 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7574 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7575 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7576 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7579 int belt_nr = getBeltNrFromBeltSwitchElement(element);
7580 int belt_dir_nr = element - belt_base_element[belt_nr];
7582 return (belt_dir_nr % 3);
7585 int getBeltDirFromBeltElement(int element)
7587 static int belt_move_dir[3] =
7594 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7596 return belt_move_dir[belt_dir_nr];
7599 int getBeltDirFromBeltSwitchElement(int element)
7601 static int belt_move_dir[3] =
7608 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7610 return belt_move_dir[belt_dir_nr];
7613 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7615 static int belt_base_element[4] =
7617 EL_CONVEYOR_BELT_1_LEFT,
7618 EL_CONVEYOR_BELT_2_LEFT,
7619 EL_CONVEYOR_BELT_3_LEFT,
7620 EL_CONVEYOR_BELT_4_LEFT
7623 return belt_base_element[belt_nr] + belt_dir_nr;
7626 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7628 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7630 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7633 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7635 static int belt_base_element[4] =
7637 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7638 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7639 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7640 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7643 return belt_base_element[belt_nr] + belt_dir_nr;
7646 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7648 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7650 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7653 boolean getTeamMode_EM()
7655 return game.team_mode;
7658 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7660 int game_frame_delay_value;
7662 game_frame_delay_value =
7663 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7664 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7667 if (tape.playing && tape.warp_forward && !tape.pausing)
7668 game_frame_delay_value = 0;
7670 return game_frame_delay_value;
7673 unsigned int InitRND(int seed)
7675 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7676 return InitEngineRandom_EM(seed);
7677 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7678 return InitEngineRandom_SP(seed);
7679 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
7680 return InitEngineRandom_MM(seed);
7682 return InitEngineRandom_RND(seed);
7685 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7686 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7688 inline static int get_effective_element_EM(int tile, int frame_em)
7690 int element = object_mapping[tile].element_rnd;
7691 int action = object_mapping[tile].action;
7692 boolean is_backside = object_mapping[tile].is_backside;
7693 boolean action_removing = (action == ACTION_DIGGING ||
7694 action == ACTION_SNAPPING ||
7695 action == ACTION_COLLECTING);
7701 case Yacid_splash_eB:
7702 case Yacid_splash_wB:
7703 return (frame_em > 5 ? EL_EMPTY : element);
7709 else /* frame_em == 7 */
7713 case Yacid_splash_eB:
7714 case Yacid_splash_wB:
7717 case Yemerald_stone:
7720 case Ydiamond_stone:
7724 case Xdrip_stretchB:
7743 case Xsand_stonein_1:
7744 case Xsand_stonein_2:
7745 case Xsand_stonein_3:
7746 case Xsand_stonein_4:
7750 return (is_backside || action_removing ? EL_EMPTY : element);
7755 inline static boolean check_linear_animation_EM(int tile)
7759 case Xsand_stonesand_1:
7760 case Xsand_stonesand_quickout_1:
7761 case Xsand_sandstone_1:
7762 case Xsand_stonein_1:
7763 case Xsand_stoneout_1:
7782 case Yacid_splash_eB:
7783 case Yacid_splash_wB:
7784 case Yemerald_stone:
7791 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7792 boolean has_crumbled_graphics,
7793 int crumbled, int sync_frame)
7795 /* if element can be crumbled, but certain action graphics are just empty
7796 space (like instantly snapping sand to empty space in 1 frame), do not
7797 treat these empty space graphics as crumbled graphics in EMC engine */
7798 if (crumbled == IMG_EMPTY_SPACE)
7799 has_crumbled_graphics = FALSE;
7801 if (has_crumbled_graphics)
7803 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7804 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7805 g_crumbled->anim_delay,
7806 g_crumbled->anim_mode,
7807 g_crumbled->anim_start_frame,
7810 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7811 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7813 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7814 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
7816 g_em->has_crumbled_graphics = TRUE;
7820 g_em->crumbled_bitmap = NULL;
7821 g_em->crumbled_src_x = 0;
7822 g_em->crumbled_src_y = 0;
7823 g_em->crumbled_border_size = 0;
7824 g_em->crumbled_tile_size = 0;
7826 g_em->has_crumbled_graphics = FALSE;
7830 void ResetGfxAnimation_EM(int x, int y, int tile)
7835 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7836 int tile, int frame_em, int x, int y)
7838 int action = object_mapping[tile].action;
7839 int direction = object_mapping[tile].direction;
7840 int effective_element = get_effective_element_EM(tile, frame_em);
7841 int graphic = (direction == MV_NONE ?
7842 el_act2img(effective_element, action) :
7843 el_act_dir2img(effective_element, action, direction));
7844 struct GraphicInfo *g = &graphic_info[graphic];
7846 boolean action_removing = (action == ACTION_DIGGING ||
7847 action == ACTION_SNAPPING ||
7848 action == ACTION_COLLECTING);
7849 boolean action_moving = (action == ACTION_FALLING ||
7850 action == ACTION_MOVING ||
7851 action == ACTION_PUSHING ||
7852 action == ACTION_EATING ||
7853 action == ACTION_FILLING ||
7854 action == ACTION_EMPTYING);
7855 boolean action_falling = (action == ACTION_FALLING ||
7856 action == ACTION_FILLING ||
7857 action == ACTION_EMPTYING);
7859 /* special case: graphic uses "2nd movement tile" and has defined
7860 7 frames for movement animation (or less) => use default graphic
7861 for last (8th) frame which ends the movement animation */
7862 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7864 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7865 graphic = (direction == MV_NONE ?
7866 el_act2img(effective_element, action) :
7867 el_act_dir2img(effective_element, action, direction));
7869 g = &graphic_info[graphic];
7872 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7876 else if (action_moving)
7878 boolean is_backside = object_mapping[tile].is_backside;
7882 int direction = object_mapping[tile].direction;
7883 int move_dir = (action_falling ? MV_DOWN : direction);
7888 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7889 if (g->double_movement && frame_em == 0)
7893 if (move_dir == MV_LEFT)
7894 GfxFrame[x - 1][y] = GfxFrame[x][y];
7895 else if (move_dir == MV_RIGHT)
7896 GfxFrame[x + 1][y] = GfxFrame[x][y];
7897 else if (move_dir == MV_UP)
7898 GfxFrame[x][y - 1] = GfxFrame[x][y];
7899 else if (move_dir == MV_DOWN)
7900 GfxFrame[x][y + 1] = GfxFrame[x][y];
7907 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7908 if (tile == Xsand_stonesand_quickout_1 ||
7909 tile == Xsand_stonesand_quickout_2)
7913 if (graphic_info[graphic].anim_global_sync)
7914 sync_frame = FrameCounter;
7915 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7916 sync_frame = GfxFrame[x][y];
7918 sync_frame = 0; /* playfield border (pseudo steel) */
7920 SetRandomAnimationValue(x, y);
7922 int frame = getAnimationFrame(g->anim_frames,
7925 g->anim_start_frame,
7928 g_em->unique_identifier =
7929 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7932 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7933 int tile, int frame_em, int x, int y)
7935 int action = object_mapping[tile].action;
7936 int direction = object_mapping[tile].direction;
7937 boolean is_backside = object_mapping[tile].is_backside;
7938 int effective_element = get_effective_element_EM(tile, frame_em);
7939 int effective_action = action;
7940 int graphic = (direction == MV_NONE ?
7941 el_act2img(effective_element, effective_action) :
7942 el_act_dir2img(effective_element, effective_action,
7944 int crumbled = (direction == MV_NONE ?
7945 el_act2crm(effective_element, effective_action) :
7946 el_act_dir2crm(effective_element, effective_action,
7948 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7949 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7950 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7951 struct GraphicInfo *g = &graphic_info[graphic];
7954 /* special case: graphic uses "2nd movement tile" and has defined
7955 7 frames for movement animation (or less) => use default graphic
7956 for last (8th) frame which ends the movement animation */
7957 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7959 effective_action = ACTION_DEFAULT;
7960 graphic = (direction == MV_NONE ?
7961 el_act2img(effective_element, effective_action) :
7962 el_act_dir2img(effective_element, effective_action,
7964 crumbled = (direction == MV_NONE ?
7965 el_act2crm(effective_element, effective_action) :
7966 el_act_dir2crm(effective_element, effective_action,
7969 g = &graphic_info[graphic];
7972 if (graphic_info[graphic].anim_global_sync)
7973 sync_frame = FrameCounter;
7974 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7975 sync_frame = GfxFrame[x][y];
7977 sync_frame = 0; /* playfield border (pseudo steel) */
7979 SetRandomAnimationValue(x, y);
7981 int frame = getAnimationFrame(g->anim_frames,
7984 g->anim_start_frame,
7987 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7988 g->double_movement && is_backside);
7990 /* (updating the "crumbled" graphic definitions is probably not really needed,
7991 as animations for crumbled graphics can't be longer than one EMC cycle) */
7992 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7996 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7997 int player_nr, int anim, int frame_em)
7999 int element = player_mapping[player_nr][anim].element_rnd;
8000 int action = player_mapping[player_nr][anim].action;
8001 int direction = player_mapping[player_nr][anim].direction;
8002 int graphic = (direction == MV_NONE ?
8003 el_act2img(element, action) :
8004 el_act_dir2img(element, action, direction));
8005 struct GraphicInfo *g = &graphic_info[graphic];
8008 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8010 stored_player[player_nr].StepFrame = frame_em;
8012 sync_frame = stored_player[player_nr].Frame;
8014 int frame = getAnimationFrame(g->anim_frames,
8017 g->anim_start_frame,
8020 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8021 &g_em->src_x, &g_em->src_y, FALSE);
8024 void InitGraphicInfo_EM(void)
8029 int num_em_gfx_errors = 0;
8031 if (graphic_info_em_object[0][0].bitmap == NULL)
8033 /* EM graphics not yet initialized in em_open_all() */
8038 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8041 /* always start with reliable default values */
8042 for (i = 0; i < TILE_MAX; i++)
8044 object_mapping[i].element_rnd = EL_UNKNOWN;
8045 object_mapping[i].is_backside = FALSE;
8046 object_mapping[i].action = ACTION_DEFAULT;
8047 object_mapping[i].direction = MV_NONE;
8050 /* always start with reliable default values */
8051 for (p = 0; p < MAX_PLAYERS; p++)
8053 for (i = 0; i < SPR_MAX; i++)
8055 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8056 player_mapping[p][i].action = ACTION_DEFAULT;
8057 player_mapping[p][i].direction = MV_NONE;
8061 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8063 int e = em_object_mapping_list[i].element_em;
8065 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8066 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8068 if (em_object_mapping_list[i].action != -1)
8069 object_mapping[e].action = em_object_mapping_list[i].action;
8071 if (em_object_mapping_list[i].direction != -1)
8072 object_mapping[e].direction =
8073 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8076 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8078 int a = em_player_mapping_list[i].action_em;
8079 int p = em_player_mapping_list[i].player_nr;
8081 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8083 if (em_player_mapping_list[i].action != -1)
8084 player_mapping[p][a].action = em_player_mapping_list[i].action;
8086 if (em_player_mapping_list[i].direction != -1)
8087 player_mapping[p][a].direction =
8088 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8091 for (i = 0; i < TILE_MAX; i++)
8093 int element = object_mapping[i].element_rnd;
8094 int action = object_mapping[i].action;
8095 int direction = object_mapping[i].direction;
8096 boolean is_backside = object_mapping[i].is_backside;
8097 boolean action_exploding = ((action == ACTION_EXPLODING ||
8098 action == ACTION_SMASHED_BY_ROCK ||
8099 action == ACTION_SMASHED_BY_SPRING) &&
8100 element != EL_DIAMOND);
8101 boolean action_active = (action == ACTION_ACTIVE);
8102 boolean action_other = (action == ACTION_OTHER);
8104 for (j = 0; j < 8; j++)
8106 int effective_element = get_effective_element_EM(i, j);
8107 int effective_action = (j < 7 ? action :
8108 i == Xdrip_stretch ? action :
8109 i == Xdrip_stretchB ? action :
8110 i == Ydrip_s1 ? action :
8111 i == Ydrip_s1B ? action :
8112 i == Xball_1B ? action :
8113 i == Xball_2 ? action :
8114 i == Xball_2B ? action :
8115 i == Yball_eat ? action :
8116 i == Ykey_1_eat ? action :
8117 i == Ykey_2_eat ? action :
8118 i == Ykey_3_eat ? action :
8119 i == Ykey_4_eat ? action :
8120 i == Ykey_5_eat ? action :
8121 i == Ykey_6_eat ? action :
8122 i == Ykey_7_eat ? action :
8123 i == Ykey_8_eat ? action :
8124 i == Ylenses_eat ? action :
8125 i == Ymagnify_eat ? action :
8126 i == Ygrass_eat ? action :
8127 i == Ydirt_eat ? action :
8128 i == Xsand_stonein_1 ? action :
8129 i == Xsand_stonein_2 ? action :
8130 i == Xsand_stonein_3 ? action :
8131 i == Xsand_stonein_4 ? action :
8132 i == Xsand_stoneout_1 ? action :
8133 i == Xsand_stoneout_2 ? action :
8134 i == Xboom_android ? ACTION_EXPLODING :
8135 action_exploding ? ACTION_EXPLODING :
8136 action_active ? action :
8137 action_other ? action :
8139 int graphic = (el_act_dir2img(effective_element, effective_action,
8141 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8143 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8144 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8145 boolean has_action_graphics = (graphic != base_graphic);
8146 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8147 struct GraphicInfo *g = &graphic_info[graphic];
8148 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8151 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
8152 boolean special_animation = (action != ACTION_DEFAULT &&
8153 g->anim_frames == 3 &&
8154 g->anim_delay == 2 &&
8155 g->anim_mode & ANIM_LINEAR);
8156 int sync_frame = (i == Xdrip_stretch ? 7 :
8157 i == Xdrip_stretchB ? 7 :
8158 i == Ydrip_s2 ? j + 8 :
8159 i == Ydrip_s2B ? j + 8 :
8168 i == Xfake_acid_1 ? 0 :
8169 i == Xfake_acid_2 ? 10 :
8170 i == Xfake_acid_3 ? 20 :
8171 i == Xfake_acid_4 ? 30 :
8172 i == Xfake_acid_5 ? 40 :
8173 i == Xfake_acid_6 ? 50 :
8174 i == Xfake_acid_7 ? 60 :
8175 i == Xfake_acid_8 ? 70 :
8177 i == Xball_2B ? j + 8 :
8178 i == Yball_eat ? j + 1 :
8179 i == Ykey_1_eat ? j + 1 :
8180 i == Ykey_2_eat ? j + 1 :
8181 i == Ykey_3_eat ? j + 1 :
8182 i == Ykey_4_eat ? j + 1 :
8183 i == Ykey_5_eat ? j + 1 :
8184 i == Ykey_6_eat ? j + 1 :
8185 i == Ykey_7_eat ? j + 1 :
8186 i == Ykey_8_eat ? j + 1 :
8187 i == Ylenses_eat ? j + 1 :
8188 i == Ymagnify_eat ? j + 1 :
8189 i == Ygrass_eat ? j + 1 :
8190 i == Ydirt_eat ? j + 1 :
8191 i == Xamoeba_1 ? 0 :
8192 i == Xamoeba_2 ? 1 :
8193 i == Xamoeba_3 ? 2 :
8194 i == Xamoeba_4 ? 3 :
8195 i == Xamoeba_5 ? 0 :
8196 i == Xamoeba_6 ? 1 :
8197 i == Xamoeba_7 ? 2 :
8198 i == Xamoeba_8 ? 3 :
8199 i == Xexit_2 ? j + 8 :
8200 i == Xexit_3 ? j + 16 :
8201 i == Xdynamite_1 ? 0 :
8202 i == Xdynamite_2 ? 8 :
8203 i == Xdynamite_3 ? 16 :
8204 i == Xdynamite_4 ? 24 :
8205 i == Xsand_stonein_1 ? j + 1 :
8206 i == Xsand_stonein_2 ? j + 9 :
8207 i == Xsand_stonein_3 ? j + 17 :
8208 i == Xsand_stonein_4 ? j + 25 :
8209 i == Xsand_stoneout_1 && j == 0 ? 0 :
8210 i == Xsand_stoneout_1 && j == 1 ? 0 :
8211 i == Xsand_stoneout_1 && j == 2 ? 1 :
8212 i == Xsand_stoneout_1 && j == 3 ? 2 :
8213 i == Xsand_stoneout_1 && j == 4 ? 2 :
8214 i == Xsand_stoneout_1 && j == 5 ? 3 :
8215 i == Xsand_stoneout_1 && j == 6 ? 4 :
8216 i == Xsand_stoneout_1 && j == 7 ? 4 :
8217 i == Xsand_stoneout_2 && j == 0 ? 5 :
8218 i == Xsand_stoneout_2 && j == 1 ? 6 :
8219 i == Xsand_stoneout_2 && j == 2 ? 7 :
8220 i == Xsand_stoneout_2 && j == 3 ? 8 :
8221 i == Xsand_stoneout_2 && j == 4 ? 9 :
8222 i == Xsand_stoneout_2 && j == 5 ? 11 :
8223 i == Xsand_stoneout_2 && j == 6 ? 13 :
8224 i == Xsand_stoneout_2 && j == 7 ? 15 :
8225 i == Xboom_bug && j == 1 ? 2 :
8226 i == Xboom_bug && j == 2 ? 2 :
8227 i == Xboom_bug && j == 3 ? 4 :
8228 i == Xboom_bug && j == 4 ? 4 :
8229 i == Xboom_bug && j == 5 ? 2 :
8230 i == Xboom_bug && j == 6 ? 2 :
8231 i == Xboom_bug && j == 7 ? 0 :
8232 i == Xboom_bomb && j == 1 ? 2 :
8233 i == Xboom_bomb && j == 2 ? 2 :
8234 i == Xboom_bomb && j == 3 ? 4 :
8235 i == Xboom_bomb && j == 4 ? 4 :
8236 i == Xboom_bomb && j == 5 ? 2 :
8237 i == Xboom_bomb && j == 6 ? 2 :
8238 i == Xboom_bomb && j == 7 ? 0 :
8239 i == Xboom_android && j == 7 ? 6 :
8240 i == Xboom_1 && j == 1 ? 2 :
8241 i == Xboom_1 && j == 2 ? 2 :
8242 i == Xboom_1 && j == 3 ? 4 :
8243 i == Xboom_1 && j == 4 ? 4 :
8244 i == Xboom_1 && j == 5 ? 6 :
8245 i == Xboom_1 && j == 6 ? 6 :
8246 i == Xboom_1 && j == 7 ? 8 :
8247 i == Xboom_2 && j == 0 ? 8 :
8248 i == Xboom_2 && j == 1 ? 8 :
8249 i == Xboom_2 && j == 2 ? 10 :
8250 i == Xboom_2 && j == 3 ? 10 :
8251 i == Xboom_2 && j == 4 ? 10 :
8252 i == Xboom_2 && j == 5 ? 12 :
8253 i == Xboom_2 && j == 6 ? 12 :
8254 i == Xboom_2 && j == 7 ? 12 :
8255 special_animation && j == 4 ? 3 :
8256 effective_action != action ? 0 :
8260 Bitmap *debug_bitmap = g_em->bitmap;
8261 int debug_src_x = g_em->src_x;
8262 int debug_src_y = g_em->src_y;
8265 int frame = getAnimationFrame(g->anim_frames,
8268 g->anim_start_frame,
8271 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8272 g->double_movement && is_backside);
8274 g_em->bitmap = src_bitmap;
8275 g_em->src_x = src_x;
8276 g_em->src_y = src_y;
8277 g_em->src_offset_x = 0;
8278 g_em->src_offset_y = 0;
8279 g_em->dst_offset_x = 0;
8280 g_em->dst_offset_y = 0;
8281 g_em->width = TILEX;
8282 g_em->height = TILEY;
8284 g_em->preserve_background = FALSE;
8286 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8289 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8290 effective_action == ACTION_MOVING ||
8291 effective_action == ACTION_PUSHING ||
8292 effective_action == ACTION_EATING)) ||
8293 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8294 effective_action == ACTION_EMPTYING)))
8297 (effective_action == ACTION_FALLING ||
8298 effective_action == ACTION_FILLING ||
8299 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8300 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8301 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8302 int num_steps = (i == Ydrip_s1 ? 16 :
8303 i == Ydrip_s1B ? 16 :
8304 i == Ydrip_s2 ? 16 :
8305 i == Ydrip_s2B ? 16 :
8306 i == Xsand_stonein_1 ? 32 :
8307 i == Xsand_stonein_2 ? 32 :
8308 i == Xsand_stonein_3 ? 32 :
8309 i == Xsand_stonein_4 ? 32 :
8310 i == Xsand_stoneout_1 ? 16 :
8311 i == Xsand_stoneout_2 ? 16 : 8);
8312 int cx = ABS(dx) * (TILEX / num_steps);
8313 int cy = ABS(dy) * (TILEY / num_steps);
8314 int step_frame = (i == Ydrip_s2 ? j + 8 :
8315 i == Ydrip_s2B ? j + 8 :
8316 i == Xsand_stonein_2 ? j + 8 :
8317 i == Xsand_stonein_3 ? j + 16 :
8318 i == Xsand_stonein_4 ? j + 24 :
8319 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8320 int step = (is_backside ? step_frame : num_steps - step_frame);
8322 if (is_backside) /* tile where movement starts */
8324 if (dx < 0 || dy < 0)
8326 g_em->src_offset_x = cx * step;
8327 g_em->src_offset_y = cy * step;
8331 g_em->dst_offset_x = cx * step;
8332 g_em->dst_offset_y = cy * step;
8335 else /* tile where movement ends */
8337 if (dx < 0 || dy < 0)
8339 g_em->dst_offset_x = cx * step;
8340 g_em->dst_offset_y = cy * step;
8344 g_em->src_offset_x = cx * step;
8345 g_em->src_offset_y = cy * step;
8349 g_em->width = TILEX - cx * step;
8350 g_em->height = TILEY - cy * step;
8353 /* create unique graphic identifier to decide if tile must be redrawn */
8354 /* bit 31 - 16 (16 bit): EM style graphic
8355 bit 15 - 12 ( 4 bit): EM style frame
8356 bit 11 - 6 ( 6 bit): graphic width
8357 bit 5 - 0 ( 6 bit): graphic height */
8358 g_em->unique_identifier =
8359 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8363 /* skip check for EMC elements not contained in original EMC artwork */
8364 if (element == EL_EMC_FAKE_ACID)
8367 if (g_em->bitmap != debug_bitmap ||
8368 g_em->src_x != debug_src_x ||
8369 g_em->src_y != debug_src_y ||
8370 g_em->src_offset_x != 0 ||
8371 g_em->src_offset_y != 0 ||
8372 g_em->dst_offset_x != 0 ||
8373 g_em->dst_offset_y != 0 ||
8374 g_em->width != TILEX ||
8375 g_em->height != TILEY)
8377 static int last_i = -1;
8385 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8386 i, element, element_info[element].token_name,
8387 element_action_info[effective_action].suffix, direction);
8389 if (element != effective_element)
8390 printf(" [%d ('%s')]",
8392 element_info[effective_element].token_name);
8396 if (g_em->bitmap != debug_bitmap)
8397 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8398 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8400 if (g_em->src_x != debug_src_x ||
8401 g_em->src_y != debug_src_y)
8402 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8403 j, (is_backside ? 'B' : 'F'),
8404 g_em->src_x, g_em->src_y,
8405 g_em->src_x / 32, g_em->src_y / 32,
8406 debug_src_x, debug_src_y,
8407 debug_src_x / 32, debug_src_y / 32);
8409 if (g_em->src_offset_x != 0 ||
8410 g_em->src_offset_y != 0 ||
8411 g_em->dst_offset_x != 0 ||
8412 g_em->dst_offset_y != 0)
8413 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8415 g_em->src_offset_x, g_em->src_offset_y,
8416 g_em->dst_offset_x, g_em->dst_offset_y);
8418 if (g_em->width != TILEX ||
8419 g_em->height != TILEY)
8420 printf(" %d (%d): size %d,%d should be %d,%d\n",
8422 g_em->width, g_em->height, TILEX, TILEY);
8424 num_em_gfx_errors++;
8431 for (i = 0; i < TILE_MAX; i++)
8433 for (j = 0; j < 8; j++)
8435 int element = object_mapping[i].element_rnd;
8436 int action = object_mapping[i].action;
8437 int direction = object_mapping[i].direction;
8438 boolean is_backside = object_mapping[i].is_backside;
8439 int graphic_action = el_act_dir2img(element, action, direction);
8440 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8442 if ((action == ACTION_SMASHED_BY_ROCK ||
8443 action == ACTION_SMASHED_BY_SPRING ||
8444 action == ACTION_EATING) &&
8445 graphic_action == graphic_default)
8447 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8448 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8449 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8450 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8453 /* no separate animation for "smashed by rock" -- use rock instead */
8454 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8455 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8457 g_em->bitmap = g_xx->bitmap;
8458 g_em->src_x = g_xx->src_x;
8459 g_em->src_y = g_xx->src_y;
8460 g_em->src_offset_x = g_xx->src_offset_x;
8461 g_em->src_offset_y = g_xx->src_offset_y;
8462 g_em->dst_offset_x = g_xx->dst_offset_x;
8463 g_em->dst_offset_y = g_xx->dst_offset_y;
8464 g_em->width = g_xx->width;
8465 g_em->height = g_xx->height;
8466 g_em->unique_identifier = g_xx->unique_identifier;
8469 g_em->preserve_background = TRUE;
8474 for (p = 0; p < MAX_PLAYERS; p++)
8476 for (i = 0; i < SPR_MAX; i++)
8478 int element = player_mapping[p][i].element_rnd;
8479 int action = player_mapping[p][i].action;
8480 int direction = player_mapping[p][i].direction;
8482 for (j = 0; j < 8; j++)
8484 int effective_element = element;
8485 int effective_action = action;
8486 int graphic = (direction == MV_NONE ?
8487 el_act2img(effective_element, effective_action) :
8488 el_act_dir2img(effective_element, effective_action,
8490 struct GraphicInfo *g = &graphic_info[graphic];
8491 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8497 Bitmap *debug_bitmap = g_em->bitmap;
8498 int debug_src_x = g_em->src_x;
8499 int debug_src_y = g_em->src_y;
8502 int frame = getAnimationFrame(g->anim_frames,
8505 g->anim_start_frame,
8508 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8510 g_em->bitmap = src_bitmap;
8511 g_em->src_x = src_x;
8512 g_em->src_y = src_y;
8513 g_em->src_offset_x = 0;
8514 g_em->src_offset_y = 0;
8515 g_em->dst_offset_x = 0;
8516 g_em->dst_offset_y = 0;
8517 g_em->width = TILEX;
8518 g_em->height = TILEY;
8522 /* skip check for EMC elements not contained in original EMC artwork */
8523 if (element == EL_PLAYER_3 ||
8524 element == EL_PLAYER_4)
8527 if (g_em->bitmap != debug_bitmap ||
8528 g_em->src_x != debug_src_x ||
8529 g_em->src_y != debug_src_y)
8531 static int last_i = -1;
8539 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
8540 p, i, element, element_info[element].token_name,
8541 element_action_info[effective_action].suffix, direction);
8543 if (element != effective_element)
8544 printf(" [%d ('%s')]",
8546 element_info[effective_element].token_name);
8550 if (g_em->bitmap != debug_bitmap)
8551 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
8552 j, (int)(g_em->bitmap), (int)(debug_bitmap));
8554 if (g_em->src_x != debug_src_x ||
8555 g_em->src_y != debug_src_y)
8556 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8558 g_em->src_x, g_em->src_y,
8559 g_em->src_x / 32, g_em->src_y / 32,
8560 debug_src_x, debug_src_y,
8561 debug_src_x / 32, debug_src_y / 32);
8563 num_em_gfx_errors++;
8573 printf("::: [%d errors found]\n", num_em_gfx_errors);
8579 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
8580 boolean any_player_moving,
8581 boolean any_player_snapping,
8582 boolean any_player_dropping)
8584 if (frame == 0 && !any_player_dropping)
8586 if (!local_player->was_waiting)
8588 if (!CheckSaveEngineSnapshotToList())
8591 local_player->was_waiting = TRUE;
8594 else if (any_player_moving || any_player_snapping || any_player_dropping)
8596 local_player->was_waiting = FALSE;
8600 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8601 boolean murphy_is_dropping)
8603 if (murphy_is_waiting)
8605 if (!local_player->was_waiting)
8607 if (!CheckSaveEngineSnapshotToList())
8610 local_player->was_waiting = TRUE;
8615 local_player->was_waiting = FALSE;
8619 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8620 boolean any_player_moving,
8621 boolean any_player_snapping,
8622 boolean any_player_dropping)
8624 if (tape.single_step && tape.recording && !tape.pausing)
8625 if (frame == 0 && !any_player_dropping)
8626 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8628 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8629 any_player_snapping, any_player_dropping);
8632 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8633 boolean murphy_is_dropping)
8635 boolean murphy_starts_dropping = FALSE;
8638 for (i = 0; i < MAX_PLAYERS; i++)
8639 if (stored_player[i].force_dropping)
8640 murphy_starts_dropping = TRUE;
8642 if (tape.single_step && tape.recording && !tape.pausing)
8643 if (murphy_is_waiting && !murphy_starts_dropping)
8644 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8646 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8649 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8650 int graphic, int sync_frame, int x, int y)
8652 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8654 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8657 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8659 return (IS_NEXT_FRAME(sync_frame, graphic));
8662 int getGraphicInfo_Delay(int graphic)
8664 return graphic_info[graphic].anim_delay;
8667 void PlayMenuSoundExt(int sound)
8669 if (sound == SND_UNDEFINED)
8672 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8673 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8676 if (IS_LOOP_SOUND(sound))
8677 PlaySoundLoop(sound);
8682 void PlayMenuSound()
8684 PlayMenuSoundExt(menu.sound[game_status]);
8687 void PlayMenuSoundStereo(int sound, int stereo_position)
8689 if (sound == SND_UNDEFINED)
8692 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8693 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8696 if (IS_LOOP_SOUND(sound))
8697 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8699 PlaySoundStereo(sound, stereo_position);
8702 void PlayMenuSoundIfLoopExt(int sound)
8704 if (sound == SND_UNDEFINED)
8707 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8708 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8711 if (IS_LOOP_SOUND(sound))
8712 PlaySoundLoop(sound);
8715 void PlayMenuSoundIfLoop()
8717 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8720 void PlayMenuMusicExt(int music)
8722 if (music == MUS_UNDEFINED)
8725 if (!setup.sound_music)
8731 void PlayMenuMusic()
8733 char *curr_music = getCurrentlyPlayingMusicFilename();
8734 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8736 if (!strEqual(curr_music, next_music))
8737 PlayMenuMusicExt(menu.music[game_status]);
8740 void PlayMenuSoundsAndMusic()
8746 static void FadeMenuSounds()
8751 static void FadeMenuMusic()
8753 char *curr_music = getCurrentlyPlayingMusicFilename();
8754 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8756 if (!strEqual(curr_music, next_music))
8760 void FadeMenuSoundsAndMusic()
8766 void PlaySoundActivating()
8769 PlaySound(SND_MENU_ITEM_ACTIVATING);
8773 void PlaySoundSelecting()
8776 PlaySound(SND_MENU_ITEM_SELECTING);
8780 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8782 boolean change_fullscreen = (setup.fullscreen !=
8783 video.fullscreen_enabled);
8784 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8785 setup.window_scaling_percent !=
8786 video.window_scaling_percent);
8788 if (change_window_scaling_percent && video.fullscreen_enabled)
8791 if (!change_window_scaling_percent && !video.fullscreen_available)
8794 #if defined(TARGET_SDL2)
8795 if (change_window_scaling_percent)
8797 SDLSetWindowScaling(setup.window_scaling_percent);
8801 else if (change_fullscreen)
8803 SDLSetWindowFullscreen(setup.fullscreen);
8805 /* set setup value according to successfully changed fullscreen mode */
8806 setup.fullscreen = video.fullscreen_enabled;
8812 if (change_fullscreen ||
8813 change_window_scaling_percent)
8815 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8817 /* save backbuffer content which gets lost when toggling fullscreen mode */
8818 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8820 if (change_window_scaling_percent)
8822 /* keep window mode, but change window scaling */
8823 video.fullscreen_enabled = TRUE; /* force new window scaling */
8826 /* toggle fullscreen */
8827 ChangeVideoModeIfNeeded(setup.fullscreen);
8829 /* set setup value according to successfully changed fullscreen mode */
8830 setup.fullscreen = video.fullscreen_enabled;
8832 /* restore backbuffer content from temporary backbuffer backup bitmap */
8833 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8835 FreeBitmap(tmp_backbuffer);
8837 /* update visible window/screen */
8838 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8842 void JoinRectangles(int *x, int *y, int *width, int *height,
8843 int x2, int y2, int width2, int height2)
8845 // do not join with "off-screen" rectangle
8846 if (x2 == -1 || y2 == -1)
8851 *width = MAX(*width, width2);
8852 *height = MAX(*height, height2);
8855 void SetAnimStatus(int anim_status_new)
8857 if (anim_status_new == GAME_MODE_MAIN)
8858 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
8859 else if (anim_status_new == GAME_MODE_SCORES)
8860 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
8862 global.anim_status_next = anim_status_new;
8864 // directly set screen modes that are entered without fading
8865 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
8866 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
8867 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
8868 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
8869 global.anim_status = global.anim_status_next;
8872 void SetGameStatus(int game_status_new)
8874 if (game_status_new != game_status)
8875 game_status_last_screen = game_status;
8877 game_status = game_status_new;
8879 SetAnimStatus(game_status_new);
8882 void SetFontStatus(int game_status_new)
8884 static int last_game_status = -1;
8886 if (game_status_new != -1)
8888 // set game status for font use after storing last game status
8889 last_game_status = game_status;
8890 game_status = game_status_new;
8894 // reset game status after font use from last stored game status
8895 game_status = last_game_status;
8899 void ResetFontStatus()
8904 void ChangeViewportPropertiesIfNeeded()
8906 int gfx_game_mode = game_status;
8907 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8909 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
8910 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8911 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8912 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8913 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8914 int new_win_xsize = vp_window->width;
8915 int new_win_ysize = vp_window->height;
8916 int border_size = vp_playfield->border_size;
8917 int new_sx = vp_playfield->x + border_size;
8918 int new_sy = vp_playfield->y + border_size;
8919 int new_sxsize = vp_playfield->width - 2 * border_size;
8920 int new_sysize = vp_playfield->height - 2 * border_size;
8921 int new_real_sx = vp_playfield->x;
8922 int new_real_sy = vp_playfield->y;
8923 int new_full_sxsize = vp_playfield->width;
8924 int new_full_sysize = vp_playfield->height;
8925 int new_dx = vp_door_1->x;
8926 int new_dy = vp_door_1->y;
8927 int new_dxsize = vp_door_1->width;
8928 int new_dysize = vp_door_1->height;
8929 int new_vx = vp_door_2->x;
8930 int new_vy = vp_door_2->y;
8931 int new_vxsize = vp_door_2->width;
8932 int new_vysize = vp_door_2->height;
8933 int new_ex = vp_door_3->x;
8934 int new_ey = vp_door_3->y;
8935 int new_exsize = vp_door_3->width;
8936 int new_eysize = vp_door_3->height;
8937 int new_tilesize_var =
8938 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8940 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8941 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8942 int new_scr_fieldx = new_sxsize / tilesize;
8943 int new_scr_fieldy = new_sysize / tilesize;
8944 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8945 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8946 boolean init_gfx_buffers = FALSE;
8947 boolean init_video_buffer = FALSE;
8948 boolean init_gadgets_and_anims = FALSE;
8949 boolean init_em_graphics = FALSE;
8951 if (new_win_xsize != WIN_XSIZE ||
8952 new_win_ysize != WIN_YSIZE)
8954 WIN_XSIZE = new_win_xsize;
8955 WIN_YSIZE = new_win_ysize;
8957 init_video_buffer = TRUE;
8958 init_gfx_buffers = TRUE;
8959 init_gadgets_and_anims = TRUE;
8961 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8964 if (new_scr_fieldx != SCR_FIELDX ||
8965 new_scr_fieldy != SCR_FIELDY)
8967 /* this always toggles between MAIN and GAME when using small tile size */
8969 SCR_FIELDX = new_scr_fieldx;
8970 SCR_FIELDY = new_scr_fieldy;
8972 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8983 new_sxsize != SXSIZE ||
8984 new_sysize != SYSIZE ||
8985 new_dxsize != DXSIZE ||
8986 new_dysize != DYSIZE ||
8987 new_vxsize != VXSIZE ||
8988 new_vysize != VYSIZE ||
8989 new_exsize != EXSIZE ||
8990 new_eysize != EYSIZE ||
8991 new_real_sx != REAL_SX ||
8992 new_real_sy != REAL_SY ||
8993 new_full_sxsize != FULL_SXSIZE ||
8994 new_full_sysize != FULL_SYSIZE ||
8995 new_tilesize_var != TILESIZE_VAR
8998 // ------------------------------------------------------------------------
8999 // determine next fading area for changed viewport definitions
9000 // ------------------------------------------------------------------------
9002 // start with current playfield area (default fading area)
9005 FADE_SXSIZE = FULL_SXSIZE;
9006 FADE_SYSIZE = FULL_SYSIZE;
9008 // add new playfield area if position or size has changed
9009 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9010 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9012 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9013 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9016 // add current and new door 1 area if position or size has changed
9017 if (new_dx != DX || new_dy != DY ||
9018 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9020 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9021 DX, DY, DXSIZE, DYSIZE);
9022 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9023 new_dx, new_dy, new_dxsize, new_dysize);
9026 // add current and new door 2 area if position or size has changed
9027 if (new_dx != VX || new_dy != VY ||
9028 new_dxsize != VXSIZE || new_dysize != VYSIZE)
9030 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9031 VX, VY, VXSIZE, VYSIZE);
9032 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9033 new_vx, new_vy, new_vxsize, new_vysize);
9036 // ------------------------------------------------------------------------
9037 // handle changed tile size
9038 // ------------------------------------------------------------------------
9040 if (new_tilesize_var != TILESIZE_VAR)
9042 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9044 // changing tile size invalidates scroll values of engine snapshots
9045 FreeEngineSnapshotSingle();
9047 // changing tile size requires update of graphic mapping for EM engine
9048 init_em_graphics = TRUE;
9059 SXSIZE = new_sxsize;
9060 SYSIZE = new_sysize;
9061 DXSIZE = new_dxsize;
9062 DYSIZE = new_dysize;
9063 VXSIZE = new_vxsize;
9064 VYSIZE = new_vysize;
9065 EXSIZE = new_exsize;
9066 EYSIZE = new_eysize;
9067 REAL_SX = new_real_sx;
9068 REAL_SY = new_real_sy;
9069 FULL_SXSIZE = new_full_sxsize;
9070 FULL_SYSIZE = new_full_sysize;
9071 TILESIZE_VAR = new_tilesize_var;
9073 init_gfx_buffers = TRUE;
9074 init_gadgets_and_anims = TRUE;
9076 // printf("::: viewports: init_gfx_buffers\n");
9077 // printf("::: viewports: init_gadgets_and_anims\n");
9080 if (init_gfx_buffers)
9082 // printf("::: init_gfx_buffers\n");
9084 SCR_FIELDX = new_scr_fieldx_buffers;
9085 SCR_FIELDY = new_scr_fieldy_buffers;
9089 SCR_FIELDX = new_scr_fieldx;
9090 SCR_FIELDY = new_scr_fieldy;
9092 SetDrawDeactivationMask(REDRAW_NONE);
9093 SetDrawBackgroundMask(REDRAW_FIELD);
9096 if (init_video_buffer)
9098 // printf("::: init_video_buffer\n");
9100 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9101 InitImageTextures();
9104 if (init_gadgets_and_anims)
9106 // printf("::: init_gadgets_and_anims\n");
9109 InitGlobalAnimations();
9112 if (init_em_graphics)
9114 InitGraphicInfo_EM();