1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 /* select level set with EMC X11 graphics before activating EM GFX debugging */
27 #define DEBUG_EM_GFX FALSE
28 #define DEBUG_FRAME_TIME FALSE
30 /* tool button identifiers */
31 #define TOOL_CTRL_ID_YES 0
32 #define TOOL_CTRL_ID_NO 1
33 #define TOOL_CTRL_ID_CONFIRM 2
34 #define TOOL_CTRL_ID_PLAYER_1 3
35 #define TOOL_CTRL_ID_PLAYER_2 4
36 #define TOOL_CTRL_ID_PLAYER_3 5
37 #define TOOL_CTRL_ID_PLAYER_4 6
39 #define NUM_TOOL_BUTTONS 7
41 /* constants for number of doors and door parts */
43 #define NUM_PANELS NUM_DOORS
44 // #define NUM_PANELS 0
45 #define MAX_PARTS_PER_DOOR 8
46 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
47 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
50 struct DoorPartOrderInfo
56 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
58 struct DoorPartControlInfo
62 struct DoorPartPosInfo *pos;
65 static struct DoorPartControlInfo door_part_controls[] =
69 IMG_GFX_DOOR_1_PART_1,
74 IMG_GFX_DOOR_1_PART_2,
79 IMG_GFX_DOOR_1_PART_3,
84 IMG_GFX_DOOR_1_PART_4,
89 IMG_GFX_DOOR_1_PART_5,
94 IMG_GFX_DOOR_1_PART_6,
99 IMG_GFX_DOOR_1_PART_7,
104 IMG_GFX_DOOR_1_PART_8,
110 IMG_GFX_DOOR_2_PART_1,
115 IMG_GFX_DOOR_2_PART_2,
120 IMG_GFX_DOOR_2_PART_3,
125 IMG_GFX_DOOR_2_PART_4,
130 IMG_GFX_DOOR_2_PART_5,
135 IMG_GFX_DOOR_2_PART_6,
140 IMG_GFX_DOOR_2_PART_7,
145 IMG_GFX_DOOR_2_PART_8,
151 IMG_BACKGROUND_PANEL,
168 /* forward declaration for internal use */
169 static void UnmapToolButtons();
170 static void HandleToolButtons(struct GadgetInfo *);
171 static int el_act_dir2crm(int, int, int);
172 static int el_act2crm(int, int);
174 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
175 static int request_gadget_id = -1;
177 static char *print_if_not_empty(int element)
179 static char *s = NULL;
180 char *token_name = element_info[element].token_name;
185 s = checked_malloc(strlen(token_name) + 10 + 1);
187 if (element != EL_EMPTY)
188 sprintf(s, "%d\t['%s']", element, token_name);
190 sprintf(s, "%d", element);
195 int correctLevelPosX_EM(int lx)
198 lx -= (BorderElement != EL_EMPTY ? 1 : 0);
203 int correctLevelPosY_EM(int ly)
206 ly -= (BorderElement != EL_EMPTY ? 1 : 0);
211 static int getFieldbufferOffsetX_RND()
213 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
214 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
215 int dx_var = dx * TILESIZE_VAR / TILESIZE;
218 if (EVEN(SCR_FIELDX))
220 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
222 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
223 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
225 fx += (dx_var > 0 ? TILEX_VAR : 0);
232 if (full_lev_fieldx <= SCR_FIELDX)
234 if (EVEN(SCR_FIELDX))
235 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
237 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
243 static int getFieldbufferOffsetY_RND()
245 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
246 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
247 int dy_var = dy * TILESIZE_VAR / TILESIZE;
250 if (EVEN(SCR_FIELDY))
252 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
254 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
255 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
257 fy += (dy_var > 0 ? TILEY_VAR : 0);
264 if (full_lev_fieldy <= SCR_FIELDY)
266 if (EVEN(SCR_FIELDY))
267 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
269 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
275 static int getLevelFromScreenX_RND(int sx)
277 int fx = getFieldbufferOffsetX_RND();
280 int lx = LEVELX((px + dx) / TILESIZE_VAR);
285 static int getLevelFromScreenY_RND(int sy)
287 int fy = getFieldbufferOffsetY_RND();
290 int ly = LEVELY((py + dy) / TILESIZE_VAR);
295 static int getLevelFromScreenX_EM(int sx)
297 int level_xsize = level.native_em_level->lev->width;
298 int full_xsize = level_xsize * TILESIZE_VAR;
300 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
302 int fx = getFieldbufferOffsetX_EM();
305 int lx = LEVELX((px + dx) / TILESIZE_VAR);
307 lx = correctLevelPosX_EM(lx);
312 static int getLevelFromScreenY_EM(int sy)
314 int level_ysize = level.native_em_level->lev->height;
315 int full_ysize = level_ysize * TILESIZE_VAR;
317 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
319 int fy = getFieldbufferOffsetY_EM();
322 int ly = LEVELY((py + dy) / TILESIZE_VAR);
324 ly = correctLevelPosY_EM(ly);
329 static int getLevelFromScreenX_SP(int sx)
331 int menBorder = setup.sp_show_border_elements;
332 int level_xsize = level.native_sp_level->width;
333 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
335 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
337 int fx = getFieldbufferOffsetX_SP();
340 int lx = LEVELX((px + dx) / TILESIZE_VAR);
345 static int getLevelFromScreenY_SP(int sy)
347 int menBorder = setup.sp_show_border_elements;
348 int level_ysize = level.native_sp_level->height;
349 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
351 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
353 int fy = getFieldbufferOffsetY_SP();
356 int ly = LEVELY((py + dy) / TILESIZE_VAR);
361 static int getLevelFromScreenX_MM(int sx)
364 int level_xsize = level.native_mm_level->fieldx;
365 int full_xsize = level_xsize * TILESIZE_VAR;
367 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
371 int lx = px / TILESIZE_VAR;
376 static int getLevelFromScreenY_MM(int sy)
379 int level_ysize = level.native_mm_level->fieldy;
380 int full_ysize = level_ysize * TILESIZE_VAR;
382 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
386 int ly = py / TILESIZE_VAR;
391 int getLevelFromScreenX(int x)
393 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
394 return getLevelFromScreenX_EM(x);
395 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
396 return getLevelFromScreenX_SP(x);
397 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
398 return getLevelFromScreenX_MM(x);
400 return getLevelFromScreenX_RND(x);
403 int getLevelFromScreenY(int y)
405 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
406 return getLevelFromScreenY_EM(y);
407 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
408 return getLevelFromScreenY_SP(y);
409 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
410 return getLevelFromScreenY_MM(y);
412 return getLevelFromScreenY_RND(y);
415 void DumpTile(int x, int y)
421 printf_line("-", 79);
422 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
423 printf_line("-", 79);
425 if (!IN_LEV_FIELD(x, y))
427 printf("(not in level field)\n");
433 token_name = element_info[Feld[x][y]].token_name;
435 printf(" Feld: %d\t['%s']\n", Feld[x][y], token_name);
436 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
437 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
438 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
439 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
440 printf(" MovPos: %d\n", MovPos[x][y]);
441 printf(" MovDir: %d\n", MovDir[x][y]);
442 printf(" MovDelay: %d\n", MovDelay[x][y]);
443 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
444 printf(" CustomValue: %d\n", CustomValue[x][y]);
445 printf(" GfxElement: %d\n", GfxElement[x][y]);
446 printf(" GfxAction: %d\n", GfxAction[x][y]);
447 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
448 printf(" Player x/y: %d, %d\n", local_player->jx, local_player->jy);
452 void DumpTileFromScreen(int sx, int sy)
454 int lx = getLevelFromScreenX(sx);
455 int ly = getLevelFromScreenY(sy);
460 void SetDrawtoField(int mode)
462 if (mode == DRAW_TO_FIELDBUFFER)
468 BX2 = SCR_FIELDX + 1;
469 BY2 = SCR_FIELDY + 1;
471 drawto_field = fieldbuffer;
473 else /* DRAW_TO_BACKBUFFER */
479 BX2 = SCR_FIELDX - 1;
480 BY2 = SCR_FIELDY - 1;
482 drawto_field = backbuffer;
486 static void RedrawPlayfield_RND()
488 if (game.envelope_active)
491 DrawLevel(REDRAW_ALL);
495 void RedrawPlayfield()
497 if (game_status != GAME_MODE_PLAYING)
500 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
501 RedrawPlayfield_EM(TRUE);
502 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
503 RedrawPlayfield_SP(TRUE);
504 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
505 RedrawPlayfield_MM();
506 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
507 RedrawPlayfield_RND();
509 BlitScreenToBitmap(backbuffer);
511 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
515 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
518 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
519 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
521 if (x == -1 && y == -1)
524 if (draw_target == DRAW_TO_SCREEN)
525 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
527 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
530 static void DrawMaskedBorderExt_FIELD(int draw_target)
532 if (global.border_status >= GAME_MODE_MAIN &&
533 global.border_status <= GAME_MODE_PLAYING &&
534 border.draw_masked[global.border_status])
535 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
539 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
541 // when drawing to backbuffer, never draw border over open doors
542 if (draw_target == DRAW_TO_BACKBUFFER &&
543 (GetDoorState() & DOOR_OPEN_1))
546 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
547 (global.border_status != GAME_MODE_EDITOR ||
548 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
549 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
552 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
554 // when drawing to backbuffer, never draw border over open doors
555 if (draw_target == DRAW_TO_BACKBUFFER &&
556 (GetDoorState() & DOOR_OPEN_2))
559 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
560 global.border_status != GAME_MODE_EDITOR)
561 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
564 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
566 /* currently not available */
569 static void DrawMaskedBorderExt_ALL(int draw_target)
571 DrawMaskedBorderExt_FIELD(draw_target);
572 DrawMaskedBorderExt_DOOR_1(draw_target);
573 DrawMaskedBorderExt_DOOR_2(draw_target);
574 DrawMaskedBorderExt_DOOR_3(draw_target);
577 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
579 /* never draw masked screen borders on borderless screens */
580 if (global.border_status == GAME_MODE_LOADING ||
581 global.border_status == GAME_MODE_TITLE)
584 if (redraw_mask & REDRAW_ALL)
585 DrawMaskedBorderExt_ALL(draw_target);
588 if (redraw_mask & REDRAW_FIELD)
589 DrawMaskedBorderExt_FIELD(draw_target);
590 if (redraw_mask & REDRAW_DOOR_1)
591 DrawMaskedBorderExt_DOOR_1(draw_target);
592 if (redraw_mask & REDRAW_DOOR_2)
593 DrawMaskedBorderExt_DOOR_2(draw_target);
594 if (redraw_mask & REDRAW_DOOR_3)
595 DrawMaskedBorderExt_DOOR_3(draw_target);
599 void DrawMaskedBorder_FIELD()
601 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
604 void DrawMaskedBorder(int redraw_mask)
606 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
609 void DrawMaskedBorderToTarget(int draw_target)
611 if (draw_target == DRAW_TO_BACKBUFFER ||
612 draw_target == DRAW_TO_SCREEN)
614 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
618 int last_border_status = global.border_status;
620 if (draw_target == DRAW_TO_FADE_SOURCE)
622 global.border_status = gfx.fade_border_source_status;
623 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
625 else if (draw_target == DRAW_TO_FADE_TARGET)
627 global.border_status = gfx.fade_border_target_status;
628 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
631 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
633 global.border_status = last_border_status;
634 gfx.masked_border_bitmap_ptr = backbuffer;
638 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
640 int fx = getFieldbufferOffsetX_RND();
641 int fy = getFieldbufferOffsetY_RND();
643 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
646 void BlitScreenToBitmap(Bitmap *target_bitmap)
648 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
649 BlitScreenToBitmap_EM(target_bitmap);
650 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
651 BlitScreenToBitmap_SP(target_bitmap);
652 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
653 BlitScreenToBitmap_MM(target_bitmap);
654 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
655 BlitScreenToBitmap_RND(target_bitmap);
657 redraw_mask |= REDRAW_FIELD;
660 void DrawFramesPerSecond()
663 int font_nr = FONT_TEXT_2;
664 int font_width = getFontWidth(font_nr);
665 int draw_deactivation_mask = GetDrawDeactivationMask();
666 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
668 /* draw FPS with leading space (needed if field buffer deactivated) */
669 sprintf(text, " %04.1f fps", global.frames_per_second);
671 /* override draw deactivation mask (required for invisible warp mode) */
672 SetDrawDeactivationMask(REDRAW_NONE);
674 /* draw opaque FPS if field buffer deactivated, else draw masked FPS */
675 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
676 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
678 /* set draw deactivation mask to previous value */
679 SetDrawDeactivationMask(draw_deactivation_mask);
681 /* force full-screen redraw in this frame */
682 redraw_mask = REDRAW_ALL;
686 static void PrintFrameTimeDebugging()
688 static unsigned int last_counter = 0;
689 unsigned int counter = Counter();
690 int diff_1 = counter - last_counter;
691 int diff_2 = diff_1 - GAME_FRAME_DELAY;
693 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
694 char diff_bar[2 * diff_2_max + 5];
698 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
700 for (i = 0; i < diff_2_max; i++)
701 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
702 i >= diff_2_max - diff_2_cut ? '-' : ' ');
704 diff_bar[pos++] = '|';
706 for (i = 0; i < diff_2_max; i++)
707 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
709 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
711 diff_bar[pos++] = '\0';
713 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
716 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
719 last_counter = counter;
723 static int unifiedRedrawMask(int mask)
725 if (mask & REDRAW_ALL)
728 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
734 static boolean equalRedrawMasks(int mask_1, int mask_2)
736 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
741 static int last_redraw_mask = REDRAW_NONE;
743 // force screen redraw in every frame to continue drawing global animations
744 // (but always use the last redraw mask to prevent unwanted side effects)
745 if (redraw_mask == REDRAW_NONE)
746 redraw_mask = last_redraw_mask;
748 last_redraw_mask = redraw_mask;
751 // masked border now drawn immediately when blitting backbuffer to window
753 // draw masked border to all viewports, if defined
754 DrawMaskedBorder(redraw_mask);
757 // draw frames per second (only if debug mode is enabled)
758 if (redraw_mask & REDRAW_FPS)
759 DrawFramesPerSecond();
761 // remove playfield redraw before potentially merging with doors redraw
762 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
763 redraw_mask &= ~REDRAW_FIELD;
765 // redraw complete window if both playfield and (some) doors need redraw
766 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
767 redraw_mask = REDRAW_ALL;
769 /* although redrawing the whole window would be fine for normal gameplay,
770 being able to only redraw the playfield is required for deactivating
771 certain drawing areas (mainly playfield) to work, which is needed for
772 warp-forward to be fast enough (by skipping redraw of most frames) */
774 if (redraw_mask & REDRAW_ALL)
776 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
778 else if (redraw_mask & REDRAW_FIELD)
780 BlitBitmap(backbuffer, window,
781 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
783 else if (redraw_mask & REDRAW_DOORS)
785 // merge door areas to prevent calling screen redraw more than once
791 if (redraw_mask & REDRAW_DOOR_1)
795 x2 = MAX(x2, DX + DXSIZE);
796 y2 = MAX(y2, DY + DYSIZE);
799 if (redraw_mask & REDRAW_DOOR_2)
803 x2 = MAX(x2, VX + VXSIZE);
804 y2 = MAX(y2, VY + VYSIZE);
807 if (redraw_mask & REDRAW_DOOR_3)
811 x2 = MAX(x2, EX + EXSIZE);
812 y2 = MAX(y2, EY + EYSIZE);
815 // make sure that at least one pixel is blitted, and inside the screen
816 // (else nothing is blitted, causing the animations not to be updated)
817 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
818 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
819 x2 = MIN(MAX(1, x2), WIN_XSIZE);
820 y2 = MIN(MAX(1, y2), WIN_YSIZE);
822 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
825 redraw_mask = REDRAW_NONE;
828 PrintFrameTimeDebugging();
832 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
834 unsigned int frame_delay_value_old = GetVideoFrameDelay();
836 SetVideoFrameDelay(frame_delay_value);
840 SetVideoFrameDelay(frame_delay_value_old);
843 static int fade_type_skip = FADE_TYPE_NONE;
845 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
847 void (*draw_border_function)(void) = NULL;
848 int x, y, width, height;
849 int fade_delay, post_delay;
851 if (fade_type == FADE_TYPE_FADE_OUT)
853 if (fade_type_skip != FADE_TYPE_NONE)
855 /* skip all fade operations until specified fade operation */
856 if (fade_type & fade_type_skip)
857 fade_type_skip = FADE_TYPE_NONE;
862 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
866 redraw_mask |= fade_mask;
868 if (fade_type == FADE_TYPE_SKIP)
870 fade_type_skip = fade_mode;
875 fade_delay = fading.fade_delay;
876 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
878 if (fade_type_skip != FADE_TYPE_NONE)
880 /* skip all fade operations until specified fade operation */
881 if (fade_type & fade_type_skip)
882 fade_type_skip = FADE_TYPE_NONE;
887 if (global.autoplay_leveldir)
892 if (fade_mask == REDRAW_FIELD)
897 height = FADE_SYSIZE;
899 if (border.draw_masked_when_fading)
900 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
902 DrawMaskedBorder_FIELD(); /* draw once */
904 else /* REDRAW_ALL */
912 if (!setup.fade_screens ||
914 fading.fade_mode == FADE_MODE_NONE)
916 if (fade_mode == FADE_MODE_FADE_OUT)
919 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
921 redraw_mask &= ~fade_mask;
926 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
927 draw_border_function);
929 redraw_mask &= ~fade_mask;
932 static void SetScreenStates_BeforeFadingIn()
934 // temporarily set screen mode for animations to screen after fading in
935 global.anim_status = global.anim_status_next;
937 // store backbuffer with all animations that will be started after fading in
938 if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
939 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
941 // set screen mode for animations back to fading
942 global.anim_status = GAME_MODE_PSEUDO_FADING;
945 static void SetScreenStates_AfterFadingIn()
947 // store new source screen (to use correct masked border for fading)
948 gfx.fade_border_source_status = global.border_status;
950 global.anim_status = global.anim_status_next;
953 static void SetScreenStates_BeforeFadingOut()
955 // store new target screen (to use correct masked border for fading)
956 gfx.fade_border_target_status = game_status;
958 // set screen mode for animations to fading
959 global.anim_status = GAME_MODE_PSEUDO_FADING;
961 // store backbuffer with all animations that will be stopped for fading out
962 if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
963 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
966 static void SetScreenStates_AfterFadingOut()
968 global.border_status = game_status;
971 void FadeIn(int fade_mask)
973 SetScreenStates_BeforeFadingIn();
976 DrawMaskedBorder(REDRAW_ALL);
979 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
980 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
982 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
986 FADE_SXSIZE = FULL_SXSIZE;
987 FADE_SYSIZE = FULL_SYSIZE;
989 if (game_status == GAME_MODE_PLAYING &&
990 strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
991 SetOverlayActive(TRUE);
993 SetScreenStates_AfterFadingIn();
995 // force update of global animation status in case of rapid screen changes
996 redraw_mask = REDRAW_ALL;
1000 void FadeOut(int fade_mask)
1002 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1003 if (!equalRedrawMasks(fade_mask, redraw_mask))
1006 SetScreenStates_BeforeFadingOut();
1008 SetOverlayActive(FALSE);
1011 DrawMaskedBorder(REDRAW_ALL);
1014 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1015 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1017 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1019 SetScreenStates_AfterFadingOut();
1022 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1024 static struct TitleFadingInfo fading_leave_stored;
1027 fading_leave_stored = fading_leave;
1029 fading = fading_leave_stored;
1032 void FadeSetEnterMenu()
1034 fading = menu.enter_menu;
1036 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1039 void FadeSetLeaveMenu()
1041 fading = menu.leave_menu;
1043 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1046 void FadeSetEnterScreen()
1048 fading = menu.enter_screen[game_status];
1050 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
1053 void FadeSetNextScreen()
1055 fading = menu.next_screen[game_status];
1057 // (do not overwrite fade mode set by FadeSetEnterScreen)
1058 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1061 void FadeSetLeaveScreen()
1063 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
1066 void FadeSetFromType(int type)
1068 if (type & TYPE_ENTER_SCREEN)
1069 FadeSetEnterScreen();
1070 else if (type & TYPE_ENTER)
1072 else if (type & TYPE_LEAVE)
1076 void FadeSetDisabled()
1078 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1080 fading = fading_none;
1083 void FadeSkipNextFadeIn()
1085 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1088 void FadeSkipNextFadeOut()
1090 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1093 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1095 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1097 return (graphic == IMG_UNDEFINED ? NULL :
1098 graphic_info[graphic].bitmap != NULL || redefined ?
1099 graphic_info[graphic].bitmap :
1100 graphic_info[default_graphic].bitmap);
1103 Bitmap *getBackgroundBitmap(int graphic)
1105 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1108 Bitmap *getGlobalBorderBitmap(int graphic)
1110 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1113 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1116 (status == GAME_MODE_MAIN ||
1117 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1118 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1119 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1120 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1123 return getGlobalBorderBitmap(graphic);
1126 void SetWindowBackgroundImageIfDefined(int graphic)
1128 if (graphic_info[graphic].bitmap)
1129 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1132 void SetMainBackgroundImageIfDefined(int graphic)
1134 if (graphic_info[graphic].bitmap)
1135 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1138 void SetDoorBackgroundImageIfDefined(int graphic)
1140 if (graphic_info[graphic].bitmap)
1141 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1144 void SetWindowBackgroundImage(int graphic)
1146 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1149 void SetMainBackgroundImage(int graphic)
1151 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1154 void SetDoorBackgroundImage(int graphic)
1156 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1159 void SetPanelBackground()
1161 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1163 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1164 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1166 SetDoorBackgroundBitmap(bitmap_db_panel);
1169 void DrawBackground(int x, int y, int width, int height)
1171 /* "drawto" might still point to playfield buffer here (hall of fame) */
1172 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1174 if (IN_GFX_FIELD_FULL(x, y))
1175 redraw_mask |= REDRAW_FIELD;
1176 else if (IN_GFX_DOOR_1(x, y))
1177 redraw_mask |= REDRAW_DOOR_1;
1178 else if (IN_GFX_DOOR_2(x, y))
1179 redraw_mask |= REDRAW_DOOR_2;
1180 else if (IN_GFX_DOOR_3(x, y))
1181 redraw_mask |= REDRAW_DOOR_3;
1184 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1186 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1188 if (font->bitmap == NULL)
1191 DrawBackground(x, y, width, height);
1194 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1196 struct GraphicInfo *g = &graphic_info[graphic];
1198 if (g->bitmap == NULL)
1201 DrawBackground(x, y, width, height);
1204 static int game_status_last = -1;
1205 static Bitmap *global_border_bitmap_last = NULL;
1206 static Bitmap *global_border_bitmap = NULL;
1207 static int real_sx_last = -1, real_sy_last = -1;
1208 static int full_sxsize_last = -1, full_sysize_last = -1;
1209 static int dx_last = -1, dy_last = -1;
1210 static int dxsize_last = -1, dysize_last = -1;
1211 static int vx_last = -1, vy_last = -1;
1212 static int vxsize_last = -1, vysize_last = -1;
1214 boolean CheckIfGlobalBorderHasChanged()
1216 // if game status has not changed, global border has not changed either
1217 if (game_status == game_status_last)
1220 // determine and store new global border bitmap for current game status
1221 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1223 return (global_border_bitmap_last != global_border_bitmap);
1226 boolean CheckIfGlobalBorderRedrawIsNeeded()
1228 // if game status has not changed, nothing has to be redrawn
1229 if (game_status == game_status_last)
1232 // redraw if last screen was title screen
1233 if (game_status_last == GAME_MODE_TITLE)
1236 // redraw if global screen border has changed
1237 if (CheckIfGlobalBorderHasChanged())
1240 // redraw if position or size of playfield area has changed
1241 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1242 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1245 // redraw if position or size of door area has changed
1246 if (dx_last != DX || dy_last != DY ||
1247 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1250 // redraw if position or size of tape area has changed
1251 if (vx_last != VX || vy_last != VY ||
1252 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1258 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1261 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1263 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1266 void RedrawGlobalBorder()
1268 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1270 RedrawGlobalBorderFromBitmap(bitmap);
1272 redraw_mask = REDRAW_ALL;
1275 static void RedrawGlobalBorderIfNeeded()
1277 if (game_status == game_status_last)
1280 // copy current draw buffer to later copy back areas that have not changed
1281 if (game_status_last != GAME_MODE_TITLE)
1282 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1284 if (CheckIfGlobalBorderRedrawIsNeeded())
1286 // redraw global screen border (or clear, if defined to be empty)
1287 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1289 // copy previous playfield and door areas, if they are defined on both
1290 // previous and current screen and if they still have the same size
1292 if (real_sx_last != -1 && real_sy_last != -1 &&
1293 REAL_SX != -1 && REAL_SY != -1 &&
1294 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1295 BlitBitmap(bitmap_db_store_1, backbuffer,
1296 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1299 if (dx_last != -1 && dy_last != -1 &&
1300 DX != -1 && DY != -1 &&
1301 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1302 BlitBitmap(bitmap_db_store_1, backbuffer,
1303 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1305 if (vx_last != -1 && vy_last != -1 &&
1306 VX != -1 && VY != -1 &&
1307 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1308 BlitBitmap(bitmap_db_store_1, backbuffer,
1309 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1311 redraw_mask = REDRAW_ALL;
1314 game_status_last = game_status;
1316 global_border_bitmap_last = global_border_bitmap;
1318 real_sx_last = REAL_SX;
1319 real_sy_last = REAL_SY;
1320 full_sxsize_last = FULL_SXSIZE;
1321 full_sysize_last = FULL_SYSIZE;
1324 dxsize_last = DXSIZE;
1325 dysize_last = DYSIZE;
1328 vxsize_last = VXSIZE;
1329 vysize_last = VYSIZE;
1334 RedrawGlobalBorderIfNeeded();
1336 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1337 /* (when entering hall of fame after playing) */
1338 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1340 /* !!! maybe this should be done before clearing the background !!! */
1341 if (game_status == GAME_MODE_PLAYING)
1343 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1344 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1348 SetDrawtoField(DRAW_TO_BACKBUFFER);
1352 void MarkTileDirty(int x, int y)
1354 redraw_mask |= REDRAW_FIELD;
1357 void SetBorderElement()
1361 BorderElement = EL_EMPTY;
1363 /* the MM game engine does not use a visible border element */
1364 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1367 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1369 for (x = 0; x < lev_fieldx; x++)
1371 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1372 BorderElement = EL_STEELWALL;
1374 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1380 void FloodFillLevelExt(int from_x, int from_y, int fill_element,
1381 int max_array_fieldx, int max_array_fieldy,
1382 short field[max_array_fieldx][max_array_fieldy],
1383 int max_fieldx, int max_fieldy)
1387 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1388 static int safety = 0;
1390 /* check if starting field still has the desired content */
1391 if (field[from_x][from_y] == fill_element)
1396 if (safety > max_fieldx * max_fieldy)
1397 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1399 old_element = field[from_x][from_y];
1400 field[from_x][from_y] = fill_element;
1402 for (i = 0; i < 4; i++)
1404 x = from_x + check[i][0];
1405 y = from_y + check[i][1];
1407 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1408 FloodFillLevelExt(x, y, fill_element, max_array_fieldx, max_array_fieldy,
1409 field, max_fieldx, max_fieldy);
1415 void FloodFillLevel(int from_x, int from_y, int fill_element,
1416 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1417 int max_fieldx, int max_fieldy)
1419 FloodFillLevelExt(from_x, from_y, fill_element,
1420 MAX_LEV_FIELDX, MAX_LEV_FIELDY, field,
1421 max_fieldx, max_fieldy);
1424 void SetRandomAnimationValue(int x, int y)
1426 gfx.anim_random_frame = GfxRandom[x][y];
1429 int getGraphicAnimationFrame(int graphic, int sync_frame)
1431 /* animation synchronized with global frame counter, not move position */
1432 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1433 sync_frame = FrameCounter;
1435 return getAnimationFrame(graphic_info[graphic].anim_frames,
1436 graphic_info[graphic].anim_delay,
1437 graphic_info[graphic].anim_mode,
1438 graphic_info[graphic].anim_start_frame,
1442 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1444 struct GraphicInfo *g = &graphic_info[graphic];
1445 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1447 if (tilesize == gfx.standard_tile_size)
1448 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1449 else if (tilesize == game.tile_size)
1450 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1452 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1455 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1456 boolean get_backside)
1458 struct GraphicInfo *g = &graphic_info[graphic];
1459 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1460 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1462 if (g->offset_y == 0) /* frames are ordered horizontally */
1464 int max_width = g->anim_frames_per_line * g->width;
1465 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1467 *x = pos % max_width;
1468 *y = src_y % g->height + pos / max_width * g->height;
1470 else if (g->offset_x == 0) /* frames are ordered vertically */
1472 int max_height = g->anim_frames_per_line * g->height;
1473 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1475 *x = src_x % g->width + pos / max_height * g->width;
1476 *y = pos % max_height;
1478 else /* frames are ordered diagonally */
1480 *x = src_x + frame * g->offset_x;
1481 *y = src_y + frame * g->offset_y;
1485 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1486 Bitmap **bitmap, int *x, int *y,
1487 boolean get_backside)
1489 struct GraphicInfo *g = &graphic_info[graphic];
1491 // if no in-game graphics defined, always use standard graphic size
1492 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1493 tilesize = TILESIZE;
1495 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1496 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1498 *x = *x * tilesize / g->tile_size;
1499 *y = *y * tilesize / g->tile_size;
1502 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1503 Bitmap **bitmap, int *x, int *y)
1505 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1508 void getFixedGraphicSource(int graphic, int frame,
1509 Bitmap **bitmap, int *x, int *y)
1511 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1514 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1516 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1519 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1520 int *x, int *y, boolean get_backside)
1522 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1526 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1528 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1531 void DrawGraphic(int x, int y, int graphic, int frame)
1534 if (!IN_SCR_FIELD(x, y))
1536 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1537 printf("DrawGraphic(): This should never happen!\n");
1542 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1545 MarkTileDirty(x, y);
1548 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1551 if (!IN_SCR_FIELD(x, y))
1553 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1554 printf("DrawGraphic(): This should never happen!\n");
1559 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1561 MarkTileDirty(x, y);
1564 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1570 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1572 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1575 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1581 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1582 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1585 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1588 if (!IN_SCR_FIELD(x, y))
1590 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1591 printf("DrawGraphicThruMask(): This should never happen!\n");
1596 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1599 MarkTileDirty(x, y);
1602 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1605 if (!IN_SCR_FIELD(x, y))
1607 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1608 printf("DrawGraphicThruMask(): This should never happen!\n");
1613 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1615 MarkTileDirty(x, y);
1618 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1624 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1626 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1630 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1631 int graphic, int frame)
1636 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1638 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1642 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1644 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1646 MarkTileDirty(x / tilesize, y / tilesize);
1649 void DrawSizedGraphicThruMask(int x, int y, int graphic, int frame,
1652 DrawSizedGraphicThruMaskExt(drawto, SX + x * tilesize, SY + y * tilesize,
1653 graphic, frame, tilesize);
1654 MarkTileDirty(x / tilesize, y / tilesize);
1657 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1663 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1664 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1667 void DrawSizedGraphicThruMaskExt(DrawBuffer *d, int x, int y, int graphic,
1668 int frame, int tilesize)
1673 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1674 BlitBitmapMasked(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1677 void DrawMiniGraphic(int x, int y, int graphic)
1679 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1680 MarkTileDirty(x / 2, y / 2);
1683 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1688 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1689 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1692 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1693 int graphic, int frame,
1694 int cut_mode, int mask_mode)
1699 int width = TILEX, height = TILEY;
1702 if (dx || dy) /* shifted graphic */
1704 if (x < BX1) /* object enters playfield from the left */
1711 else if (x > BX2) /* object enters playfield from the right */
1717 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1723 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1725 else if (dx) /* general horizontal movement */
1726 MarkTileDirty(x + SIGN(dx), y);
1728 if (y < BY1) /* object enters playfield from the top */
1730 if (cut_mode == CUT_BELOW) /* object completely above top border */
1738 else if (y > BY2) /* object enters playfield from the bottom */
1744 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1750 else if (dy > 0 && cut_mode == CUT_ABOVE)
1752 if (y == BY2) /* object completely above bottom border */
1758 MarkTileDirty(x, y + 1);
1759 } /* object leaves playfield to the bottom */
1760 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1762 else if (dy) /* general vertical movement */
1763 MarkTileDirty(x, y + SIGN(dy));
1767 if (!IN_SCR_FIELD(x, y))
1769 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1770 printf("DrawGraphicShifted(): This should never happen!\n");
1775 width = width * TILESIZE_VAR / TILESIZE;
1776 height = height * TILESIZE_VAR / TILESIZE;
1777 cx = cx * TILESIZE_VAR / TILESIZE;
1778 cy = cy * TILESIZE_VAR / TILESIZE;
1779 dx = dx * TILESIZE_VAR / TILESIZE;
1780 dy = dy * TILESIZE_VAR / TILESIZE;
1782 if (width > 0 && height > 0)
1784 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1789 dst_x = FX + x * TILEX_VAR + dx;
1790 dst_y = FY + y * TILEY_VAR + dy;
1792 if (mask_mode == USE_MASKING)
1793 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1796 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1799 MarkTileDirty(x, y);
1803 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1804 int graphic, int frame,
1805 int cut_mode, int mask_mode)
1810 int width = TILEX_VAR, height = TILEY_VAR;
1813 int x2 = x + SIGN(dx);
1814 int y2 = y + SIGN(dy);
1816 /* movement with two-tile animations must be sync'ed with movement position,
1817 not with current GfxFrame (which can be higher when using slow movement) */
1818 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1819 int anim_frames = graphic_info[graphic].anim_frames;
1821 /* (we also need anim_delay here for movement animations with less frames) */
1822 int anim_delay = graphic_info[graphic].anim_delay;
1823 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1825 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1826 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1828 /* re-calculate animation frame for two-tile movement animation */
1829 frame = getGraphicAnimationFrame(graphic, sync_frame);
1831 /* check if movement start graphic inside screen area and should be drawn */
1832 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1834 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1836 dst_x = FX + x1 * TILEX_VAR;
1837 dst_y = FY + y1 * TILEY_VAR;
1839 if (mask_mode == USE_MASKING)
1840 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1843 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1846 MarkTileDirty(x1, y1);
1849 /* check if movement end graphic inside screen area and should be drawn */
1850 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1852 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1854 dst_x = FX + x2 * TILEX_VAR;
1855 dst_y = FY + y2 * TILEY_VAR;
1857 if (mask_mode == USE_MASKING)
1858 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1861 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1864 MarkTileDirty(x2, y2);
1868 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1869 int graphic, int frame,
1870 int cut_mode, int mask_mode)
1874 DrawGraphic(x, y, graphic, frame);
1879 if (graphic_info[graphic].double_movement) /* EM style movement images */
1880 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1882 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1885 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1886 int frame, int cut_mode)
1888 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1891 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1892 int cut_mode, int mask_mode)
1894 int lx = LEVELX(x), ly = LEVELY(y);
1898 if (IN_LEV_FIELD(lx, ly))
1900 SetRandomAnimationValue(lx, ly);
1902 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1903 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1905 /* do not use double (EM style) movement graphic when not moving */
1906 if (graphic_info[graphic].double_movement && !dx && !dy)
1908 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1909 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1912 else /* border element */
1914 graphic = el2img(element);
1915 frame = getGraphicAnimationFrame(graphic, -1);
1918 if (element == EL_EXPANDABLE_WALL)
1920 boolean left_stopped = FALSE, right_stopped = FALSE;
1922 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1923 left_stopped = TRUE;
1924 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1925 right_stopped = TRUE;
1927 if (left_stopped && right_stopped)
1929 else if (left_stopped)
1931 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1932 frame = graphic_info[graphic].anim_frames - 1;
1934 else if (right_stopped)
1936 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1937 frame = graphic_info[graphic].anim_frames - 1;
1942 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1943 else if (mask_mode == USE_MASKING)
1944 DrawGraphicThruMask(x, y, graphic, frame);
1946 DrawGraphic(x, y, graphic, frame);
1949 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1950 int cut_mode, int mask_mode)
1952 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1953 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1954 cut_mode, mask_mode);
1957 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1960 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1963 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1966 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1969 void DrawLevelElementThruMask(int x, int y, int element)
1971 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1974 void DrawLevelFieldThruMask(int x, int y)
1976 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1979 /* !!! implementation of quicksand is totally broken !!! */
1980 #define IS_CRUMBLED_TILE(x, y, e) \
1981 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1982 !IS_MOVING(x, y) || \
1983 (e) == EL_QUICKSAND_EMPTYING || \
1984 (e) == EL_QUICKSAND_FAST_EMPTYING))
1986 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1991 int width, height, cx, cy;
1992 int sx = SCREENX(x), sy = SCREENY(y);
1993 int crumbled_border_size = graphic_info[graphic].border_size;
1994 int crumbled_tile_size = graphic_info[graphic].tile_size;
1995 int crumbled_border_size_var =
1996 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
1999 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2001 for (i = 1; i < 4; i++)
2003 int dxx = (i & 1 ? dx : 0);
2004 int dyy = (i & 2 ? dy : 0);
2007 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2010 /* check if neighbour field is of same crumble type */
2011 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
2012 graphic_info[graphic].class ==
2013 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
2015 /* return if check prevents inner corner */
2016 if (same == (dxx == dx && dyy == dy))
2020 /* if we reach this point, we have an inner corner */
2022 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
2024 width = crumbled_border_size_var;
2025 height = crumbled_border_size_var;
2026 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
2027 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2029 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2030 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2033 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2038 int width, height, bx, by, cx, cy;
2039 int sx = SCREENX(x), sy = SCREENY(y);
2040 int crumbled_border_size = graphic_info[graphic].border_size;
2041 int crumbled_tile_size = graphic_info[graphic].tile_size;
2042 int crumbled_border_size_var =
2043 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2044 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2047 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2049 /* draw simple, sloppy, non-corner-accurate crumbled border */
2051 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2052 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2053 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2054 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2056 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2057 FX + sx * TILEX_VAR + cx,
2058 FY + sy * TILEY_VAR + cy);
2060 /* (remaining middle border part must be at least as big as corner part) */
2061 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2062 crumbled_border_size_var >= TILESIZE_VAR / 3)
2065 /* correct corners of crumbled border, if needed */
2067 for (i = -1; i <= 1; i += 2)
2069 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2070 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2071 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2074 /* check if neighbour field is of same crumble type */
2075 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2076 graphic_info[graphic].class ==
2077 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2079 /* no crumbled corner, but continued crumbled border */
2081 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2082 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2083 int b1 = (i == 1 ? crumbled_border_size_var :
2084 TILESIZE_VAR - 2 * crumbled_border_size_var);
2086 width = crumbled_border_size_var;
2087 height = crumbled_border_size_var;
2089 if (dir == 1 || dir == 2)
2104 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2106 FX + sx * TILEX_VAR + cx,
2107 FY + sy * TILEY_VAR + cy);
2112 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2114 int sx = SCREENX(x), sy = SCREENY(y);
2117 static int xy[4][2] =
2125 if (!IN_LEV_FIELD(x, y))
2128 element = TILE_GFX_ELEMENT(x, y);
2130 if (IS_CRUMBLED_TILE(x, y, element)) /* crumble field itself */
2132 if (!IN_SCR_FIELD(sx, sy))
2135 /* crumble field borders towards direct neighbour fields */
2136 for (i = 0; i < 4; i++)
2138 int xx = x + xy[i][0];
2139 int yy = y + xy[i][1];
2141 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2144 /* check if neighbour field is of same crumble type */
2145 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2146 graphic_info[graphic].class ==
2147 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2150 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2153 /* crumble inner field corners towards corner neighbour fields */
2154 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2155 graphic_info[graphic].anim_frames == 2)
2157 for (i = 0; i < 4; i++)
2159 int dx = (i & 1 ? +1 : -1);
2160 int dy = (i & 2 ? +1 : -1);
2162 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2166 MarkTileDirty(sx, sy);
2168 else /* center field is not crumbled -- crumble neighbour fields */
2170 /* crumble field borders of direct neighbour fields */
2171 for (i = 0; i < 4; i++)
2173 int xx = x + xy[i][0];
2174 int yy = y + xy[i][1];
2175 int sxx = sx + xy[i][0];
2176 int syy = sy + xy[i][1];
2178 if (!IN_LEV_FIELD(xx, yy) ||
2179 !IN_SCR_FIELD(sxx, syy))
2182 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2185 element = TILE_GFX_ELEMENT(xx, yy);
2187 if (!IS_CRUMBLED_TILE(xx, yy, element))
2190 graphic = el_act2crm(element, ACTION_DEFAULT);
2192 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2194 MarkTileDirty(sxx, syy);
2197 /* crumble inner field corners of corner neighbour fields */
2198 for (i = 0; i < 4; i++)
2200 int dx = (i & 1 ? +1 : -1);
2201 int dy = (i & 2 ? +1 : -1);
2207 if (!IN_LEV_FIELD(xx, yy) ||
2208 !IN_SCR_FIELD(sxx, syy))
2211 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2214 element = TILE_GFX_ELEMENT(xx, yy);
2216 if (!IS_CRUMBLED_TILE(xx, yy, element))
2219 graphic = el_act2crm(element, ACTION_DEFAULT);
2221 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2222 graphic_info[graphic].anim_frames == 2)
2223 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2225 MarkTileDirty(sxx, syy);
2230 void DrawLevelFieldCrumbled(int x, int y)
2234 if (!IN_LEV_FIELD(x, y))
2237 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2238 GfxElement[x][y] != EL_UNDEFINED &&
2239 GFX_CRUMBLED(GfxElement[x][y]))
2241 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2246 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2248 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2251 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2254 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2255 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2256 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2257 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2258 int sx = SCREENX(x), sy = SCREENY(y);
2260 DrawGraphic(sx, sy, graphic1, frame1);
2261 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2264 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2266 int sx = SCREENX(x), sy = SCREENY(y);
2267 static int xy[4][2] =
2276 /* crumble direct neighbour fields (required for field borders) */
2277 for (i = 0; i < 4; i++)
2279 int xx = x + xy[i][0];
2280 int yy = y + xy[i][1];
2281 int sxx = sx + xy[i][0];
2282 int syy = sy + xy[i][1];
2284 if (!IN_LEV_FIELD(xx, yy) ||
2285 !IN_SCR_FIELD(sxx, syy) ||
2286 !GFX_CRUMBLED(Feld[xx][yy]) ||
2290 DrawLevelField(xx, yy);
2293 /* crumble corner neighbour fields (required for inner field corners) */
2294 for (i = 0; i < 4; i++)
2296 int dx = (i & 1 ? +1 : -1);
2297 int dy = (i & 2 ? +1 : -1);
2303 if (!IN_LEV_FIELD(xx, yy) ||
2304 !IN_SCR_FIELD(sxx, syy) ||
2305 !GFX_CRUMBLED(Feld[xx][yy]) ||
2309 int element = TILE_GFX_ELEMENT(xx, yy);
2310 int graphic = el_act2crm(element, ACTION_DEFAULT);
2312 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2313 graphic_info[graphic].anim_frames == 2)
2314 DrawLevelField(xx, yy);
2318 static int getBorderElement(int x, int y)
2322 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2323 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2324 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2325 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2326 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2327 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2328 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2330 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2331 int steel_position = (x == -1 && y == -1 ? 0 :
2332 x == lev_fieldx && y == -1 ? 1 :
2333 x == -1 && y == lev_fieldy ? 2 :
2334 x == lev_fieldx && y == lev_fieldy ? 3 :
2335 x == -1 || x == lev_fieldx ? 4 :
2336 y == -1 || y == lev_fieldy ? 5 : 6);
2338 return border[steel_position][steel_type];
2341 void DrawScreenElement(int x, int y, int element)
2343 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2344 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2347 void DrawLevelElement(int x, int y, int element)
2349 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2350 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2353 void DrawScreenField(int x, int y)
2355 int lx = LEVELX(x), ly = LEVELY(y);
2356 int element, content;
2358 if (!IN_LEV_FIELD(lx, ly))
2360 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2363 element = getBorderElement(lx, ly);
2365 DrawScreenElement(x, y, element);
2370 element = Feld[lx][ly];
2371 content = Store[lx][ly];
2373 if (IS_MOVING(lx, ly))
2375 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2376 boolean cut_mode = NO_CUTTING;
2378 if (element == EL_QUICKSAND_EMPTYING ||
2379 element == EL_QUICKSAND_FAST_EMPTYING ||
2380 element == EL_MAGIC_WALL_EMPTYING ||
2381 element == EL_BD_MAGIC_WALL_EMPTYING ||
2382 element == EL_DC_MAGIC_WALL_EMPTYING ||
2383 element == EL_AMOEBA_DROPPING)
2384 cut_mode = CUT_ABOVE;
2385 else if (element == EL_QUICKSAND_FILLING ||
2386 element == EL_QUICKSAND_FAST_FILLING ||
2387 element == EL_MAGIC_WALL_FILLING ||
2388 element == EL_BD_MAGIC_WALL_FILLING ||
2389 element == EL_DC_MAGIC_WALL_FILLING)
2390 cut_mode = CUT_BELOW;
2392 if (cut_mode == CUT_ABOVE)
2393 DrawScreenElement(x, y, element);
2395 DrawScreenElement(x, y, EL_EMPTY);
2398 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2399 else if (cut_mode == NO_CUTTING)
2400 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2403 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2405 if (cut_mode == CUT_BELOW &&
2406 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2407 DrawLevelElement(lx, ly + 1, element);
2410 if (content == EL_ACID)
2412 int dir = MovDir[lx][ly];
2413 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2414 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2416 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2418 // prevent target field from being drawn again (but without masking)
2419 // (this would happen if target field is scanned after moving element)
2420 Stop[newlx][newly] = TRUE;
2423 else if (IS_BLOCKED(lx, ly))
2428 boolean cut_mode = NO_CUTTING;
2429 int element_old, content_old;
2431 Blocked2Moving(lx, ly, &oldx, &oldy);
2434 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2435 MovDir[oldx][oldy] == MV_RIGHT);
2437 element_old = Feld[oldx][oldy];
2438 content_old = Store[oldx][oldy];
2440 if (element_old == EL_QUICKSAND_EMPTYING ||
2441 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2442 element_old == EL_MAGIC_WALL_EMPTYING ||
2443 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2444 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2445 element_old == EL_AMOEBA_DROPPING)
2446 cut_mode = CUT_ABOVE;
2448 DrawScreenElement(x, y, EL_EMPTY);
2451 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2453 else if (cut_mode == NO_CUTTING)
2454 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2457 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2460 else if (IS_DRAWABLE(element))
2461 DrawScreenElement(x, y, element);
2463 DrawScreenElement(x, y, EL_EMPTY);
2466 void DrawLevelField(int x, int y)
2468 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2469 DrawScreenField(SCREENX(x), SCREENY(y));
2470 else if (IS_MOVING(x, y))
2474 Moving2Blocked(x, y, &newx, &newy);
2475 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2476 DrawScreenField(SCREENX(newx), SCREENY(newy));
2478 else if (IS_BLOCKED(x, y))
2482 Blocked2Moving(x, y, &oldx, &oldy);
2483 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2484 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2488 static void DrawSizedWallExt_MM(int dst_x, int dst_y, int element, int tilesize,
2489 int (*el2img_function)(int), boolean masked)
2491 int element_base = map_mm_wall_element(element);
2492 int element_bits = (IS_DF_WALL(element) ?
2493 element - EL_DF_WALL_START :
2494 element - EL_MM_WALL_START) & 0x000f;
2495 int graphic = el2img_function(element_base);
2496 int tilesize_draw = tilesize / 2;
2501 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2503 for (i = 0; i < 4; i++)
2505 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2506 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2508 if (element_bits & (1 << i))
2511 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y,
2512 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2514 BlitBitmap(src_bitmap, drawto, src_x, src_y,
2515 tilesize_draw, tilesize_draw, dst_draw_x, dst_draw_y);
2520 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2521 tilesize_draw, tilesize_draw);
2526 void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2527 int (*el2img_function)(int))
2529 DrawSizedWallExt_MM(dst_x, dst_y, element, tilesize, el2img_function, FALSE);
2532 void DrawSizedElementExt(int x, int y, int element, int tilesize,
2535 if (IS_MM_WALL(element))
2537 DrawSizedWallExt_MM(SX + x * tilesize, SY + y * tilesize,
2538 element, tilesize, el2edimg, masked);
2542 int graphic = el2edimg(element);
2545 DrawSizedGraphicThruMask(x, y, graphic, 0, tilesize);
2547 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2551 void DrawSizedElement(int x, int y, int element, int tilesize)
2553 DrawSizedElementExt(x, y, element, tilesize, FALSE);
2556 void DrawSizedElementThruMask(int x, int y, int element, int tilesize)
2558 DrawSizedElementExt(x, y, element, tilesize, TRUE);
2561 void DrawMiniElement(int x, int y, int element)
2565 graphic = el2edimg(element);
2566 DrawMiniGraphic(x, y, graphic);
2569 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2572 int x = sx + scroll_x, y = sy + scroll_y;
2574 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2575 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2576 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2577 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2579 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2582 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2584 int x = sx + scroll_x, y = sy + scroll_y;
2586 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2587 DrawMiniElement(sx, sy, EL_EMPTY);
2588 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2589 DrawMiniElement(sx, sy, Feld[x][y]);
2591 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2594 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2595 int x, int y, int xsize, int ysize,
2596 int tile_width, int tile_height)
2600 int dst_x = startx + x * tile_width;
2601 int dst_y = starty + y * tile_height;
2602 int width = graphic_info[graphic].width;
2603 int height = graphic_info[graphic].height;
2604 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2605 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2606 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2607 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2608 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2609 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2610 boolean draw_masked = graphic_info[graphic].draw_masked;
2612 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2614 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2616 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2620 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2621 inner_sx + (x - 1) * tile_width % inner_width);
2622 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2623 inner_sy + (y - 1) * tile_height % inner_height);
2626 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2629 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2633 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2634 int x, int y, int xsize, int ysize, int font_nr)
2636 int font_width = getFontWidth(font_nr);
2637 int font_height = getFontHeight(font_nr);
2639 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2640 font_width, font_height);
2643 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2645 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2646 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2647 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2648 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2649 boolean no_delay = (tape.warp_forward);
2650 unsigned int anim_delay = 0;
2651 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2652 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2653 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2654 int font_width = getFontWidth(font_nr);
2655 int font_height = getFontHeight(font_nr);
2656 int max_xsize = level.envelope[envelope_nr].xsize;
2657 int max_ysize = level.envelope[envelope_nr].ysize;
2658 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2659 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2660 int xend = max_xsize;
2661 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2662 int xstep = (xstart < xend ? 1 : 0);
2663 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2665 int end = MAX(xend - xstart, yend - ystart);
2668 for (i = start; i <= end; i++)
2670 int last_frame = end; // last frame of this "for" loop
2671 int x = xstart + i * xstep;
2672 int y = ystart + i * ystep;
2673 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2674 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2675 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2676 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2679 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2681 BlitScreenToBitmap(backbuffer);
2683 SetDrawtoField(DRAW_TO_BACKBUFFER);
2685 for (yy = 0; yy < ysize; yy++)
2686 for (xx = 0; xx < xsize; xx++)
2687 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2689 DrawTextBuffer(sx + font_width, sy + font_height,
2690 level.envelope[envelope_nr].text, font_nr, max_xsize,
2691 xsize - 2, ysize - 2, 0, mask_mode,
2692 level.envelope[envelope_nr].autowrap,
2693 level.envelope[envelope_nr].centered, FALSE);
2695 redraw_mask |= REDRAW_FIELD;
2698 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2702 void ShowEnvelope(int envelope_nr)
2704 int element = EL_ENVELOPE_1 + envelope_nr;
2705 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2706 int sound_opening = element_info[element].sound[ACTION_OPENING];
2707 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2708 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2709 boolean no_delay = (tape.warp_forward);
2710 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2711 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2712 int anim_mode = graphic_info[graphic].anim_mode;
2713 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2714 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2716 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2718 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2720 if (anim_mode == ANIM_DEFAULT)
2721 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2723 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2726 Delay(wait_delay_value);
2728 WaitForEventToContinue();
2730 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2732 if (anim_mode != ANIM_NONE)
2733 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2735 if (anim_mode == ANIM_DEFAULT)
2736 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2738 game.envelope_active = FALSE;
2740 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2742 redraw_mask |= REDRAW_FIELD;
2746 static void setRequestBasePosition(int *x, int *y)
2748 int sx_base, sy_base;
2750 if (request.x != -1)
2751 sx_base = request.x;
2752 else if (request.align == ALIGN_LEFT)
2754 else if (request.align == ALIGN_RIGHT)
2755 sx_base = SX + SXSIZE;
2757 sx_base = SX + SXSIZE / 2;
2759 if (request.y != -1)
2760 sy_base = request.y;
2761 else if (request.valign == VALIGN_TOP)
2763 else if (request.valign == VALIGN_BOTTOM)
2764 sy_base = SY + SYSIZE;
2766 sy_base = SY + SYSIZE / 2;
2772 static void setRequestPositionExt(int *x, int *y, int width, int height,
2773 boolean add_border_size)
2775 int border_size = request.border_size;
2776 int sx_base, sy_base;
2779 setRequestBasePosition(&sx_base, &sy_base);
2781 if (request.align == ALIGN_LEFT)
2783 else if (request.align == ALIGN_RIGHT)
2784 sx = sx_base - width;
2786 sx = sx_base - width / 2;
2788 if (request.valign == VALIGN_TOP)
2790 else if (request.valign == VALIGN_BOTTOM)
2791 sy = sy_base - height;
2793 sy = sy_base - height / 2;
2795 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2796 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2798 if (add_border_size)
2808 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2810 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2813 void DrawEnvelopeRequest(char *text)
2815 char *text_final = text;
2816 char *text_door_style = NULL;
2817 int graphic = IMG_BACKGROUND_REQUEST;
2818 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2819 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2820 int font_nr = FONT_REQUEST;
2821 int font_width = getFontWidth(font_nr);
2822 int font_height = getFontHeight(font_nr);
2823 int border_size = request.border_size;
2824 int line_spacing = request.line_spacing;
2825 int line_height = font_height + line_spacing;
2826 int max_text_width = request.width - 2 * border_size;
2827 int max_text_height = request.height - 2 * border_size;
2828 int line_length = max_text_width / font_width;
2829 int max_lines = max_text_height / line_height;
2830 int text_width = line_length * font_width;
2831 int width = request.width;
2832 int height = request.height;
2833 int tile_size = MAX(request.step_offset, 1);
2834 int x_steps = width / tile_size;
2835 int y_steps = height / tile_size;
2836 int sx_offset = border_size;
2837 int sy_offset = border_size;
2841 if (request.centered)
2842 sx_offset = (request.width - text_width) / 2;
2844 if (request.wrap_single_words && !request.autowrap)
2846 char *src_text_ptr, *dst_text_ptr;
2848 text_door_style = checked_malloc(2 * strlen(text) + 1);
2850 src_text_ptr = text;
2851 dst_text_ptr = text_door_style;
2853 while (*src_text_ptr)
2855 if (*src_text_ptr == ' ' ||
2856 *src_text_ptr == '?' ||
2857 *src_text_ptr == '!')
2858 *dst_text_ptr++ = '\n';
2860 if (*src_text_ptr != ' ')
2861 *dst_text_ptr++ = *src_text_ptr;
2866 *dst_text_ptr = '\0';
2868 text_final = text_door_style;
2871 setRequestPosition(&sx, &sy, FALSE);
2873 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2875 for (y = 0; y < y_steps; y++)
2876 for (x = 0; x < x_steps; x++)
2877 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2878 x, y, x_steps, y_steps,
2879 tile_size, tile_size);
2881 /* force DOOR font inside door area */
2882 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2884 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2885 line_length, -1, max_lines, line_spacing, mask_mode,
2886 request.autowrap, request.centered, FALSE);
2890 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2891 RedrawGadget(tool_gadget[i]);
2893 // store readily prepared envelope request for later use when animating
2894 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2896 if (text_door_style)
2897 free(text_door_style);
2900 void AnimateEnvelopeRequest(int anim_mode, int action)
2902 int graphic = IMG_BACKGROUND_REQUEST;
2903 boolean draw_masked = graphic_info[graphic].draw_masked;
2904 int delay_value_normal = request.step_delay;
2905 int delay_value_fast = delay_value_normal / 2;
2906 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2907 boolean no_delay = (tape.warp_forward);
2908 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2909 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
2910 unsigned int anim_delay = 0;
2912 int tile_size = MAX(request.step_offset, 1);
2913 int max_xsize = request.width / tile_size;
2914 int max_ysize = request.height / tile_size;
2915 int max_xsize_inner = max_xsize - 2;
2916 int max_ysize_inner = max_ysize - 2;
2918 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2919 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2920 int xend = max_xsize_inner;
2921 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2922 int xstep = (xstart < xend ? 1 : 0);
2923 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2925 int end = MAX(xend - xstart, yend - ystart);
2928 if (setup.quick_doors)
2935 for (i = start; i <= end; i++)
2937 int last_frame = end; // last frame of this "for" loop
2938 int x = xstart + i * xstep;
2939 int y = ystart + i * ystep;
2940 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2941 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2942 int xsize_size_left = (xsize - 1) * tile_size;
2943 int ysize_size_top = (ysize - 1) * tile_size;
2944 int max_xsize_pos = (max_xsize - 1) * tile_size;
2945 int max_ysize_pos = (max_ysize - 1) * tile_size;
2946 int width = xsize * tile_size;
2947 int height = ysize * tile_size;
2952 setRequestPosition(&src_x, &src_y, FALSE);
2953 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2955 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2957 for (yy = 0; yy < 2; yy++)
2959 for (xx = 0; xx < 2; xx++)
2961 int src_xx = src_x + xx * max_xsize_pos;
2962 int src_yy = src_y + yy * max_ysize_pos;
2963 int dst_xx = dst_x + xx * xsize_size_left;
2964 int dst_yy = dst_y + yy * ysize_size_top;
2965 int xx_size = (xx ? tile_size : xsize_size_left);
2966 int yy_size = (yy ? tile_size : ysize_size_top);
2969 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
2970 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2972 BlitBitmap(bitmap_db_store_2, backbuffer,
2973 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2977 redraw_mask |= REDRAW_FIELD;
2981 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2985 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2987 int graphic = IMG_BACKGROUND_REQUEST;
2988 int sound_opening = SND_REQUEST_OPENING;
2989 int sound_closing = SND_REQUEST_CLOSING;
2990 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2991 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2992 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2993 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2994 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2996 if (game_status == GAME_MODE_PLAYING)
2997 BlitScreenToBitmap(backbuffer);
2999 SetDrawtoField(DRAW_TO_BACKBUFFER);
3001 // SetDrawBackgroundMask(REDRAW_NONE);
3003 if (action == ACTION_OPENING)
3005 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3007 if (req_state & REQ_ASK)
3009 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3010 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3012 else if (req_state & REQ_CONFIRM)
3014 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3016 else if (req_state & REQ_PLAYER)
3018 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3019 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3020 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3021 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3024 DrawEnvelopeRequest(text);
3027 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
3029 if (action == ACTION_OPENING)
3031 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
3033 if (anim_mode == ANIM_DEFAULT)
3034 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
3036 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
3040 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
3042 if (anim_mode != ANIM_NONE)
3043 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
3045 if (anim_mode == ANIM_DEFAULT)
3046 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
3049 game.envelope_active = FALSE;
3051 if (action == ACTION_CLOSING)
3052 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
3054 // SetDrawBackgroundMask(last_draw_background_mask);
3056 redraw_mask |= REDRAW_FIELD;
3060 if (action == ACTION_CLOSING &&
3061 game_status == GAME_MODE_PLAYING &&
3062 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3063 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3066 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3068 if (IS_MM_WALL(element))
3070 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3076 int graphic = el2preimg(element);
3078 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3079 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3084 void DrawLevel(int draw_background_mask)
3088 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3089 SetDrawBackgroundMask(draw_background_mask);
3093 for (x = BX1; x <= BX2; x++)
3094 for (y = BY1; y <= BY2; y++)
3095 DrawScreenField(x, y);
3097 redraw_mask |= REDRAW_FIELD;
3100 void DrawSizedLevel(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 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3109 redraw_mask |= REDRAW_FIELD;
3112 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3116 for (x = 0; x < size_x; x++)
3117 for (y = 0; y < size_y; y++)
3118 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3120 redraw_mask |= REDRAW_FIELD;
3123 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3125 boolean show_level_border = (BorderElement != EL_EMPTY);
3126 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3127 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3128 int tile_size = preview.tile_size;
3129 int preview_width = preview.xsize * tile_size;
3130 int preview_height = preview.ysize * tile_size;
3131 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3132 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3133 int real_preview_width = real_preview_xsize * tile_size;
3134 int real_preview_height = real_preview_ysize * tile_size;
3135 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3136 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3139 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3142 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3144 dst_x += (preview_width - real_preview_width) / 2;
3145 dst_y += (preview_height - real_preview_height) / 2;
3147 for (x = 0; x < real_preview_xsize; x++)
3149 for (y = 0; y < real_preview_ysize; y++)
3151 int lx = from_x + x + (show_level_border ? -1 : 0);
3152 int ly = from_y + y + (show_level_border ? -1 : 0);
3153 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3154 getBorderElement(lx, ly));
3156 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3157 element, tile_size);
3161 redraw_mask |= REDRAW_FIELD;
3164 #define MICROLABEL_EMPTY 0
3165 #define MICROLABEL_LEVEL_NAME 1
3166 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3167 #define MICROLABEL_LEVEL_AUTHOR 3
3168 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3169 #define MICROLABEL_IMPORTED_FROM 5
3170 #define MICROLABEL_IMPORTED_BY_HEAD 6
3171 #define MICROLABEL_IMPORTED_BY 7
3173 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3175 int max_text_width = SXSIZE;
3176 int font_width = getFontWidth(font_nr);
3178 if (pos->align == ALIGN_CENTER)
3179 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3180 else if (pos->align == ALIGN_RIGHT)
3181 max_text_width = pos->x;
3183 max_text_width = SXSIZE - pos->x;
3185 return max_text_width / font_width;
3188 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3190 char label_text[MAX_OUTPUT_LINESIZE + 1];
3191 int max_len_label_text;
3192 int font_nr = pos->font;
3195 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3198 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3199 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3200 mode == MICROLABEL_IMPORTED_BY_HEAD)
3201 font_nr = pos->font_alt;
3203 max_len_label_text = getMaxTextLength(pos, font_nr);
3205 if (pos->size != -1)
3206 max_len_label_text = pos->size;
3208 for (i = 0; i < max_len_label_text; i++)
3209 label_text[i] = ' ';
3210 label_text[max_len_label_text] = '\0';
3212 if (strlen(label_text) > 0)
3213 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3216 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3217 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3218 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3219 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3220 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3221 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3222 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3223 max_len_label_text);
3224 label_text[max_len_label_text] = '\0';
3226 if (strlen(label_text) > 0)
3227 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3229 redraw_mask |= REDRAW_FIELD;
3232 static void DrawPreviewLevelLabel(int mode)
3234 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3237 static void DrawPreviewLevelInfo(int mode)
3239 if (mode == MICROLABEL_LEVEL_NAME)
3240 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3241 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3242 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3245 static void DrawPreviewLevelExt(boolean restart)
3247 static unsigned int scroll_delay = 0;
3248 static unsigned int label_delay = 0;
3249 static int from_x, from_y, scroll_direction;
3250 static int label_state, label_counter;
3251 unsigned int scroll_delay_value = preview.step_delay;
3252 boolean show_level_border = (BorderElement != EL_EMPTY);
3253 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3254 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3261 if (preview.anim_mode == ANIM_CENTERED)
3263 if (level_xsize > preview.xsize)
3264 from_x = (level_xsize - preview.xsize) / 2;
3265 if (level_ysize > preview.ysize)
3266 from_y = (level_ysize - preview.ysize) / 2;
3269 from_x += preview.xoffset;
3270 from_y += preview.yoffset;
3272 scroll_direction = MV_RIGHT;
3276 DrawPreviewLevelPlayfield(from_x, from_y);
3277 DrawPreviewLevelLabel(label_state);
3279 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3280 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3282 /* initialize delay counters */
3283 DelayReached(&scroll_delay, 0);
3284 DelayReached(&label_delay, 0);
3286 if (leveldir_current->name)
3288 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3289 char label_text[MAX_OUTPUT_LINESIZE + 1];
3290 int font_nr = pos->font;
3291 int max_len_label_text = getMaxTextLength(pos, font_nr);
3293 if (pos->size != -1)
3294 max_len_label_text = pos->size;
3296 strncpy(label_text, leveldir_current->name, max_len_label_text);
3297 label_text[max_len_label_text] = '\0';
3299 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3300 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3306 /* scroll preview level, if needed */
3307 if (preview.anim_mode != ANIM_NONE &&
3308 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3309 DelayReached(&scroll_delay, scroll_delay_value))
3311 switch (scroll_direction)
3316 from_x -= preview.step_offset;
3317 from_x = (from_x < 0 ? 0 : from_x);
3320 scroll_direction = MV_UP;
3324 if (from_x < level_xsize - preview.xsize)
3326 from_x += preview.step_offset;
3327 from_x = (from_x > level_xsize - preview.xsize ?
3328 level_xsize - preview.xsize : from_x);
3331 scroll_direction = MV_DOWN;
3337 from_y -= preview.step_offset;
3338 from_y = (from_y < 0 ? 0 : from_y);
3341 scroll_direction = MV_RIGHT;
3345 if (from_y < level_ysize - preview.ysize)
3347 from_y += preview.step_offset;
3348 from_y = (from_y > level_ysize - preview.ysize ?
3349 level_ysize - preview.ysize : from_y);
3352 scroll_direction = MV_LEFT;
3359 DrawPreviewLevelPlayfield(from_x, from_y);
3362 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
3363 /* redraw micro level label, if needed */
3364 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3365 !strEqual(level.author, ANONYMOUS_NAME) &&
3366 !strEqual(level.author, leveldir_current->name) &&
3367 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3369 int max_label_counter = 23;
3371 if (leveldir_current->imported_from != NULL &&
3372 strlen(leveldir_current->imported_from) > 0)
3373 max_label_counter += 14;
3374 if (leveldir_current->imported_by != NULL &&
3375 strlen(leveldir_current->imported_by) > 0)
3376 max_label_counter += 14;
3378 label_counter = (label_counter + 1) % max_label_counter;
3379 label_state = (label_counter >= 0 && label_counter <= 7 ?
3380 MICROLABEL_LEVEL_NAME :
3381 label_counter >= 9 && label_counter <= 12 ?
3382 MICROLABEL_LEVEL_AUTHOR_HEAD :
3383 label_counter >= 14 && label_counter <= 21 ?
3384 MICROLABEL_LEVEL_AUTHOR :
3385 label_counter >= 23 && label_counter <= 26 ?
3386 MICROLABEL_IMPORTED_FROM_HEAD :
3387 label_counter >= 28 && label_counter <= 35 ?
3388 MICROLABEL_IMPORTED_FROM :
3389 label_counter >= 37 && label_counter <= 40 ?
3390 MICROLABEL_IMPORTED_BY_HEAD :
3391 label_counter >= 42 && label_counter <= 49 ?
3392 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3394 if (leveldir_current->imported_from == NULL &&
3395 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3396 label_state == MICROLABEL_IMPORTED_FROM))
3397 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3398 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3400 DrawPreviewLevelLabel(label_state);
3404 void DrawPreviewLevelInitial()
3406 DrawPreviewLevelExt(TRUE);
3409 void DrawPreviewLevelAnimation()
3411 DrawPreviewLevelExt(FALSE);
3414 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3415 int graphic, int sync_frame,
3418 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3420 if (mask_mode == USE_MASKING)
3421 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3423 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3426 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3427 int graphic, int sync_frame, int mask_mode)
3429 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3431 if (mask_mode == USE_MASKING)
3432 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3434 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3437 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3439 int lx = LEVELX(x), ly = LEVELY(y);
3441 if (!IN_SCR_FIELD(x, y))
3444 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3445 graphic, GfxFrame[lx][ly], NO_MASKING);
3447 MarkTileDirty(x, y);
3450 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3452 int lx = LEVELX(x), ly = LEVELY(y);
3454 if (!IN_SCR_FIELD(x, y))
3457 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3458 graphic, GfxFrame[lx][ly], NO_MASKING);
3459 MarkTileDirty(x, y);
3462 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3464 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3467 void DrawLevelElementAnimation(int x, int y, int element)
3469 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3471 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3474 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3476 int sx = SCREENX(x), sy = SCREENY(y);
3478 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3481 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3484 DrawGraphicAnimation(sx, sy, graphic);
3487 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3488 DrawLevelFieldCrumbled(x, y);
3490 if (GFX_CRUMBLED(Feld[x][y]))
3491 DrawLevelFieldCrumbled(x, y);
3495 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3497 int sx = SCREENX(x), sy = SCREENY(y);
3500 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3503 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3505 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3508 DrawGraphicAnimation(sx, sy, graphic);
3510 if (GFX_CRUMBLED(element))
3511 DrawLevelFieldCrumbled(x, y);
3514 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3516 if (player->use_murphy)
3518 /* this works only because currently only one player can be "murphy" ... */
3519 static int last_horizontal_dir = MV_LEFT;
3520 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3522 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3523 last_horizontal_dir = move_dir;
3525 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3527 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3529 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3535 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3538 static boolean equalGraphics(int graphic1, int graphic2)
3540 struct GraphicInfo *g1 = &graphic_info[graphic1];
3541 struct GraphicInfo *g2 = &graphic_info[graphic2];
3543 return (g1->bitmap == g2->bitmap &&
3544 g1->src_x == g2->src_x &&
3545 g1->src_y == g2->src_y &&
3546 g1->anim_frames == g2->anim_frames &&
3547 g1->anim_delay == g2->anim_delay &&
3548 g1->anim_mode == g2->anim_mode);
3551 void DrawAllPlayers()
3555 for (i = 0; i < MAX_PLAYERS; i++)
3556 if (stored_player[i].active)
3557 DrawPlayer(&stored_player[i]);
3560 void DrawPlayerField(int x, int y)
3562 if (!IS_PLAYER(x, y))
3565 DrawPlayer(PLAYERINFO(x, y));
3568 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3570 void DrawPlayer(struct PlayerInfo *player)
3572 int jx = player->jx;
3573 int jy = player->jy;
3574 int move_dir = player->MovDir;
3575 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3576 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3577 int last_jx = (player->is_moving ? jx - dx : jx);
3578 int last_jy = (player->is_moving ? jy - dy : jy);
3579 int next_jx = jx + dx;
3580 int next_jy = jy + dy;
3581 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3582 boolean player_is_opaque = FALSE;
3583 int sx = SCREENX(jx), sy = SCREENY(jy);
3584 int sxx = 0, syy = 0;
3585 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3587 int action = ACTION_DEFAULT;
3588 int last_player_graphic = getPlayerGraphic(player, move_dir);
3589 int last_player_frame = player->Frame;
3592 /* GfxElement[][] is set to the element the player is digging or collecting;
3593 remove also for off-screen player if the player is not moving anymore */
3594 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3595 GfxElement[jx][jy] = EL_UNDEFINED;
3597 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3601 if (!IN_LEV_FIELD(jx, jy))
3603 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3604 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3605 printf("DrawPlayerField(): This should never happen!\n");
3610 if (element == EL_EXPLOSION)
3613 action = (player->is_pushing ? ACTION_PUSHING :
3614 player->is_digging ? ACTION_DIGGING :
3615 player->is_collecting ? ACTION_COLLECTING :
3616 player->is_moving ? ACTION_MOVING :
3617 player->is_snapping ? ACTION_SNAPPING :
3618 player->is_dropping ? ACTION_DROPPING :
3619 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3621 if (player->is_waiting)
3622 move_dir = player->dir_waiting;
3624 InitPlayerGfxAnimation(player, action, move_dir);
3626 /* ----------------------------------------------------------------------- */
3627 /* draw things in the field the player is leaving, if needed */
3628 /* ----------------------------------------------------------------------- */
3630 if (player->is_moving)
3632 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3634 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3636 if (last_element == EL_DYNAMITE_ACTIVE ||
3637 last_element == EL_EM_DYNAMITE_ACTIVE ||
3638 last_element == EL_SP_DISK_RED_ACTIVE)
3639 DrawDynamite(last_jx, last_jy);
3641 DrawLevelFieldThruMask(last_jx, last_jy);
3643 else if (last_element == EL_DYNAMITE_ACTIVE ||
3644 last_element == EL_EM_DYNAMITE_ACTIVE ||
3645 last_element == EL_SP_DISK_RED_ACTIVE)
3646 DrawDynamite(last_jx, last_jy);
3648 /* !!! this is not enough to prevent flickering of players which are
3649 moving next to each others without a free tile between them -- this
3650 can only be solved by drawing all players layer by layer (first the
3651 background, then the foreground etc.) !!! => TODO */
3652 else if (!IS_PLAYER(last_jx, last_jy))
3653 DrawLevelField(last_jx, last_jy);
3656 DrawLevelField(last_jx, last_jy);
3659 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3660 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3663 if (!IN_SCR_FIELD(sx, sy))
3666 /* ----------------------------------------------------------------------- */
3667 /* draw things behind the player, if needed */
3668 /* ----------------------------------------------------------------------- */
3671 DrawLevelElement(jx, jy, Back[jx][jy]);
3672 else if (IS_ACTIVE_BOMB(element))
3673 DrawLevelElement(jx, jy, EL_EMPTY);
3676 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3678 int old_element = GfxElement[jx][jy];
3679 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3680 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3682 if (GFX_CRUMBLED(old_element))
3683 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3685 DrawGraphic(sx, sy, old_graphic, frame);
3687 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3688 player_is_opaque = TRUE;
3692 GfxElement[jx][jy] = EL_UNDEFINED;
3694 /* make sure that pushed elements are drawn with correct frame rate */
3695 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3697 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3698 GfxFrame[jx][jy] = player->StepFrame;
3700 DrawLevelField(jx, jy);
3704 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3705 /* ----------------------------------------------------------------------- */
3706 /* draw player himself */
3707 /* ----------------------------------------------------------------------- */
3709 graphic = getPlayerGraphic(player, move_dir);
3711 /* in the case of changed player action or direction, prevent the current
3712 animation frame from being restarted for identical animations */
3713 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3714 player->Frame = last_player_frame;
3716 frame = getGraphicAnimationFrame(graphic, player->Frame);
3720 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3721 sxx = player->GfxPos;
3723 syy = player->GfxPos;
3726 if (player_is_opaque)
3727 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3729 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3731 if (SHIELD_ON(player))
3733 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3734 IMG_SHIELD_NORMAL_ACTIVE);
3735 int frame = getGraphicAnimationFrame(graphic, -1);
3737 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3741 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3744 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3745 sxx = player->GfxPos;
3747 syy = player->GfxPos;
3751 /* ----------------------------------------------------------------------- */
3752 /* draw things the player is pushing, if needed */
3753 /* ----------------------------------------------------------------------- */
3755 if (player->is_pushing && player->is_moving)
3757 int px = SCREENX(jx), py = SCREENY(jy);
3758 int pxx = (TILEX - ABS(sxx)) * dx;
3759 int pyy = (TILEY - ABS(syy)) * dy;
3760 int gfx_frame = GfxFrame[jx][jy];
3766 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3768 element = Feld[next_jx][next_jy];
3769 gfx_frame = GfxFrame[next_jx][next_jy];
3772 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3774 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3775 frame = getGraphicAnimationFrame(graphic, sync_frame);
3777 /* draw background element under pushed element (like the Sokoban field) */
3778 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3780 /* this allows transparent pushing animation over non-black background */
3783 DrawLevelElement(jx, jy, Back[jx][jy]);
3785 DrawLevelElement(jx, jy, EL_EMPTY);
3787 if (Back[next_jx][next_jy])
3788 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3790 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3792 else if (Back[next_jx][next_jy])
3793 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3796 /* do not draw (EM style) pushing animation when pushing is finished */
3797 /* (two-tile animations usually do not contain start and end frame) */
3798 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3799 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3801 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3803 /* masked drawing is needed for EMC style (double) movement graphics */
3804 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3805 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3809 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3810 /* ----------------------------------------------------------------------- */
3811 /* draw player himself */
3812 /* ----------------------------------------------------------------------- */
3814 graphic = getPlayerGraphic(player, move_dir);
3816 /* in the case of changed player action or direction, prevent the current
3817 animation frame from being restarted for identical animations */
3818 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3819 player->Frame = last_player_frame;
3821 frame = getGraphicAnimationFrame(graphic, player->Frame);
3825 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3826 sxx = player->GfxPos;
3828 syy = player->GfxPos;
3831 if (player_is_opaque)
3832 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3834 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3836 if (SHIELD_ON(player))
3838 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3839 IMG_SHIELD_NORMAL_ACTIVE);
3840 int frame = getGraphicAnimationFrame(graphic, -1);
3842 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3846 /* ----------------------------------------------------------------------- */
3847 /* draw things in front of player (active dynamite or dynabombs) */
3848 /* ----------------------------------------------------------------------- */
3850 if (IS_ACTIVE_BOMB(element))
3852 graphic = el2img(element);
3853 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3855 if (game.emulation == EMU_SUPAPLEX)
3856 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3858 DrawGraphicThruMask(sx, sy, graphic, frame);
3861 if (player_is_moving && last_element == EL_EXPLOSION)
3863 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3864 GfxElement[last_jx][last_jy] : EL_EMPTY);
3865 int graphic = el_act2img(element, ACTION_EXPLODING);
3866 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3867 int phase = ExplodePhase[last_jx][last_jy] - 1;
3868 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3871 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3874 /* ----------------------------------------------------------------------- */
3875 /* draw elements the player is just walking/passing through/under */
3876 /* ----------------------------------------------------------------------- */
3878 if (player_is_moving)
3880 /* handle the field the player is leaving ... */
3881 if (IS_ACCESSIBLE_INSIDE(last_element))
3882 DrawLevelField(last_jx, last_jy);
3883 else if (IS_ACCESSIBLE_UNDER(last_element))
3884 DrawLevelFieldThruMask(last_jx, last_jy);
3887 /* do not redraw accessible elements if the player is just pushing them */
3888 if (!player_is_moving || !player->is_pushing)
3890 /* ... and the field the player is entering */
3891 if (IS_ACCESSIBLE_INSIDE(element))
3892 DrawLevelField(jx, jy);
3893 else if (IS_ACCESSIBLE_UNDER(element))
3894 DrawLevelFieldThruMask(jx, jy);
3897 MarkTileDirty(sx, sy);
3900 /* ------------------------------------------------------------------------- */
3902 void WaitForEventToContinue()
3904 boolean still_wait = TRUE;
3906 if (program.headless)
3909 /* simulate releasing mouse button over last gadget, if still pressed */
3911 HandleGadgets(-1, -1, 0);
3913 button_status = MB_RELEASED;
3921 if (NextValidEvent(&event))
3925 case EVENT_BUTTONPRESS:
3926 case EVENT_KEYPRESS:
3927 #if defined(TARGET_SDL2)
3928 case SDL_CONTROLLERBUTTONDOWN:
3930 case SDL_JOYBUTTONDOWN:
3934 case EVENT_KEYRELEASE:
3935 ClearPlayerAction();
3939 HandleOtherEvents(&event);
3943 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3952 #define MAX_REQUEST_LINES 13
3953 #define MAX_REQUEST_LINE_FONT1_LEN 7
3954 #define MAX_REQUEST_LINE_FONT2_LEN 10
3956 static int RequestHandleEvents(unsigned int req_state)
3958 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3959 local_player->LevelSolved_GameEnd);
3960 int width = request.width;
3961 int height = request.height;
3965 setRequestPosition(&sx, &sy, FALSE);
3967 button_status = MB_RELEASED;
3969 request_gadget_id = -1;
3976 /* the MM game engine does not use a special (scrollable) field buffer */
3977 if (level.game_engine_type != GAME_ENGINE_TYPE_MM)
3978 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3980 HandleGameActions();
3982 SetDrawtoField(DRAW_TO_BACKBUFFER);
3984 if (global.use_envelope_request)
3986 /* copy current state of request area to middle of playfield area */
3987 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
3995 while (NextValidEvent(&event))
3999 case EVENT_BUTTONPRESS:
4000 case EVENT_BUTTONRELEASE:
4001 case EVENT_MOTIONNOTIFY:
4005 if (event.type == EVENT_MOTIONNOTIFY)
4010 motion_status = TRUE;
4011 mx = ((MotionEvent *) &event)->x;
4012 my = ((MotionEvent *) &event)->y;
4016 motion_status = FALSE;
4017 mx = ((ButtonEvent *) &event)->x;
4018 my = ((ButtonEvent *) &event)->y;
4019 if (event.type == EVENT_BUTTONPRESS)
4020 button_status = ((ButtonEvent *) &event)->button;
4022 button_status = MB_RELEASED;
4025 /* this sets 'request_gadget_id' */
4026 HandleGadgets(mx, my, button_status);
4028 switch (request_gadget_id)
4030 case TOOL_CTRL_ID_YES:
4033 case TOOL_CTRL_ID_NO:
4036 case TOOL_CTRL_ID_CONFIRM:
4037 result = TRUE | FALSE;
4040 case TOOL_CTRL_ID_PLAYER_1:
4043 case TOOL_CTRL_ID_PLAYER_2:
4046 case TOOL_CTRL_ID_PLAYER_3:
4049 case TOOL_CTRL_ID_PLAYER_4:
4060 #if defined(TARGET_SDL2)
4061 case SDL_WINDOWEVENT:
4062 HandleWindowEvent((WindowEvent *) &event);
4065 case SDL_APP_WILLENTERBACKGROUND:
4066 case SDL_APP_DIDENTERBACKGROUND:
4067 case SDL_APP_WILLENTERFOREGROUND:
4068 case SDL_APP_DIDENTERFOREGROUND:
4069 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4073 case EVENT_KEYPRESS:
4075 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4080 if (req_state & REQ_CONFIRM)
4085 #if defined(TARGET_SDL2)
4088 #if defined(KSYM_Rewind)
4089 case KSYM_Rewind: /* for Amazon Fire TV remote */
4096 #if defined(TARGET_SDL2)
4098 #if defined(KSYM_FastForward)
4099 case KSYM_FastForward: /* for Amazon Fire TV remote */
4106 HandleKeysDebug(key);
4110 if (req_state & REQ_PLAYER)
4116 case EVENT_KEYRELEASE:
4117 ClearPlayerAction();
4120 #if defined(TARGET_SDL2)
4121 case SDL_CONTROLLERBUTTONDOWN:
4122 switch (event.cbutton.button)
4124 case SDL_CONTROLLER_BUTTON_A:
4125 case SDL_CONTROLLER_BUTTON_X:
4126 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4130 case SDL_CONTROLLER_BUTTON_B:
4131 case SDL_CONTROLLER_BUTTON_Y:
4132 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4133 case SDL_CONTROLLER_BUTTON_BACK:
4138 if (req_state & REQ_PLAYER)
4143 case SDL_CONTROLLERBUTTONUP:
4144 HandleJoystickEvent(&event);
4145 ClearPlayerAction();
4150 HandleOtherEvents(&event);
4155 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4157 int joy = AnyJoystick();
4159 if (joy & JOY_BUTTON_1)
4161 else if (joy & JOY_BUTTON_2)
4167 if (global.use_envelope_request)
4169 /* copy back current state of pressed buttons inside request area */
4170 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4180 static boolean RequestDoor(char *text, unsigned int req_state)
4182 unsigned int old_door_state;
4183 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4184 int font_nr = FONT_TEXT_2;
4189 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4191 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4192 font_nr = FONT_TEXT_1;
4195 if (game_status == GAME_MODE_PLAYING)
4196 BlitScreenToBitmap(backbuffer);
4198 /* disable deactivated drawing when quick-loading level tape recording */
4199 if (tape.playing && tape.deactivate_display)
4200 TapeDeactivateDisplayOff(TRUE);
4202 SetMouseCursor(CURSOR_DEFAULT);
4204 #if defined(NETWORK_AVALIABLE)
4205 /* pause network game while waiting for request to answer */
4206 if (options.network &&
4207 game_status == GAME_MODE_PLAYING &&
4208 req_state & REQUEST_WAIT_FOR_INPUT)
4209 SendToServer_PausePlaying();
4212 old_door_state = GetDoorState();
4214 /* simulate releasing mouse button over last gadget, if still pressed */
4216 HandleGadgets(-1, -1, 0);
4220 /* draw released gadget before proceeding */
4223 if (old_door_state & DOOR_OPEN_1)
4225 CloseDoor(DOOR_CLOSE_1);
4227 /* save old door content */
4228 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4229 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4232 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4233 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4235 /* clear door drawing field */
4236 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4238 /* force DOOR font inside door area */
4239 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4241 /* write text for request */
4242 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4244 char text_line[max_request_line_len + 1];
4250 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4252 tc = *(text_ptr + tx);
4253 // if (!tc || tc == ' ')
4254 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4258 if ((tc == '?' || tc == '!') && tl == 0)
4268 strncpy(text_line, text_ptr, tl);
4271 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4272 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4273 text_line, font_nr);
4275 text_ptr += tl + (tc == ' ' ? 1 : 0);
4276 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4281 if (req_state & REQ_ASK)
4283 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4284 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4286 else if (req_state & REQ_CONFIRM)
4288 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4290 else if (req_state & REQ_PLAYER)
4292 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4293 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4294 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4295 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4298 /* copy request gadgets to door backbuffer */
4299 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4301 OpenDoor(DOOR_OPEN_1);
4303 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4305 if (game_status == GAME_MODE_PLAYING)
4307 SetPanelBackground();
4308 SetDrawBackgroundMask(REDRAW_DOOR_1);
4312 SetDrawBackgroundMask(REDRAW_FIELD);
4318 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4320 // ---------- handle request buttons ----------
4321 result = RequestHandleEvents(req_state);
4325 if (!(req_state & REQ_STAY_OPEN))
4327 CloseDoor(DOOR_CLOSE_1);
4329 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4330 (req_state & REQ_REOPEN))
4331 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4336 if (game_status == GAME_MODE_PLAYING)
4338 SetPanelBackground();
4339 SetDrawBackgroundMask(REDRAW_DOOR_1);
4343 SetDrawBackgroundMask(REDRAW_FIELD);
4346 #if defined(NETWORK_AVALIABLE)
4347 /* continue network game after request */
4348 if (options.network &&
4349 game_status == GAME_MODE_PLAYING &&
4350 req_state & REQUEST_WAIT_FOR_INPUT)
4351 SendToServer_ContinuePlaying();
4354 /* restore deactivated drawing when quick-loading level tape recording */
4355 if (tape.playing && tape.deactivate_display)
4356 TapeDeactivateDisplayOn();
4361 static boolean RequestEnvelope(char *text, unsigned int req_state)
4365 if (game_status == GAME_MODE_PLAYING)
4366 BlitScreenToBitmap(backbuffer);
4368 /* disable deactivated drawing when quick-loading level tape recording */
4369 if (tape.playing && tape.deactivate_display)
4370 TapeDeactivateDisplayOff(TRUE);
4372 SetMouseCursor(CURSOR_DEFAULT);
4374 #if defined(NETWORK_AVALIABLE)
4375 /* pause network game while waiting for request to answer */
4376 if (options.network &&
4377 game_status == GAME_MODE_PLAYING &&
4378 req_state & REQUEST_WAIT_FOR_INPUT)
4379 SendToServer_PausePlaying();
4382 /* simulate releasing mouse button over last gadget, if still pressed */
4384 HandleGadgets(-1, -1, 0);
4388 // (replace with setting corresponding request background)
4389 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4390 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4392 /* clear door drawing field */
4393 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4395 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4397 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4399 if (game_status == GAME_MODE_PLAYING)
4401 SetPanelBackground();
4402 SetDrawBackgroundMask(REDRAW_DOOR_1);
4406 SetDrawBackgroundMask(REDRAW_FIELD);
4412 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4414 // ---------- handle request buttons ----------
4415 result = RequestHandleEvents(req_state);
4419 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4423 if (game_status == GAME_MODE_PLAYING)
4425 SetPanelBackground();
4426 SetDrawBackgroundMask(REDRAW_DOOR_1);
4430 SetDrawBackgroundMask(REDRAW_FIELD);
4433 #if defined(NETWORK_AVALIABLE)
4434 /* continue network game after request */
4435 if (options.network &&
4436 game_status == GAME_MODE_PLAYING &&
4437 req_state & REQUEST_WAIT_FOR_INPUT)
4438 SendToServer_ContinuePlaying();
4441 /* restore deactivated drawing when quick-loading level tape recording */
4442 if (tape.playing && tape.deactivate_display)
4443 TapeDeactivateDisplayOn();
4448 boolean Request(char *text, unsigned int req_state)
4450 boolean overlay_active = GetOverlayActive();
4453 SetOverlayActive(FALSE);
4455 if (global.use_envelope_request)
4456 result = RequestEnvelope(text, req_state);
4458 result = RequestDoor(text, req_state);
4460 SetOverlayActive(overlay_active);
4465 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4467 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4468 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4471 if (dpo1->sort_priority != dpo2->sort_priority)
4472 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4474 compare_result = dpo1->nr - dpo2->nr;
4476 return compare_result;
4479 void InitGraphicCompatibilityInfo_Doors()
4485 struct DoorInfo *door;
4489 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4490 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4492 { -1, -1, -1, NULL }
4494 struct Rect door_rect_list[] =
4496 { DX, DY, DXSIZE, DYSIZE },
4497 { VX, VY, VXSIZE, VYSIZE }
4501 for (i = 0; doors[i].door_token != -1; i++)
4503 int door_token = doors[i].door_token;
4504 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4505 int part_1 = doors[i].part_1;
4506 int part_8 = doors[i].part_8;
4507 int part_2 = part_1 + 1;
4508 int part_3 = part_1 + 2;
4509 struct DoorInfo *door = doors[i].door;
4510 struct Rect *door_rect = &door_rect_list[door_index];
4511 boolean door_gfx_redefined = FALSE;
4513 /* check if any door part graphic definitions have been redefined */
4515 for (j = 0; door_part_controls[j].door_token != -1; j++)
4517 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4518 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4520 if (dpc->door_token == door_token && fi->redefined)
4521 door_gfx_redefined = TRUE;
4524 /* check for old-style door graphic/animation modifications */
4526 if (!door_gfx_redefined)
4528 if (door->anim_mode & ANIM_STATIC_PANEL)
4530 door->panel.step_xoffset = 0;
4531 door->panel.step_yoffset = 0;
4534 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4536 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4537 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4538 int num_door_steps, num_panel_steps;
4540 /* remove door part graphics other than the two default wings */
4542 for (j = 0; door_part_controls[j].door_token != -1; j++)
4544 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4545 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4547 if (dpc->graphic >= part_3 &&
4548 dpc->graphic <= part_8)
4552 /* set graphics and screen positions of the default wings */
4554 g_part_1->width = door_rect->width;
4555 g_part_1->height = door_rect->height;
4556 g_part_2->width = door_rect->width;
4557 g_part_2->height = door_rect->height;
4558 g_part_2->src_x = door_rect->width;
4559 g_part_2->src_y = g_part_1->src_y;
4561 door->part_2.x = door->part_1.x;
4562 door->part_2.y = door->part_1.y;
4564 if (door->width != -1)
4566 g_part_1->width = door->width;
4567 g_part_2->width = door->width;
4569 // special treatment for graphics and screen position of right wing
4570 g_part_2->src_x += door_rect->width - door->width;
4571 door->part_2.x += door_rect->width - door->width;
4574 if (door->height != -1)
4576 g_part_1->height = door->height;
4577 g_part_2->height = door->height;
4579 // special treatment for graphics and screen position of bottom wing
4580 g_part_2->src_y += door_rect->height - door->height;
4581 door->part_2.y += door_rect->height - door->height;
4584 /* set animation delays for the default wings and panels */
4586 door->part_1.step_delay = door->step_delay;
4587 door->part_2.step_delay = door->step_delay;
4588 door->panel.step_delay = door->step_delay;
4590 /* set animation draw order for the default wings */
4592 door->part_1.sort_priority = 2; /* draw left wing over ... */
4593 door->part_2.sort_priority = 1; /* ... right wing */
4595 /* set animation draw offset for the default wings */
4597 if (door->anim_mode & ANIM_HORIZONTAL)
4599 door->part_1.step_xoffset = door->step_offset;
4600 door->part_1.step_yoffset = 0;
4601 door->part_2.step_xoffset = door->step_offset * -1;
4602 door->part_2.step_yoffset = 0;
4604 num_door_steps = g_part_1->width / door->step_offset;
4606 else // ANIM_VERTICAL
4608 door->part_1.step_xoffset = 0;
4609 door->part_1.step_yoffset = door->step_offset;
4610 door->part_2.step_xoffset = 0;
4611 door->part_2.step_yoffset = door->step_offset * -1;
4613 num_door_steps = g_part_1->height / door->step_offset;
4616 /* set animation draw offset for the default panels */
4618 if (door->step_offset > 1)
4620 num_panel_steps = 2 * door_rect->height / door->step_offset;
4621 door->panel.start_step = num_panel_steps - num_door_steps;
4622 door->panel.start_step_closing = door->panel.start_step;
4626 num_panel_steps = door_rect->height / door->step_offset;
4627 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4628 door->panel.start_step_closing = door->panel.start_step;
4629 door->panel.step_delay *= 2;
4640 for (i = 0; door_part_controls[i].door_token != -1; i++)
4642 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4643 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4645 /* initialize "start_step_opening" and "start_step_closing", if needed */
4646 if (dpc->pos->start_step_opening == 0 &&
4647 dpc->pos->start_step_closing == 0)
4649 // dpc->pos->start_step_opening = dpc->pos->start_step;
4650 dpc->pos->start_step_closing = dpc->pos->start_step;
4653 /* fill structure for door part draw order (sorted below) */
4655 dpo->sort_priority = dpc->pos->sort_priority;
4658 /* sort door part controls according to sort_priority and graphic number */
4659 qsort(door_part_order, MAX_DOOR_PARTS,
4660 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4663 unsigned int OpenDoor(unsigned int door_state)
4665 if (door_state & DOOR_COPY_BACK)
4667 if (door_state & DOOR_OPEN_1)
4668 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4669 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4671 if (door_state & DOOR_OPEN_2)
4672 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4673 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4675 door_state &= ~DOOR_COPY_BACK;
4678 return MoveDoor(door_state);
4681 unsigned int CloseDoor(unsigned int door_state)
4683 unsigned int old_door_state = GetDoorState();
4685 if (!(door_state & DOOR_NO_COPY_BACK))
4687 if (old_door_state & DOOR_OPEN_1)
4688 BlitBitmap(backbuffer, bitmap_db_door_1,
4689 DX, DY, DXSIZE, DYSIZE, 0, 0);
4691 if (old_door_state & DOOR_OPEN_2)
4692 BlitBitmap(backbuffer, bitmap_db_door_2,
4693 VX, VY, VXSIZE, VYSIZE, 0, 0);
4695 door_state &= ~DOOR_NO_COPY_BACK;
4698 return MoveDoor(door_state);
4701 unsigned int GetDoorState()
4703 return MoveDoor(DOOR_GET_STATE);
4706 unsigned int SetDoorState(unsigned int door_state)
4708 return MoveDoor(door_state | DOOR_SET_STATE);
4711 int euclid(int a, int b)
4713 return (b ? euclid(b, a % b) : a);
4716 unsigned int MoveDoor(unsigned int door_state)
4718 struct Rect door_rect_list[] =
4720 { DX, DY, DXSIZE, DYSIZE },
4721 { VX, VY, VXSIZE, VYSIZE }
4723 static int door1 = DOOR_CLOSE_1;
4724 static int door2 = DOOR_CLOSE_2;
4725 unsigned int door_delay = 0;
4726 unsigned int door_delay_value;
4729 if (door_state == DOOR_GET_STATE)
4730 return (door1 | door2);
4732 if (door_state & DOOR_SET_STATE)
4734 if (door_state & DOOR_ACTION_1)
4735 door1 = door_state & DOOR_ACTION_1;
4736 if (door_state & DOOR_ACTION_2)
4737 door2 = door_state & DOOR_ACTION_2;
4739 return (door1 | door2);
4742 if (!(door_state & DOOR_FORCE_REDRAW))
4744 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4745 door_state &= ~DOOR_OPEN_1;
4746 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4747 door_state &= ~DOOR_CLOSE_1;
4748 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4749 door_state &= ~DOOR_OPEN_2;
4750 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4751 door_state &= ~DOOR_CLOSE_2;
4754 if (global.autoplay_leveldir)
4756 door_state |= DOOR_NO_DELAY;
4757 door_state &= ~DOOR_CLOSE_ALL;
4760 if (game_status == GAME_MODE_EDITOR)
4761 door_state |= DOOR_NO_DELAY;
4763 if (door_state & DOOR_ACTION)
4765 boolean door_panel_drawn[NUM_DOORS];
4766 boolean panel_has_doors[NUM_DOORS];
4767 boolean door_part_skip[MAX_DOOR_PARTS];
4768 boolean door_part_done[MAX_DOOR_PARTS];
4769 boolean door_part_done_all;
4770 int num_steps[MAX_DOOR_PARTS];
4771 int max_move_delay = 0; // delay for complete animations of all doors
4772 int max_step_delay = 0; // delay (ms) between two animation frames
4773 int num_move_steps = 0; // number of animation steps for all doors
4774 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4775 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4776 int current_move_delay = 0;
4780 for (i = 0; i < NUM_DOORS; i++)
4781 panel_has_doors[i] = FALSE;
4783 for (i = 0; i < MAX_DOOR_PARTS; i++)
4785 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4786 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4787 int door_token = dpc->door_token;
4789 door_part_done[i] = FALSE;
4790 door_part_skip[i] = (!(door_state & door_token) ||
4794 for (i = 0; i < MAX_DOOR_PARTS; i++)
4796 int nr = door_part_order[i].nr;
4797 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4798 struct DoorPartPosInfo *pos = dpc->pos;
4799 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4800 int door_token = dpc->door_token;
4801 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4802 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4803 int step_xoffset = ABS(pos->step_xoffset);
4804 int step_yoffset = ABS(pos->step_yoffset);
4805 int step_delay = pos->step_delay;
4806 int current_door_state = door_state & door_token;
4807 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4808 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4809 boolean part_opening = (is_panel ? door_closing : door_opening);
4810 int start_step = (part_opening ? pos->start_step_opening :
4811 pos->start_step_closing);
4812 float move_xsize = (step_xoffset ? g->width : 0);
4813 float move_ysize = (step_yoffset ? g->height : 0);
4814 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4815 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4816 int move_steps = (move_xsteps && move_ysteps ?
4817 MIN(move_xsteps, move_ysteps) :
4818 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4819 int move_delay = move_steps * step_delay;
4821 if (door_part_skip[nr])
4824 max_move_delay = MAX(max_move_delay, move_delay);
4825 max_step_delay = (max_step_delay == 0 ? step_delay :
4826 euclid(max_step_delay, step_delay));
4827 num_steps[nr] = move_steps;
4831 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4833 panel_has_doors[door_index] = TRUE;
4837 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4839 num_move_steps = max_move_delay / max_step_delay;
4840 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4842 door_delay_value = max_step_delay;
4844 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4846 start = num_move_steps - 1;
4850 /* opening door sound has priority over simultaneously closing door */
4851 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4852 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4853 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4854 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4857 for (k = start; k < num_move_steps; k++)
4859 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4861 door_part_done_all = TRUE;
4863 for (i = 0; i < NUM_DOORS; i++)
4864 door_panel_drawn[i] = FALSE;
4866 for (i = 0; i < MAX_DOOR_PARTS; i++)
4868 int nr = door_part_order[i].nr;
4869 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4870 struct DoorPartPosInfo *pos = dpc->pos;
4871 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4872 int door_token = dpc->door_token;
4873 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4874 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4875 boolean is_panel_and_door_has_closed = FALSE;
4876 struct Rect *door_rect = &door_rect_list[door_index];
4877 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4879 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4880 int current_door_state = door_state & door_token;
4881 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4882 boolean door_closing = !door_opening;
4883 boolean part_opening = (is_panel ? door_closing : door_opening);
4884 boolean part_closing = !part_opening;
4885 int start_step = (part_opening ? pos->start_step_opening :
4886 pos->start_step_closing);
4887 int step_delay = pos->step_delay;
4888 int step_factor = step_delay / max_step_delay;
4889 int k1 = (step_factor ? k / step_factor + 1 : k);
4890 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4891 int kk = MAX(0, k2);
4894 int src_x, src_y, src_xx, src_yy;
4895 int dst_x, dst_y, dst_xx, dst_yy;
4898 if (door_part_skip[nr])
4901 if (!(door_state & door_token))
4909 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4910 int kk_door = MAX(0, k2_door);
4911 int sync_frame = kk_door * door_delay_value;
4912 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4914 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
4915 &g_src_x, &g_src_y);
4920 if (!door_panel_drawn[door_index])
4922 ClearRectangle(drawto, door_rect->x, door_rect->y,
4923 door_rect->width, door_rect->height);
4925 door_panel_drawn[door_index] = TRUE;
4928 // draw opening or closing door parts
4930 if (pos->step_xoffset < 0) // door part on right side
4933 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4936 if (dst_xx + width > door_rect->width)
4937 width = door_rect->width - dst_xx;
4939 else // door part on left side
4942 dst_xx = pos->x - kk * pos->step_xoffset;
4946 src_xx = ABS(dst_xx);
4950 width = g->width - src_xx;
4952 if (width > door_rect->width)
4953 width = door_rect->width;
4955 // printf("::: k == %d [%d] \n", k, start_step);
4958 if (pos->step_yoffset < 0) // door part on bottom side
4961 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4964 if (dst_yy + height > door_rect->height)
4965 height = door_rect->height - dst_yy;
4967 else // door part on top side
4970 dst_yy = pos->y - kk * pos->step_yoffset;
4974 src_yy = ABS(dst_yy);
4978 height = g->height - src_yy;
4981 src_x = g_src_x + src_xx;
4982 src_y = g_src_y + src_yy;
4984 dst_x = door_rect->x + dst_xx;
4985 dst_y = door_rect->y + dst_yy;
4987 is_panel_and_door_has_closed =
4990 panel_has_doors[door_index] &&
4991 k >= num_move_steps_doors_only - 1);
4993 if (width >= 0 && width <= g->width &&
4994 height >= 0 && height <= g->height &&
4995 !is_panel_and_door_has_closed)
4997 if (is_panel || !pos->draw_masked)
4998 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
5001 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
5005 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
5007 if ((part_opening && (width < 0 || height < 0)) ||
5008 (part_closing && (width >= g->width && height >= g->height)))
5009 door_part_done[nr] = TRUE;
5011 // continue door part animations, but not panel after door has closed
5012 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
5013 door_part_done_all = FALSE;
5016 if (!(door_state & DOOR_NO_DELAY))
5020 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
5022 current_move_delay += max_step_delay;
5024 /* prevent OS (Windows) from complaining about program not responding */
5028 if (door_part_done_all)
5033 if (door_state & DOOR_ACTION_1)
5034 door1 = door_state & DOOR_ACTION_1;
5035 if (door_state & DOOR_ACTION_2)
5036 door2 = door_state & DOOR_ACTION_2;
5038 // draw masked border over door area
5039 DrawMaskedBorder(REDRAW_DOOR_1);
5040 DrawMaskedBorder(REDRAW_DOOR_2);
5042 return (door1 | door2);
5045 static boolean useSpecialEditorDoor()
5047 int graphic = IMG_GLOBAL_BORDER_EDITOR;
5048 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
5050 // do not draw special editor door if editor border defined or redefined
5051 if (graphic_info[graphic].bitmap != NULL || redefined)
5054 // do not draw special editor door if global border defined to be empty
5055 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
5058 // do not draw special editor door if viewport definitions do not match
5062 EY + EYSIZE != VY + VYSIZE)
5068 void DrawSpecialEditorDoor()
5070 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5071 int top_border_width = gfx1->width;
5072 int top_border_height = gfx1->height;
5073 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5074 int ex = EX - outer_border;
5075 int ey = EY - outer_border;
5076 int vy = VY - outer_border;
5077 int exsize = EXSIZE + 2 * outer_border;
5079 if (!useSpecialEditorDoor())
5082 /* draw bigger level editor toolbox window */
5083 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5084 top_border_width, top_border_height, ex, ey - top_border_height);
5085 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5086 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5088 redraw_mask |= REDRAW_ALL;
5091 void UndrawSpecialEditorDoor()
5093 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5094 int top_border_width = gfx1->width;
5095 int top_border_height = gfx1->height;
5096 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5097 int ex = EX - outer_border;
5098 int ey = EY - outer_border;
5099 int ey_top = ey - top_border_height;
5100 int exsize = EXSIZE + 2 * outer_border;
5101 int eysize = EYSIZE + 2 * outer_border;
5103 if (!useSpecialEditorDoor())
5106 /* draw normal tape recorder window */
5107 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5109 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5110 ex, ey_top, top_border_width, top_border_height,
5112 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5113 ex, ey, exsize, eysize, ex, ey);
5117 // if screen background is set to "[NONE]", clear editor toolbox window
5118 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5119 ClearRectangle(drawto, ex, ey, exsize, eysize);
5122 redraw_mask |= REDRAW_ALL;
5126 /* ---------- new tool button stuff ---------------------------------------- */
5131 struct TextPosInfo *pos;
5134 } toolbutton_info[NUM_TOOL_BUTTONS] =
5137 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5138 TOOL_CTRL_ID_YES, "yes"
5141 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5142 TOOL_CTRL_ID_NO, "no"
5145 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5146 TOOL_CTRL_ID_CONFIRM, "confirm"
5149 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5150 TOOL_CTRL_ID_PLAYER_1, "player 1"
5153 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5154 TOOL_CTRL_ID_PLAYER_2, "player 2"
5157 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5158 TOOL_CTRL_ID_PLAYER_3, "player 3"
5161 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5162 TOOL_CTRL_ID_PLAYER_4, "player 4"
5166 void CreateToolButtons()
5170 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5172 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
5173 struct TextPosInfo *pos = toolbutton_info[i].pos;
5174 struct GadgetInfo *gi;
5175 Bitmap *deco_bitmap = None;
5176 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5177 unsigned int event_mask = GD_EVENT_RELEASED;
5180 int gd_x = gfx->src_x;
5181 int gd_y = gfx->src_y;
5182 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5183 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5186 if (global.use_envelope_request)
5187 setRequestPosition(&dx, &dy, TRUE);
5189 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5191 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5193 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5194 pos->size, &deco_bitmap, &deco_x, &deco_y);
5195 deco_xpos = (gfx->width - pos->size) / 2;
5196 deco_ypos = (gfx->height - pos->size) / 2;
5199 gi = CreateGadget(GDI_CUSTOM_ID, id,
5200 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5201 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
5202 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
5203 GDI_WIDTH, gfx->width,
5204 GDI_HEIGHT, gfx->height,
5205 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5206 GDI_STATE, GD_BUTTON_UNPRESSED,
5207 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5208 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5209 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5210 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5211 GDI_DECORATION_SIZE, pos->size, pos->size,
5212 GDI_DECORATION_SHIFTING, 1, 1,
5213 GDI_DIRECT_DRAW, FALSE,
5214 GDI_EVENT_MASK, event_mask,
5215 GDI_CALLBACK_ACTION, HandleToolButtons,
5219 Error(ERR_EXIT, "cannot create gadget");
5221 tool_gadget[id] = gi;
5225 void FreeToolButtons()
5229 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5230 FreeGadget(tool_gadget[i]);
5233 static void UnmapToolButtons()
5237 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5238 UnmapGadget(tool_gadget[i]);
5241 static void HandleToolButtons(struct GadgetInfo *gi)
5243 request_gadget_id = gi->custom_id;
5246 static struct Mapping_EM_to_RND_object
5249 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
5250 boolean is_backside; /* backside of moving element */
5256 em_object_mapping_list[] =
5259 Xblank, TRUE, FALSE,
5263 Yacid_splash_eB, FALSE, FALSE,
5264 EL_ACID_SPLASH_RIGHT, -1, -1
5267 Yacid_splash_wB, FALSE, FALSE,
5268 EL_ACID_SPLASH_LEFT, -1, -1
5271 #ifdef EM_ENGINE_BAD_ROLL
5273 Xstone_force_e, FALSE, FALSE,
5274 EL_ROCK, -1, MV_BIT_RIGHT
5277 Xstone_force_w, FALSE, FALSE,
5278 EL_ROCK, -1, MV_BIT_LEFT
5281 Xnut_force_e, FALSE, FALSE,
5282 EL_NUT, -1, MV_BIT_RIGHT
5285 Xnut_force_w, FALSE, FALSE,
5286 EL_NUT, -1, MV_BIT_LEFT
5289 Xspring_force_e, FALSE, FALSE,
5290 EL_SPRING, -1, MV_BIT_RIGHT
5293 Xspring_force_w, FALSE, FALSE,
5294 EL_SPRING, -1, MV_BIT_LEFT
5297 Xemerald_force_e, FALSE, FALSE,
5298 EL_EMERALD, -1, MV_BIT_RIGHT
5301 Xemerald_force_w, FALSE, FALSE,
5302 EL_EMERALD, -1, MV_BIT_LEFT
5305 Xdiamond_force_e, FALSE, FALSE,
5306 EL_DIAMOND, -1, MV_BIT_RIGHT
5309 Xdiamond_force_w, FALSE, FALSE,
5310 EL_DIAMOND, -1, MV_BIT_LEFT
5313 Xbomb_force_e, FALSE, FALSE,
5314 EL_BOMB, -1, MV_BIT_RIGHT
5317 Xbomb_force_w, FALSE, FALSE,
5318 EL_BOMB, -1, MV_BIT_LEFT
5320 #endif /* EM_ENGINE_BAD_ROLL */
5323 Xstone, TRUE, FALSE,
5327 Xstone_pause, FALSE, FALSE,
5331 Xstone_fall, FALSE, FALSE,
5335 Ystone_s, FALSE, FALSE,
5336 EL_ROCK, ACTION_FALLING, -1
5339 Ystone_sB, FALSE, TRUE,
5340 EL_ROCK, ACTION_FALLING, -1
5343 Ystone_e, FALSE, FALSE,
5344 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5347 Ystone_eB, FALSE, TRUE,
5348 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5351 Ystone_w, FALSE, FALSE,
5352 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5355 Ystone_wB, FALSE, TRUE,
5356 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5363 Xnut_pause, FALSE, FALSE,
5367 Xnut_fall, FALSE, FALSE,
5371 Ynut_s, FALSE, FALSE,
5372 EL_NUT, ACTION_FALLING, -1
5375 Ynut_sB, FALSE, TRUE,
5376 EL_NUT, ACTION_FALLING, -1
5379 Ynut_e, FALSE, FALSE,
5380 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5383 Ynut_eB, FALSE, TRUE,
5384 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5387 Ynut_w, FALSE, FALSE,
5388 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5391 Ynut_wB, FALSE, TRUE,
5392 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5395 Xbug_n, TRUE, FALSE,
5399 Xbug_e, TRUE, FALSE,
5400 EL_BUG_RIGHT, -1, -1
5403 Xbug_s, TRUE, FALSE,
5407 Xbug_w, TRUE, FALSE,
5411 Xbug_gon, FALSE, FALSE,
5415 Xbug_goe, FALSE, FALSE,
5416 EL_BUG_RIGHT, -1, -1
5419 Xbug_gos, FALSE, FALSE,
5423 Xbug_gow, FALSE, FALSE,
5427 Ybug_n, FALSE, FALSE,
5428 EL_BUG, ACTION_MOVING, MV_BIT_UP
5431 Ybug_nB, FALSE, TRUE,
5432 EL_BUG, ACTION_MOVING, MV_BIT_UP
5435 Ybug_e, FALSE, FALSE,
5436 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5439 Ybug_eB, FALSE, TRUE,
5440 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5443 Ybug_s, FALSE, FALSE,
5444 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5447 Ybug_sB, FALSE, TRUE,
5448 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5451 Ybug_w, FALSE, FALSE,
5452 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5455 Ybug_wB, FALSE, TRUE,
5456 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5459 Ybug_w_n, FALSE, FALSE,
5460 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5463 Ybug_n_e, FALSE, FALSE,
5464 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5467 Ybug_e_s, FALSE, FALSE,
5468 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5471 Ybug_s_w, FALSE, FALSE,
5472 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5475 Ybug_e_n, FALSE, FALSE,
5476 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5479 Ybug_s_e, FALSE, FALSE,
5480 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5483 Ybug_w_s, FALSE, FALSE,
5484 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5487 Ybug_n_w, FALSE, FALSE,
5488 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5491 Ybug_stone, FALSE, FALSE,
5492 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5495 Ybug_spring, FALSE, FALSE,
5496 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5499 Xtank_n, TRUE, FALSE,
5500 EL_SPACESHIP_UP, -1, -1
5503 Xtank_e, TRUE, FALSE,
5504 EL_SPACESHIP_RIGHT, -1, -1
5507 Xtank_s, TRUE, FALSE,
5508 EL_SPACESHIP_DOWN, -1, -1
5511 Xtank_w, TRUE, FALSE,
5512 EL_SPACESHIP_LEFT, -1, -1
5515 Xtank_gon, FALSE, FALSE,
5516 EL_SPACESHIP_UP, -1, -1
5519 Xtank_goe, FALSE, FALSE,
5520 EL_SPACESHIP_RIGHT, -1, -1
5523 Xtank_gos, FALSE, FALSE,
5524 EL_SPACESHIP_DOWN, -1, -1
5527 Xtank_gow, FALSE, FALSE,
5528 EL_SPACESHIP_LEFT, -1, -1
5531 Ytank_n, FALSE, FALSE,
5532 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5535 Ytank_nB, FALSE, TRUE,
5536 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5539 Ytank_e, FALSE, FALSE,
5540 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5543 Ytank_eB, FALSE, TRUE,
5544 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5547 Ytank_s, FALSE, FALSE,
5548 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5551 Ytank_sB, FALSE, TRUE,
5552 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5555 Ytank_w, FALSE, FALSE,
5556 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5559 Ytank_wB, FALSE, TRUE,
5560 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5563 Ytank_w_n, FALSE, FALSE,
5564 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5567 Ytank_n_e, FALSE, FALSE,
5568 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5571 Ytank_e_s, FALSE, FALSE,
5572 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5575 Ytank_s_w, FALSE, FALSE,
5576 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5579 Ytank_e_n, FALSE, FALSE,
5580 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5583 Ytank_s_e, FALSE, FALSE,
5584 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5587 Ytank_w_s, FALSE, FALSE,
5588 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5591 Ytank_n_w, FALSE, FALSE,
5592 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5595 Ytank_stone, FALSE, FALSE,
5596 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5599 Ytank_spring, FALSE, FALSE,
5600 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5603 Xandroid, TRUE, FALSE,
5604 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5607 Xandroid_1_n, FALSE, FALSE,
5608 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5611 Xandroid_2_n, FALSE, FALSE,
5612 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5615 Xandroid_1_e, FALSE, FALSE,
5616 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5619 Xandroid_2_e, FALSE, FALSE,
5620 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5623 Xandroid_1_w, FALSE, FALSE,
5624 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5627 Xandroid_2_w, FALSE, FALSE,
5628 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5631 Xandroid_1_s, FALSE, FALSE,
5632 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5635 Xandroid_2_s, FALSE, FALSE,
5636 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5639 Yandroid_n, FALSE, FALSE,
5640 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5643 Yandroid_nB, FALSE, TRUE,
5644 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5647 Yandroid_ne, FALSE, FALSE,
5648 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5651 Yandroid_neB, FALSE, TRUE,
5652 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5655 Yandroid_e, FALSE, FALSE,
5656 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5659 Yandroid_eB, FALSE, TRUE,
5660 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5663 Yandroid_se, FALSE, FALSE,
5664 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5667 Yandroid_seB, FALSE, TRUE,
5668 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5671 Yandroid_s, FALSE, FALSE,
5672 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5675 Yandroid_sB, FALSE, TRUE,
5676 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5679 Yandroid_sw, FALSE, FALSE,
5680 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5683 Yandroid_swB, FALSE, TRUE,
5684 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5687 Yandroid_w, FALSE, FALSE,
5688 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5691 Yandroid_wB, FALSE, TRUE,
5692 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5695 Yandroid_nw, FALSE, FALSE,
5696 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5699 Yandroid_nwB, FALSE, TRUE,
5700 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5703 Xspring, TRUE, FALSE,
5707 Xspring_pause, FALSE, FALSE,
5711 Xspring_e, FALSE, FALSE,
5715 Xspring_w, FALSE, FALSE,
5719 Xspring_fall, FALSE, FALSE,
5723 Yspring_s, FALSE, FALSE,
5724 EL_SPRING, ACTION_FALLING, -1
5727 Yspring_sB, FALSE, TRUE,
5728 EL_SPRING, ACTION_FALLING, -1
5731 Yspring_e, FALSE, FALSE,
5732 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5735 Yspring_eB, FALSE, TRUE,
5736 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5739 Yspring_w, FALSE, FALSE,
5740 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5743 Yspring_wB, FALSE, TRUE,
5744 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5747 Yspring_kill_e, FALSE, FALSE,
5748 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5751 Yspring_kill_eB, FALSE, TRUE,
5752 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5755 Yspring_kill_w, FALSE, FALSE,
5756 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5759 Yspring_kill_wB, FALSE, TRUE,
5760 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5763 Xeater_n, TRUE, FALSE,
5764 EL_YAMYAM_UP, -1, -1
5767 Xeater_e, TRUE, FALSE,
5768 EL_YAMYAM_RIGHT, -1, -1
5771 Xeater_w, TRUE, FALSE,
5772 EL_YAMYAM_LEFT, -1, -1
5775 Xeater_s, TRUE, FALSE,
5776 EL_YAMYAM_DOWN, -1, -1
5779 Yeater_n, FALSE, FALSE,
5780 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5783 Yeater_nB, FALSE, TRUE,
5784 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5787 Yeater_e, FALSE, FALSE,
5788 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5791 Yeater_eB, FALSE, TRUE,
5792 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5795 Yeater_s, FALSE, FALSE,
5796 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5799 Yeater_sB, FALSE, TRUE,
5800 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5803 Yeater_w, FALSE, FALSE,
5804 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5807 Yeater_wB, FALSE, TRUE,
5808 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5811 Yeater_stone, FALSE, FALSE,
5812 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5815 Yeater_spring, FALSE, FALSE,
5816 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5819 Xalien, TRUE, FALSE,
5823 Xalien_pause, FALSE, FALSE,
5827 Yalien_n, FALSE, FALSE,
5828 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5831 Yalien_nB, FALSE, TRUE,
5832 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5835 Yalien_e, FALSE, FALSE,
5836 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5839 Yalien_eB, FALSE, TRUE,
5840 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5843 Yalien_s, FALSE, FALSE,
5844 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5847 Yalien_sB, FALSE, TRUE,
5848 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5851 Yalien_w, FALSE, FALSE,
5852 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5855 Yalien_wB, FALSE, TRUE,
5856 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5859 Yalien_stone, FALSE, FALSE,
5860 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5863 Yalien_spring, FALSE, FALSE,
5864 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5867 Xemerald, TRUE, FALSE,
5871 Xemerald_pause, FALSE, FALSE,
5875 Xemerald_fall, FALSE, FALSE,
5879 Xemerald_shine, FALSE, FALSE,
5880 EL_EMERALD, ACTION_TWINKLING, -1
5883 Yemerald_s, FALSE, FALSE,
5884 EL_EMERALD, ACTION_FALLING, -1
5887 Yemerald_sB, FALSE, TRUE,
5888 EL_EMERALD, ACTION_FALLING, -1
5891 Yemerald_e, FALSE, FALSE,
5892 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5895 Yemerald_eB, FALSE, TRUE,
5896 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5899 Yemerald_w, FALSE, FALSE,
5900 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5903 Yemerald_wB, FALSE, TRUE,
5904 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5907 Yemerald_eat, FALSE, FALSE,
5908 EL_EMERALD, ACTION_COLLECTING, -1
5911 Yemerald_stone, FALSE, FALSE,
5912 EL_NUT, ACTION_BREAKING, -1
5915 Xdiamond, TRUE, FALSE,
5919 Xdiamond_pause, FALSE, FALSE,
5923 Xdiamond_fall, FALSE, FALSE,
5927 Xdiamond_shine, FALSE, FALSE,
5928 EL_DIAMOND, ACTION_TWINKLING, -1
5931 Ydiamond_s, FALSE, FALSE,
5932 EL_DIAMOND, ACTION_FALLING, -1
5935 Ydiamond_sB, FALSE, TRUE,
5936 EL_DIAMOND, ACTION_FALLING, -1
5939 Ydiamond_e, FALSE, FALSE,
5940 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5943 Ydiamond_eB, FALSE, TRUE,
5944 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5947 Ydiamond_w, FALSE, FALSE,
5948 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5951 Ydiamond_wB, FALSE, TRUE,
5952 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5955 Ydiamond_eat, FALSE, FALSE,
5956 EL_DIAMOND, ACTION_COLLECTING, -1
5959 Ydiamond_stone, FALSE, FALSE,
5960 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5963 Xdrip_fall, TRUE, FALSE,
5964 EL_AMOEBA_DROP, -1, -1
5967 Xdrip_stretch, FALSE, FALSE,
5968 EL_AMOEBA_DROP, ACTION_FALLING, -1
5971 Xdrip_stretchB, FALSE, TRUE,
5972 EL_AMOEBA_DROP, ACTION_FALLING, -1
5975 Xdrip_eat, FALSE, FALSE,
5976 EL_AMOEBA_DROP, ACTION_GROWING, -1
5979 Ydrip_s1, FALSE, FALSE,
5980 EL_AMOEBA_DROP, ACTION_FALLING, -1
5983 Ydrip_s1B, FALSE, TRUE,
5984 EL_AMOEBA_DROP, ACTION_FALLING, -1
5987 Ydrip_s2, FALSE, FALSE,
5988 EL_AMOEBA_DROP, ACTION_FALLING, -1
5991 Ydrip_s2B, FALSE, TRUE,
5992 EL_AMOEBA_DROP, ACTION_FALLING, -1
5999 Xbomb_pause, FALSE, FALSE,
6003 Xbomb_fall, FALSE, FALSE,
6007 Ybomb_s, FALSE, FALSE,
6008 EL_BOMB, ACTION_FALLING, -1
6011 Ybomb_sB, FALSE, TRUE,
6012 EL_BOMB, ACTION_FALLING, -1
6015 Ybomb_e, FALSE, FALSE,
6016 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6019 Ybomb_eB, FALSE, TRUE,
6020 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
6023 Ybomb_w, FALSE, FALSE,
6024 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6027 Ybomb_wB, FALSE, TRUE,
6028 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
6031 Ybomb_eat, FALSE, FALSE,
6032 EL_BOMB, ACTION_ACTIVATING, -1
6035 Xballoon, TRUE, FALSE,
6039 Yballoon_n, FALSE, FALSE,
6040 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6043 Yballoon_nB, FALSE, TRUE,
6044 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
6047 Yballoon_e, FALSE, FALSE,
6048 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6051 Yballoon_eB, FALSE, TRUE,
6052 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
6055 Yballoon_s, FALSE, FALSE,
6056 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6059 Yballoon_sB, FALSE, TRUE,
6060 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6063 Yballoon_w, FALSE, FALSE,
6064 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6067 Yballoon_wB, FALSE, TRUE,
6068 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6071 Xgrass, TRUE, FALSE,
6072 EL_EMC_GRASS, -1, -1
6075 Ygrass_nB, FALSE, FALSE,
6076 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6079 Ygrass_eB, FALSE, FALSE,
6080 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6083 Ygrass_sB, FALSE, FALSE,
6084 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6087 Ygrass_wB, FALSE, FALSE,
6088 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6095 Ydirt_nB, FALSE, FALSE,
6096 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6099 Ydirt_eB, FALSE, FALSE,
6100 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6103 Ydirt_sB, FALSE, FALSE,
6104 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6107 Ydirt_wB, FALSE, FALSE,
6108 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6111 Xacid_ne, TRUE, FALSE,
6112 EL_ACID_POOL_TOPRIGHT, -1, -1
6115 Xacid_se, TRUE, FALSE,
6116 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6119 Xacid_s, TRUE, FALSE,
6120 EL_ACID_POOL_BOTTOM, -1, -1
6123 Xacid_sw, TRUE, FALSE,
6124 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6127 Xacid_nw, TRUE, FALSE,
6128 EL_ACID_POOL_TOPLEFT, -1, -1
6131 Xacid_1, TRUE, FALSE,
6135 Xacid_2, FALSE, FALSE,
6139 Xacid_3, FALSE, FALSE,
6143 Xacid_4, FALSE, FALSE,
6147 Xacid_5, FALSE, FALSE,
6151 Xacid_6, FALSE, FALSE,
6155 Xacid_7, FALSE, FALSE,
6159 Xacid_8, FALSE, FALSE,
6163 Xball_1, TRUE, FALSE,
6164 EL_EMC_MAGIC_BALL, -1, -1
6167 Xball_1B, FALSE, FALSE,
6168 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6171 Xball_2, FALSE, FALSE,
6172 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6175 Xball_2B, FALSE, FALSE,
6176 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6179 Yball_eat, FALSE, FALSE,
6180 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6183 Ykey_1_eat, FALSE, FALSE,
6184 EL_EM_KEY_1, ACTION_COLLECTING, -1
6187 Ykey_2_eat, FALSE, FALSE,
6188 EL_EM_KEY_2, ACTION_COLLECTING, -1
6191 Ykey_3_eat, FALSE, FALSE,
6192 EL_EM_KEY_3, ACTION_COLLECTING, -1
6195 Ykey_4_eat, FALSE, FALSE,
6196 EL_EM_KEY_4, ACTION_COLLECTING, -1
6199 Ykey_5_eat, FALSE, FALSE,
6200 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6203 Ykey_6_eat, FALSE, FALSE,
6204 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6207 Ykey_7_eat, FALSE, FALSE,
6208 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6211 Ykey_8_eat, FALSE, FALSE,
6212 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6215 Ylenses_eat, FALSE, FALSE,
6216 EL_EMC_LENSES, ACTION_COLLECTING, -1
6219 Ymagnify_eat, FALSE, FALSE,
6220 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6223 Ygrass_eat, FALSE, FALSE,
6224 EL_EMC_GRASS, ACTION_SNAPPING, -1
6227 Ydirt_eat, FALSE, FALSE,
6228 EL_SAND, ACTION_SNAPPING, -1
6231 Xgrow_ns, TRUE, FALSE,
6232 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6235 Ygrow_ns_eat, FALSE, FALSE,
6236 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6239 Xgrow_ew, TRUE, FALSE,
6240 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6243 Ygrow_ew_eat, FALSE, FALSE,
6244 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6247 Xwonderwall, TRUE, FALSE,
6248 EL_MAGIC_WALL, -1, -1
6251 XwonderwallB, FALSE, FALSE,
6252 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6255 Xamoeba_1, TRUE, FALSE,
6256 EL_AMOEBA_DRY, ACTION_OTHER, -1
6259 Xamoeba_2, FALSE, FALSE,
6260 EL_AMOEBA_DRY, ACTION_OTHER, -1
6263 Xamoeba_3, FALSE, FALSE,
6264 EL_AMOEBA_DRY, ACTION_OTHER, -1
6267 Xamoeba_4, FALSE, FALSE,
6268 EL_AMOEBA_DRY, ACTION_OTHER, -1
6271 Xamoeba_5, TRUE, FALSE,
6272 EL_AMOEBA_WET, ACTION_OTHER, -1
6275 Xamoeba_6, FALSE, FALSE,
6276 EL_AMOEBA_WET, ACTION_OTHER, -1
6279 Xamoeba_7, FALSE, FALSE,
6280 EL_AMOEBA_WET, ACTION_OTHER, -1
6283 Xamoeba_8, FALSE, FALSE,
6284 EL_AMOEBA_WET, ACTION_OTHER, -1
6287 Xdoor_1, TRUE, FALSE,
6288 EL_EM_GATE_1, -1, -1
6291 Xdoor_2, TRUE, FALSE,
6292 EL_EM_GATE_2, -1, -1
6295 Xdoor_3, TRUE, FALSE,
6296 EL_EM_GATE_3, -1, -1
6299 Xdoor_4, TRUE, FALSE,
6300 EL_EM_GATE_4, -1, -1
6303 Xdoor_5, TRUE, FALSE,
6304 EL_EMC_GATE_5, -1, -1
6307 Xdoor_6, TRUE, FALSE,
6308 EL_EMC_GATE_6, -1, -1
6311 Xdoor_7, TRUE, FALSE,
6312 EL_EMC_GATE_7, -1, -1
6315 Xdoor_8, TRUE, FALSE,
6316 EL_EMC_GATE_8, -1, -1
6319 Xkey_1, TRUE, FALSE,
6323 Xkey_2, TRUE, FALSE,
6327 Xkey_3, TRUE, FALSE,
6331 Xkey_4, TRUE, FALSE,
6335 Xkey_5, TRUE, FALSE,
6336 EL_EMC_KEY_5, -1, -1
6339 Xkey_6, TRUE, FALSE,
6340 EL_EMC_KEY_6, -1, -1
6343 Xkey_7, TRUE, FALSE,
6344 EL_EMC_KEY_7, -1, -1
6347 Xkey_8, TRUE, FALSE,
6348 EL_EMC_KEY_8, -1, -1
6351 Xwind_n, TRUE, FALSE,
6352 EL_BALLOON_SWITCH_UP, -1, -1
6355 Xwind_e, TRUE, FALSE,
6356 EL_BALLOON_SWITCH_RIGHT, -1, -1
6359 Xwind_s, TRUE, FALSE,
6360 EL_BALLOON_SWITCH_DOWN, -1, -1
6363 Xwind_w, TRUE, FALSE,
6364 EL_BALLOON_SWITCH_LEFT, -1, -1
6367 Xwind_nesw, TRUE, FALSE,
6368 EL_BALLOON_SWITCH_ANY, -1, -1
6371 Xwind_stop, TRUE, FALSE,
6372 EL_BALLOON_SWITCH_NONE, -1, -1
6376 EL_EM_EXIT_CLOSED, -1, -1
6379 Xexit_1, TRUE, FALSE,
6380 EL_EM_EXIT_OPEN, -1, -1
6383 Xexit_2, FALSE, FALSE,
6384 EL_EM_EXIT_OPEN, -1, -1
6387 Xexit_3, FALSE, FALSE,
6388 EL_EM_EXIT_OPEN, -1, -1
6391 Xdynamite, TRUE, FALSE,
6392 EL_EM_DYNAMITE, -1, -1
6395 Ydynamite_eat, FALSE, FALSE,
6396 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6399 Xdynamite_1, TRUE, FALSE,
6400 EL_EM_DYNAMITE_ACTIVE, -1, -1
6403 Xdynamite_2, FALSE, FALSE,
6404 EL_EM_DYNAMITE_ACTIVE, -1, -1
6407 Xdynamite_3, FALSE, FALSE,
6408 EL_EM_DYNAMITE_ACTIVE, -1, -1
6411 Xdynamite_4, FALSE, FALSE,
6412 EL_EM_DYNAMITE_ACTIVE, -1, -1
6415 Xbumper, TRUE, FALSE,
6416 EL_EMC_SPRING_BUMPER, -1, -1
6419 XbumperB, FALSE, FALSE,
6420 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6423 Xwheel, TRUE, FALSE,
6424 EL_ROBOT_WHEEL, -1, -1
6427 XwheelB, FALSE, FALSE,
6428 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6431 Xswitch, TRUE, FALSE,
6432 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6435 XswitchB, FALSE, FALSE,
6436 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6440 EL_QUICKSAND_EMPTY, -1, -1
6443 Xsand_stone, TRUE, FALSE,
6444 EL_QUICKSAND_FULL, -1, -1
6447 Xsand_stonein_1, FALSE, TRUE,
6448 EL_ROCK, ACTION_FILLING, -1
6451 Xsand_stonein_2, FALSE, TRUE,
6452 EL_ROCK, ACTION_FILLING, -1
6455 Xsand_stonein_3, FALSE, TRUE,
6456 EL_ROCK, ACTION_FILLING, -1
6459 Xsand_stonein_4, FALSE, TRUE,
6460 EL_ROCK, ACTION_FILLING, -1
6463 Xsand_stonesand_1, FALSE, FALSE,
6464 EL_QUICKSAND_EMPTYING, -1, -1
6467 Xsand_stonesand_2, FALSE, FALSE,
6468 EL_QUICKSAND_EMPTYING, -1, -1
6471 Xsand_stonesand_3, FALSE, FALSE,
6472 EL_QUICKSAND_EMPTYING, -1, -1
6475 Xsand_stonesand_4, FALSE, FALSE,
6476 EL_QUICKSAND_EMPTYING, -1, -1
6479 Xsand_stonesand_quickout_1, FALSE, FALSE,
6480 EL_QUICKSAND_EMPTYING, -1, -1
6483 Xsand_stonesand_quickout_2, FALSE, FALSE,
6484 EL_QUICKSAND_EMPTYING, -1, -1
6487 Xsand_stoneout_1, FALSE, FALSE,
6488 EL_ROCK, ACTION_EMPTYING, -1
6491 Xsand_stoneout_2, FALSE, FALSE,
6492 EL_ROCK, ACTION_EMPTYING, -1
6495 Xsand_sandstone_1, FALSE, FALSE,
6496 EL_QUICKSAND_FILLING, -1, -1
6499 Xsand_sandstone_2, FALSE, FALSE,
6500 EL_QUICKSAND_FILLING, -1, -1
6503 Xsand_sandstone_3, FALSE, FALSE,
6504 EL_QUICKSAND_FILLING, -1, -1
6507 Xsand_sandstone_4, FALSE, FALSE,
6508 EL_QUICKSAND_FILLING, -1, -1
6511 Xplant, TRUE, FALSE,
6512 EL_EMC_PLANT, -1, -1
6515 Yplant, FALSE, FALSE,
6516 EL_EMC_PLANT, -1, -1
6519 Xlenses, TRUE, FALSE,
6520 EL_EMC_LENSES, -1, -1
6523 Xmagnify, TRUE, FALSE,
6524 EL_EMC_MAGNIFIER, -1, -1
6527 Xdripper, TRUE, FALSE,
6528 EL_EMC_DRIPPER, -1, -1
6531 XdripperB, FALSE, FALSE,
6532 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6535 Xfake_blank, TRUE, FALSE,
6536 EL_INVISIBLE_WALL, -1, -1
6539 Xfake_blankB, FALSE, FALSE,
6540 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6543 Xfake_grass, TRUE, FALSE,
6544 EL_EMC_FAKE_GRASS, -1, -1
6547 Xfake_grassB, FALSE, FALSE,
6548 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6551 Xfake_door_1, TRUE, FALSE,
6552 EL_EM_GATE_1_GRAY, -1, -1
6555 Xfake_door_2, TRUE, FALSE,
6556 EL_EM_GATE_2_GRAY, -1, -1
6559 Xfake_door_3, TRUE, FALSE,
6560 EL_EM_GATE_3_GRAY, -1, -1
6563 Xfake_door_4, TRUE, FALSE,
6564 EL_EM_GATE_4_GRAY, -1, -1
6567 Xfake_door_5, TRUE, FALSE,
6568 EL_EMC_GATE_5_GRAY, -1, -1
6571 Xfake_door_6, TRUE, FALSE,
6572 EL_EMC_GATE_6_GRAY, -1, -1
6575 Xfake_door_7, TRUE, FALSE,
6576 EL_EMC_GATE_7_GRAY, -1, -1
6579 Xfake_door_8, TRUE, FALSE,
6580 EL_EMC_GATE_8_GRAY, -1, -1
6583 Xfake_acid_1, TRUE, FALSE,
6584 EL_EMC_FAKE_ACID, -1, -1
6587 Xfake_acid_2, FALSE, FALSE,
6588 EL_EMC_FAKE_ACID, -1, -1
6591 Xfake_acid_3, FALSE, FALSE,
6592 EL_EMC_FAKE_ACID, -1, -1
6595 Xfake_acid_4, FALSE, FALSE,
6596 EL_EMC_FAKE_ACID, -1, -1
6599 Xfake_acid_5, FALSE, FALSE,
6600 EL_EMC_FAKE_ACID, -1, -1
6603 Xfake_acid_6, FALSE, FALSE,
6604 EL_EMC_FAKE_ACID, -1, -1
6607 Xfake_acid_7, FALSE, FALSE,
6608 EL_EMC_FAKE_ACID, -1, -1
6611 Xfake_acid_8, FALSE, FALSE,
6612 EL_EMC_FAKE_ACID, -1, -1
6615 Xsteel_1, TRUE, FALSE,
6616 EL_STEELWALL, -1, -1
6619 Xsteel_2, TRUE, FALSE,
6620 EL_EMC_STEELWALL_2, -1, -1
6623 Xsteel_3, TRUE, FALSE,
6624 EL_EMC_STEELWALL_3, -1, -1
6627 Xsteel_4, TRUE, FALSE,
6628 EL_EMC_STEELWALL_4, -1, -1
6631 Xwall_1, TRUE, FALSE,
6635 Xwall_2, TRUE, FALSE,
6636 EL_EMC_WALL_14, -1, -1
6639 Xwall_3, TRUE, FALSE,
6640 EL_EMC_WALL_15, -1, -1
6643 Xwall_4, TRUE, FALSE,
6644 EL_EMC_WALL_16, -1, -1
6647 Xround_wall_1, TRUE, FALSE,
6648 EL_WALL_SLIPPERY, -1, -1
6651 Xround_wall_2, TRUE, FALSE,
6652 EL_EMC_WALL_SLIPPERY_2, -1, -1
6655 Xround_wall_3, TRUE, FALSE,
6656 EL_EMC_WALL_SLIPPERY_3, -1, -1
6659 Xround_wall_4, TRUE, FALSE,
6660 EL_EMC_WALL_SLIPPERY_4, -1, -1
6663 Xdecor_1, TRUE, FALSE,
6664 EL_EMC_WALL_8, -1, -1
6667 Xdecor_2, TRUE, FALSE,
6668 EL_EMC_WALL_6, -1, -1
6671 Xdecor_3, TRUE, FALSE,
6672 EL_EMC_WALL_4, -1, -1
6675 Xdecor_4, TRUE, FALSE,
6676 EL_EMC_WALL_7, -1, -1
6679 Xdecor_5, TRUE, FALSE,
6680 EL_EMC_WALL_5, -1, -1
6683 Xdecor_6, TRUE, FALSE,
6684 EL_EMC_WALL_9, -1, -1
6687 Xdecor_7, TRUE, FALSE,
6688 EL_EMC_WALL_10, -1, -1
6691 Xdecor_8, TRUE, FALSE,
6692 EL_EMC_WALL_1, -1, -1
6695 Xdecor_9, TRUE, FALSE,
6696 EL_EMC_WALL_2, -1, -1
6699 Xdecor_10, TRUE, FALSE,
6700 EL_EMC_WALL_3, -1, -1
6703 Xdecor_11, TRUE, FALSE,
6704 EL_EMC_WALL_11, -1, -1
6707 Xdecor_12, TRUE, FALSE,
6708 EL_EMC_WALL_12, -1, -1
6711 Xalpha_0, TRUE, FALSE,
6712 EL_CHAR('0'), -1, -1
6715 Xalpha_1, TRUE, FALSE,
6716 EL_CHAR('1'), -1, -1
6719 Xalpha_2, TRUE, FALSE,
6720 EL_CHAR('2'), -1, -1
6723 Xalpha_3, TRUE, FALSE,
6724 EL_CHAR('3'), -1, -1
6727 Xalpha_4, TRUE, FALSE,
6728 EL_CHAR('4'), -1, -1
6731 Xalpha_5, TRUE, FALSE,
6732 EL_CHAR('5'), -1, -1
6735 Xalpha_6, TRUE, FALSE,
6736 EL_CHAR('6'), -1, -1
6739 Xalpha_7, TRUE, FALSE,
6740 EL_CHAR('7'), -1, -1
6743 Xalpha_8, TRUE, FALSE,
6744 EL_CHAR('8'), -1, -1
6747 Xalpha_9, TRUE, FALSE,
6748 EL_CHAR('9'), -1, -1
6751 Xalpha_excla, TRUE, FALSE,
6752 EL_CHAR('!'), -1, -1
6755 Xalpha_quote, TRUE, FALSE,
6756 EL_CHAR('"'), -1, -1
6759 Xalpha_comma, TRUE, FALSE,
6760 EL_CHAR(','), -1, -1
6763 Xalpha_minus, TRUE, FALSE,
6764 EL_CHAR('-'), -1, -1
6767 Xalpha_perio, TRUE, FALSE,
6768 EL_CHAR('.'), -1, -1
6771 Xalpha_colon, TRUE, FALSE,
6772 EL_CHAR(':'), -1, -1
6775 Xalpha_quest, TRUE, FALSE,
6776 EL_CHAR('?'), -1, -1
6779 Xalpha_a, TRUE, FALSE,
6780 EL_CHAR('A'), -1, -1
6783 Xalpha_b, TRUE, FALSE,
6784 EL_CHAR('B'), -1, -1
6787 Xalpha_c, TRUE, FALSE,
6788 EL_CHAR('C'), -1, -1
6791 Xalpha_d, TRUE, FALSE,
6792 EL_CHAR('D'), -1, -1
6795 Xalpha_e, TRUE, FALSE,
6796 EL_CHAR('E'), -1, -1
6799 Xalpha_f, TRUE, FALSE,
6800 EL_CHAR('F'), -1, -1
6803 Xalpha_g, TRUE, FALSE,
6804 EL_CHAR('G'), -1, -1
6807 Xalpha_h, TRUE, FALSE,
6808 EL_CHAR('H'), -1, -1
6811 Xalpha_i, TRUE, FALSE,
6812 EL_CHAR('I'), -1, -1
6815 Xalpha_j, TRUE, FALSE,
6816 EL_CHAR('J'), -1, -1
6819 Xalpha_k, TRUE, FALSE,
6820 EL_CHAR('K'), -1, -1
6823 Xalpha_l, TRUE, FALSE,
6824 EL_CHAR('L'), -1, -1
6827 Xalpha_m, TRUE, FALSE,
6828 EL_CHAR('M'), -1, -1
6831 Xalpha_n, TRUE, FALSE,
6832 EL_CHAR('N'), -1, -1
6835 Xalpha_o, TRUE, FALSE,
6836 EL_CHAR('O'), -1, -1
6839 Xalpha_p, TRUE, FALSE,
6840 EL_CHAR('P'), -1, -1
6843 Xalpha_q, TRUE, FALSE,
6844 EL_CHAR('Q'), -1, -1
6847 Xalpha_r, TRUE, FALSE,
6848 EL_CHAR('R'), -1, -1
6851 Xalpha_s, TRUE, FALSE,
6852 EL_CHAR('S'), -1, -1
6855 Xalpha_t, TRUE, FALSE,
6856 EL_CHAR('T'), -1, -1
6859 Xalpha_u, TRUE, FALSE,
6860 EL_CHAR('U'), -1, -1
6863 Xalpha_v, TRUE, FALSE,
6864 EL_CHAR('V'), -1, -1
6867 Xalpha_w, TRUE, FALSE,
6868 EL_CHAR('W'), -1, -1
6871 Xalpha_x, TRUE, FALSE,
6872 EL_CHAR('X'), -1, -1
6875 Xalpha_y, TRUE, FALSE,
6876 EL_CHAR('Y'), -1, -1
6879 Xalpha_z, TRUE, FALSE,
6880 EL_CHAR('Z'), -1, -1
6883 Xalpha_arrow_e, TRUE, FALSE,
6884 EL_CHAR('>'), -1, -1
6887 Xalpha_arrow_w, TRUE, FALSE,
6888 EL_CHAR('<'), -1, -1
6891 Xalpha_copyr, TRUE, FALSE,
6892 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6896 Xboom_bug, FALSE, FALSE,
6897 EL_BUG, ACTION_EXPLODING, -1
6900 Xboom_bomb, FALSE, FALSE,
6901 EL_BOMB, ACTION_EXPLODING, -1
6904 Xboom_android, FALSE, FALSE,
6905 EL_EMC_ANDROID, ACTION_OTHER, -1
6908 Xboom_1, FALSE, FALSE,
6909 EL_DEFAULT, ACTION_EXPLODING, -1
6912 Xboom_2, FALSE, FALSE,
6913 EL_DEFAULT, ACTION_EXPLODING, -1
6916 Znormal, FALSE, FALSE,
6920 Zdynamite, FALSE, FALSE,
6924 Zplayer, FALSE, FALSE,
6928 ZBORDER, FALSE, FALSE,
6938 static struct Mapping_EM_to_RND_player
6947 em_player_mapping_list[] =
6951 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6955 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6959 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6963 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6967 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6971 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6975 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6979 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6983 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6987 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6991 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6995 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6999 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
7003 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
7007 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
7011 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
7015 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
7019 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
7023 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
7027 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
7031 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
7035 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
7039 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
7043 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
7047 EL_PLAYER_1, ACTION_DEFAULT, -1,
7051 EL_PLAYER_2, ACTION_DEFAULT, -1,
7055 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
7059 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7063 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7067 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7071 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7075 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7079 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7083 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7087 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7091 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7095 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7099 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7103 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7107 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7111 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7115 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7119 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7123 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7127 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7131 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7135 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7139 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7143 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7147 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7151 EL_PLAYER_3, ACTION_DEFAULT, -1,
7155 EL_PLAYER_4, ACTION_DEFAULT, -1,
7164 int map_element_RND_to_EM(int element_rnd)
7166 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7167 static boolean mapping_initialized = FALSE;
7169 if (!mapping_initialized)
7173 /* return "Xalpha_quest" for all undefined elements in mapping array */
7174 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7175 mapping_RND_to_EM[i] = Xalpha_quest;
7177 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7178 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7179 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7180 em_object_mapping_list[i].element_em;
7182 mapping_initialized = TRUE;
7185 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7186 return mapping_RND_to_EM[element_rnd];
7188 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7193 int map_element_EM_to_RND(int element_em)
7195 static unsigned short mapping_EM_to_RND[TILE_MAX];
7196 static boolean mapping_initialized = FALSE;
7198 if (!mapping_initialized)
7202 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
7203 for (i = 0; i < TILE_MAX; i++)
7204 mapping_EM_to_RND[i] = EL_UNKNOWN;
7206 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7207 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7208 em_object_mapping_list[i].element_rnd;
7210 mapping_initialized = TRUE;
7213 if (element_em >= 0 && element_em < TILE_MAX)
7214 return mapping_EM_to_RND[element_em];
7216 Error(ERR_WARN, "invalid EM level element %d", element_em);
7221 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7223 struct LevelInfo_EM *level_em = level->native_em_level;
7224 struct LEVEL *lev = level_em->lev;
7227 for (i = 0; i < TILE_MAX; i++)
7228 lev->android_array[i] = Xblank;
7230 for (i = 0; i < level->num_android_clone_elements; i++)
7232 int element_rnd = level->android_clone_element[i];
7233 int element_em = map_element_RND_to_EM(element_rnd);
7235 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7236 if (em_object_mapping_list[j].element_rnd == element_rnd)
7237 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7241 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7243 struct LevelInfo_EM *level_em = level->native_em_level;
7244 struct LEVEL *lev = level_em->lev;
7247 level->num_android_clone_elements = 0;
7249 for (i = 0; i < TILE_MAX; i++)
7251 int element_em = lev->android_array[i];
7253 boolean element_found = FALSE;
7255 if (element_em == Xblank)
7258 element_rnd = map_element_EM_to_RND(element_em);
7260 for (j = 0; j < level->num_android_clone_elements; j++)
7261 if (level->android_clone_element[j] == element_rnd)
7262 element_found = TRUE;
7266 level->android_clone_element[level->num_android_clone_elements++] =
7269 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7274 if (level->num_android_clone_elements == 0)
7276 level->num_android_clone_elements = 1;
7277 level->android_clone_element[0] = EL_EMPTY;
7281 int map_direction_RND_to_EM(int direction)
7283 return (direction == MV_UP ? 0 :
7284 direction == MV_RIGHT ? 1 :
7285 direction == MV_DOWN ? 2 :
7286 direction == MV_LEFT ? 3 :
7290 int map_direction_EM_to_RND(int direction)
7292 return (direction == 0 ? MV_UP :
7293 direction == 1 ? MV_RIGHT :
7294 direction == 2 ? MV_DOWN :
7295 direction == 3 ? MV_LEFT :
7299 int map_element_RND_to_SP(int element_rnd)
7301 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
7303 if (element_rnd >= EL_SP_START &&
7304 element_rnd <= EL_SP_END)
7305 element_sp = element_rnd - EL_SP_START;
7306 else if (element_rnd == EL_EMPTY_SPACE)
7308 else if (element_rnd == EL_INVISIBLE_WALL)
7314 int map_element_SP_to_RND(int element_sp)
7316 int element_rnd = EL_UNKNOWN;
7318 if (element_sp >= 0x00 &&
7320 element_rnd = EL_SP_START + element_sp;
7321 else if (element_sp == 0x28)
7322 element_rnd = EL_INVISIBLE_WALL;
7327 int map_action_SP_to_RND(int action_sp)
7331 case actActive: return ACTION_ACTIVE;
7332 case actImpact: return ACTION_IMPACT;
7333 case actExploding: return ACTION_EXPLODING;
7334 case actDigging: return ACTION_DIGGING;
7335 case actSnapping: return ACTION_SNAPPING;
7336 case actCollecting: return ACTION_COLLECTING;
7337 case actPassing: return ACTION_PASSING;
7338 case actPushing: return ACTION_PUSHING;
7339 case actDropping: return ACTION_DROPPING;
7341 default: return ACTION_DEFAULT;
7345 int map_element_RND_to_MM(int element_rnd)
7347 return (element_rnd >= EL_MM_START_1 &&
7348 element_rnd <= EL_MM_END_1 ?
7349 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7351 element_rnd >= EL_MM_START_2 &&
7352 element_rnd <= EL_MM_END_2 ?
7353 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7355 element_rnd >= EL_CHAR_START &&
7356 element_rnd <= EL_CHAR_END ?
7357 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7359 element_rnd >= EL_MM_RUNTIME_START &&
7360 element_rnd <= EL_MM_RUNTIME_END ?
7361 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7363 element_rnd >= EL_MM_DUMMY_START &&
7364 element_rnd <= EL_MM_DUMMY_END ?
7365 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7367 EL_MM_EMPTY_NATIVE);
7370 int map_element_MM_to_RND(int element_mm)
7372 return (element_mm == EL_MM_EMPTY_NATIVE ||
7373 element_mm == EL_DF_EMPTY_NATIVE ?
7376 element_mm >= EL_MM_START_1_NATIVE &&
7377 element_mm <= EL_MM_END_1_NATIVE ?
7378 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7380 element_mm >= EL_MM_START_2_NATIVE &&
7381 element_mm <= EL_MM_END_2_NATIVE ?
7382 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7384 element_mm >= EL_MM_CHAR_START_NATIVE &&
7385 element_mm <= EL_MM_CHAR_END_NATIVE ?
7386 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7388 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7389 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7390 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7392 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7393 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7394 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7399 int map_action_MM_to_RND(int action_mm)
7401 /* all MM actions are defined to exactly match their RND counterparts */
7405 int map_sound_MM_to_RND(int sound_mm)
7409 case SND_MM_GAME_LEVELTIME_CHARGING:
7410 return SND_GAME_LEVELTIME_CHARGING;
7413 return SND_UNDEFINED;
7417 int map_mm_wall_element(int element)
7419 return (element >= EL_MM_STEEL_WALL_START &&
7420 element <= EL_MM_STEEL_WALL_END ?
7423 element >= EL_MM_WOODEN_WALL_START &&
7424 element <= EL_MM_WOODEN_WALL_END ?
7427 element >= EL_MM_ICE_WALL_START &&
7428 element <= EL_MM_ICE_WALL_END ?
7431 element >= EL_MM_AMOEBA_WALL_START &&
7432 element <= EL_MM_AMOEBA_WALL_END ?
7435 element >= EL_DF_STEEL_WALL_START &&
7436 element <= EL_DF_STEEL_WALL_END ?
7439 element >= EL_DF_WOODEN_WALL_START &&
7440 element <= EL_DF_WOODEN_WALL_END ?
7446 int map_mm_wall_element_editor(int element)
7450 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
7451 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
7452 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
7453 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
7454 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
7455 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
7457 default: return element;
7461 int get_next_element(int element)
7465 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7466 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7467 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7468 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7469 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7470 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7471 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7472 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7473 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7474 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7475 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7477 default: return element;
7481 int el2img_mm(int element_mm)
7483 return el2img(map_element_MM_to_RND(element_mm));
7486 int el_act_dir2img(int element, int action, int direction)
7488 element = GFX_ELEMENT(element);
7489 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7491 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7492 return element_info[element].direction_graphic[action][direction];
7495 static int el_act_dir2crm(int element, int action, int direction)
7497 element = GFX_ELEMENT(element);
7498 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7500 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7501 return element_info[element].direction_crumbled[action][direction];
7504 int el_act2img(int element, int action)
7506 element = GFX_ELEMENT(element);
7508 return element_info[element].graphic[action];
7511 int el_act2crm(int element, int action)
7513 element = GFX_ELEMENT(element);
7515 return element_info[element].crumbled[action];
7518 int el_dir2img(int element, int direction)
7520 element = GFX_ELEMENT(element);
7522 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7525 int el2baseimg(int element)
7527 return element_info[element].graphic[ACTION_DEFAULT];
7530 int el2img(int element)
7532 element = GFX_ELEMENT(element);
7534 return element_info[element].graphic[ACTION_DEFAULT];
7537 int el2edimg(int element)
7539 element = GFX_ELEMENT(element);
7541 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7544 int el2preimg(int element)
7546 element = GFX_ELEMENT(element);
7548 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7551 int el2panelimg(int element)
7553 element = GFX_ELEMENT(element);
7555 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7558 int font2baseimg(int font_nr)
7560 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7563 int getBeltNrFromBeltElement(int element)
7565 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7566 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7567 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
7570 int getBeltNrFromBeltActiveElement(int element)
7572 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
7573 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
7574 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
7577 int getBeltNrFromBeltSwitchElement(int element)
7579 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
7580 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
7581 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
7584 int getBeltDirNrFromBeltElement(int element)
7586 static int belt_base_element[4] =
7588 EL_CONVEYOR_BELT_1_LEFT,
7589 EL_CONVEYOR_BELT_2_LEFT,
7590 EL_CONVEYOR_BELT_3_LEFT,
7591 EL_CONVEYOR_BELT_4_LEFT
7594 int belt_nr = getBeltNrFromBeltElement(element);
7595 int belt_dir_nr = element - belt_base_element[belt_nr];
7597 return (belt_dir_nr % 3);
7600 int getBeltDirNrFromBeltSwitchElement(int element)
7602 static int belt_base_element[4] =
7604 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7605 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7606 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7607 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7610 int belt_nr = getBeltNrFromBeltSwitchElement(element);
7611 int belt_dir_nr = element - belt_base_element[belt_nr];
7613 return (belt_dir_nr % 3);
7616 int getBeltDirFromBeltElement(int element)
7618 static int belt_move_dir[3] =
7625 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7627 return belt_move_dir[belt_dir_nr];
7630 int getBeltDirFromBeltSwitchElement(int element)
7632 static int belt_move_dir[3] =
7639 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7641 return belt_move_dir[belt_dir_nr];
7644 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7646 static int belt_base_element[4] =
7648 EL_CONVEYOR_BELT_1_LEFT,
7649 EL_CONVEYOR_BELT_2_LEFT,
7650 EL_CONVEYOR_BELT_3_LEFT,
7651 EL_CONVEYOR_BELT_4_LEFT
7654 return belt_base_element[belt_nr] + belt_dir_nr;
7657 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7659 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7661 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7664 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7666 static int belt_base_element[4] =
7668 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7669 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7670 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7671 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7674 return belt_base_element[belt_nr] + belt_dir_nr;
7677 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7679 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7681 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7684 boolean getTeamMode_EM()
7686 return game.team_mode;
7689 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7691 int game_frame_delay_value;
7693 game_frame_delay_value =
7694 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7695 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7698 if (tape.playing && tape.warp_forward && !tape.pausing)
7699 game_frame_delay_value = 0;
7701 return game_frame_delay_value;
7704 unsigned int InitRND(int seed)
7706 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7707 return InitEngineRandom_EM(seed);
7708 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7709 return InitEngineRandom_SP(seed);
7710 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
7711 return InitEngineRandom_MM(seed);
7713 return InitEngineRandom_RND(seed);
7716 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7717 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7719 inline static int get_effective_element_EM(int tile, int frame_em)
7721 int element = object_mapping[tile].element_rnd;
7722 int action = object_mapping[tile].action;
7723 boolean is_backside = object_mapping[tile].is_backside;
7724 boolean action_removing = (action == ACTION_DIGGING ||
7725 action == ACTION_SNAPPING ||
7726 action == ACTION_COLLECTING);
7732 case Yacid_splash_eB:
7733 case Yacid_splash_wB:
7734 return (frame_em > 5 ? EL_EMPTY : element);
7740 else /* frame_em == 7 */
7744 case Yacid_splash_eB:
7745 case Yacid_splash_wB:
7748 case Yemerald_stone:
7751 case Ydiamond_stone:
7755 case Xdrip_stretchB:
7774 case Xsand_stonein_1:
7775 case Xsand_stonein_2:
7776 case Xsand_stonein_3:
7777 case Xsand_stonein_4:
7781 return (is_backside || action_removing ? EL_EMPTY : element);
7786 inline static boolean check_linear_animation_EM(int tile)
7790 case Xsand_stonesand_1:
7791 case Xsand_stonesand_quickout_1:
7792 case Xsand_sandstone_1:
7793 case Xsand_stonein_1:
7794 case Xsand_stoneout_1:
7813 case Yacid_splash_eB:
7814 case Yacid_splash_wB:
7815 case Yemerald_stone:
7822 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7823 boolean has_crumbled_graphics,
7824 int crumbled, int sync_frame)
7826 /* if element can be crumbled, but certain action graphics are just empty
7827 space (like instantly snapping sand to empty space in 1 frame), do not
7828 treat these empty space graphics as crumbled graphics in EMC engine */
7829 if (crumbled == IMG_EMPTY_SPACE)
7830 has_crumbled_graphics = FALSE;
7832 if (has_crumbled_graphics)
7834 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7835 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7836 g_crumbled->anim_delay,
7837 g_crumbled->anim_mode,
7838 g_crumbled->anim_start_frame,
7841 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7842 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7844 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7845 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
7847 g_em->has_crumbled_graphics = TRUE;
7851 g_em->crumbled_bitmap = NULL;
7852 g_em->crumbled_src_x = 0;
7853 g_em->crumbled_src_y = 0;
7854 g_em->crumbled_border_size = 0;
7855 g_em->crumbled_tile_size = 0;
7857 g_em->has_crumbled_graphics = FALSE;
7861 void ResetGfxAnimation_EM(int x, int y, int tile)
7866 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7867 int tile, int frame_em, int x, int y)
7869 int action = object_mapping[tile].action;
7870 int direction = object_mapping[tile].direction;
7871 int effective_element = get_effective_element_EM(tile, frame_em);
7872 int graphic = (direction == MV_NONE ?
7873 el_act2img(effective_element, action) :
7874 el_act_dir2img(effective_element, action, direction));
7875 struct GraphicInfo *g = &graphic_info[graphic];
7877 boolean action_removing = (action == ACTION_DIGGING ||
7878 action == ACTION_SNAPPING ||
7879 action == ACTION_COLLECTING);
7880 boolean action_moving = (action == ACTION_FALLING ||
7881 action == ACTION_MOVING ||
7882 action == ACTION_PUSHING ||
7883 action == ACTION_EATING ||
7884 action == ACTION_FILLING ||
7885 action == ACTION_EMPTYING);
7886 boolean action_falling = (action == ACTION_FALLING ||
7887 action == ACTION_FILLING ||
7888 action == ACTION_EMPTYING);
7890 /* special case: graphic uses "2nd movement tile" and has defined
7891 7 frames for movement animation (or less) => use default graphic
7892 for last (8th) frame which ends the movement animation */
7893 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7895 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7896 graphic = (direction == MV_NONE ?
7897 el_act2img(effective_element, action) :
7898 el_act_dir2img(effective_element, action, direction));
7900 g = &graphic_info[graphic];
7903 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7907 else if (action_moving)
7909 boolean is_backside = object_mapping[tile].is_backside;
7913 int direction = object_mapping[tile].direction;
7914 int move_dir = (action_falling ? MV_DOWN : direction);
7919 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7920 if (g->double_movement && frame_em == 0)
7924 if (move_dir == MV_LEFT)
7925 GfxFrame[x - 1][y] = GfxFrame[x][y];
7926 else if (move_dir == MV_RIGHT)
7927 GfxFrame[x + 1][y] = GfxFrame[x][y];
7928 else if (move_dir == MV_UP)
7929 GfxFrame[x][y - 1] = GfxFrame[x][y];
7930 else if (move_dir == MV_DOWN)
7931 GfxFrame[x][y + 1] = GfxFrame[x][y];
7938 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7939 if (tile == Xsand_stonesand_quickout_1 ||
7940 tile == Xsand_stonesand_quickout_2)
7944 if (graphic_info[graphic].anim_global_sync)
7945 sync_frame = FrameCounter;
7946 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7947 sync_frame = GfxFrame[x][y];
7949 sync_frame = 0; /* playfield border (pseudo steel) */
7951 SetRandomAnimationValue(x, y);
7953 int frame = getAnimationFrame(g->anim_frames,
7956 g->anim_start_frame,
7959 g_em->unique_identifier =
7960 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7963 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7964 int tile, int frame_em, int x, int y)
7966 int action = object_mapping[tile].action;
7967 int direction = object_mapping[tile].direction;
7968 boolean is_backside = object_mapping[tile].is_backside;
7969 int effective_element = get_effective_element_EM(tile, frame_em);
7970 int effective_action = action;
7971 int graphic = (direction == MV_NONE ?
7972 el_act2img(effective_element, effective_action) :
7973 el_act_dir2img(effective_element, effective_action,
7975 int crumbled = (direction == MV_NONE ?
7976 el_act2crm(effective_element, effective_action) :
7977 el_act_dir2crm(effective_element, effective_action,
7979 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7980 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7981 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7982 struct GraphicInfo *g = &graphic_info[graphic];
7985 /* special case: graphic uses "2nd movement tile" and has defined
7986 7 frames for movement animation (or less) => use default graphic
7987 for last (8th) frame which ends the movement animation */
7988 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7990 effective_action = ACTION_DEFAULT;
7991 graphic = (direction == MV_NONE ?
7992 el_act2img(effective_element, effective_action) :
7993 el_act_dir2img(effective_element, effective_action,
7995 crumbled = (direction == MV_NONE ?
7996 el_act2crm(effective_element, effective_action) :
7997 el_act_dir2crm(effective_element, effective_action,
8000 g = &graphic_info[graphic];
8003 if (graphic_info[graphic].anim_global_sync)
8004 sync_frame = FrameCounter;
8005 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
8006 sync_frame = GfxFrame[x][y];
8008 sync_frame = 0; /* playfield border (pseudo steel) */
8010 SetRandomAnimationValue(x, y);
8012 int frame = getAnimationFrame(g->anim_frames,
8015 g->anim_start_frame,
8018 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
8019 g->double_movement && is_backside);
8021 /* (updating the "crumbled" graphic definitions is probably not really needed,
8022 as animations for crumbled graphics can't be longer than one EMC cycle) */
8023 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8027 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
8028 int player_nr, int anim, int frame_em)
8030 int element = player_mapping[player_nr][anim].element_rnd;
8031 int action = player_mapping[player_nr][anim].action;
8032 int direction = player_mapping[player_nr][anim].direction;
8033 int graphic = (direction == MV_NONE ?
8034 el_act2img(element, action) :
8035 el_act_dir2img(element, action, direction));
8036 struct GraphicInfo *g = &graphic_info[graphic];
8039 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
8041 stored_player[player_nr].StepFrame = frame_em;
8043 sync_frame = stored_player[player_nr].Frame;
8045 int frame = getAnimationFrame(g->anim_frames,
8048 g->anim_start_frame,
8051 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
8052 &g_em->src_x, &g_em->src_y, FALSE);
8055 void InitGraphicInfo_EM(void)
8060 int num_em_gfx_errors = 0;
8062 if (graphic_info_em_object[0][0].bitmap == NULL)
8064 /* EM graphics not yet initialized in em_open_all() */
8069 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
8072 /* always start with reliable default values */
8073 for (i = 0; i < TILE_MAX; i++)
8075 object_mapping[i].element_rnd = EL_UNKNOWN;
8076 object_mapping[i].is_backside = FALSE;
8077 object_mapping[i].action = ACTION_DEFAULT;
8078 object_mapping[i].direction = MV_NONE;
8081 /* always start with reliable default values */
8082 for (p = 0; p < MAX_PLAYERS; p++)
8084 for (i = 0; i < SPR_MAX; i++)
8086 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8087 player_mapping[p][i].action = ACTION_DEFAULT;
8088 player_mapping[p][i].direction = MV_NONE;
8092 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8094 int e = em_object_mapping_list[i].element_em;
8096 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8097 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8099 if (em_object_mapping_list[i].action != -1)
8100 object_mapping[e].action = em_object_mapping_list[i].action;
8102 if (em_object_mapping_list[i].direction != -1)
8103 object_mapping[e].direction =
8104 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8107 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8109 int a = em_player_mapping_list[i].action_em;
8110 int p = em_player_mapping_list[i].player_nr;
8112 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8114 if (em_player_mapping_list[i].action != -1)
8115 player_mapping[p][a].action = em_player_mapping_list[i].action;
8117 if (em_player_mapping_list[i].direction != -1)
8118 player_mapping[p][a].direction =
8119 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8122 for (i = 0; i < TILE_MAX; i++)
8124 int element = object_mapping[i].element_rnd;
8125 int action = object_mapping[i].action;
8126 int direction = object_mapping[i].direction;
8127 boolean is_backside = object_mapping[i].is_backside;
8128 boolean action_exploding = ((action == ACTION_EXPLODING ||
8129 action == ACTION_SMASHED_BY_ROCK ||
8130 action == ACTION_SMASHED_BY_SPRING) &&
8131 element != EL_DIAMOND);
8132 boolean action_active = (action == ACTION_ACTIVE);
8133 boolean action_other = (action == ACTION_OTHER);
8135 for (j = 0; j < 8; j++)
8137 int effective_element = get_effective_element_EM(i, j);
8138 int effective_action = (j < 7 ? action :
8139 i == Xdrip_stretch ? action :
8140 i == Xdrip_stretchB ? action :
8141 i == Ydrip_s1 ? action :
8142 i == Ydrip_s1B ? action :
8143 i == Xball_1B ? action :
8144 i == Xball_2 ? action :
8145 i == Xball_2B ? action :
8146 i == Yball_eat ? action :
8147 i == Ykey_1_eat ? action :
8148 i == Ykey_2_eat ? action :
8149 i == Ykey_3_eat ? action :
8150 i == Ykey_4_eat ? action :
8151 i == Ykey_5_eat ? action :
8152 i == Ykey_6_eat ? action :
8153 i == Ykey_7_eat ? action :
8154 i == Ykey_8_eat ? action :
8155 i == Ylenses_eat ? action :
8156 i == Ymagnify_eat ? action :
8157 i == Ygrass_eat ? action :
8158 i == Ydirt_eat ? action :
8159 i == Xsand_stonein_1 ? action :
8160 i == Xsand_stonein_2 ? action :
8161 i == Xsand_stonein_3 ? action :
8162 i == Xsand_stonein_4 ? action :
8163 i == Xsand_stoneout_1 ? action :
8164 i == Xsand_stoneout_2 ? action :
8165 i == Xboom_android ? ACTION_EXPLODING :
8166 action_exploding ? ACTION_EXPLODING :
8167 action_active ? action :
8168 action_other ? action :
8170 int graphic = (el_act_dir2img(effective_element, effective_action,
8172 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8174 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8175 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8176 boolean has_action_graphics = (graphic != base_graphic);
8177 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8178 struct GraphicInfo *g = &graphic_info[graphic];
8179 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8182 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
8183 boolean special_animation = (action != ACTION_DEFAULT &&
8184 g->anim_frames == 3 &&
8185 g->anim_delay == 2 &&
8186 g->anim_mode & ANIM_LINEAR);
8187 int sync_frame = (i == Xdrip_stretch ? 7 :
8188 i == Xdrip_stretchB ? 7 :
8189 i == Ydrip_s2 ? j + 8 :
8190 i == Ydrip_s2B ? j + 8 :
8199 i == Xfake_acid_1 ? 0 :
8200 i == Xfake_acid_2 ? 10 :
8201 i == Xfake_acid_3 ? 20 :
8202 i == Xfake_acid_4 ? 30 :
8203 i == Xfake_acid_5 ? 40 :
8204 i == Xfake_acid_6 ? 50 :
8205 i == Xfake_acid_7 ? 60 :
8206 i == Xfake_acid_8 ? 70 :
8208 i == Xball_2B ? j + 8 :
8209 i == Yball_eat ? j + 1 :
8210 i == Ykey_1_eat ? j + 1 :
8211 i == Ykey_2_eat ? j + 1 :
8212 i == Ykey_3_eat ? j + 1 :
8213 i == Ykey_4_eat ? j + 1 :
8214 i == Ykey_5_eat ? j + 1 :
8215 i == Ykey_6_eat ? j + 1 :
8216 i == Ykey_7_eat ? j + 1 :
8217 i == Ykey_8_eat ? j + 1 :
8218 i == Ylenses_eat ? j + 1 :
8219 i == Ymagnify_eat ? j + 1 :
8220 i == Ygrass_eat ? j + 1 :
8221 i == Ydirt_eat ? j + 1 :
8222 i == Xamoeba_1 ? 0 :
8223 i == Xamoeba_2 ? 1 :
8224 i == Xamoeba_3 ? 2 :
8225 i == Xamoeba_4 ? 3 :
8226 i == Xamoeba_5 ? 0 :
8227 i == Xamoeba_6 ? 1 :
8228 i == Xamoeba_7 ? 2 :
8229 i == Xamoeba_8 ? 3 :
8230 i == Xexit_2 ? j + 8 :
8231 i == Xexit_3 ? j + 16 :
8232 i == Xdynamite_1 ? 0 :
8233 i == Xdynamite_2 ? 8 :
8234 i == Xdynamite_3 ? 16 :
8235 i == Xdynamite_4 ? 24 :
8236 i == Xsand_stonein_1 ? j + 1 :
8237 i == Xsand_stonein_2 ? j + 9 :
8238 i == Xsand_stonein_3 ? j + 17 :
8239 i == Xsand_stonein_4 ? j + 25 :
8240 i == Xsand_stoneout_1 && j == 0 ? 0 :
8241 i == Xsand_stoneout_1 && j == 1 ? 0 :
8242 i == Xsand_stoneout_1 && j == 2 ? 1 :
8243 i == Xsand_stoneout_1 && j == 3 ? 2 :
8244 i == Xsand_stoneout_1 && j == 4 ? 2 :
8245 i == Xsand_stoneout_1 && j == 5 ? 3 :
8246 i == Xsand_stoneout_1 && j == 6 ? 4 :
8247 i == Xsand_stoneout_1 && j == 7 ? 4 :
8248 i == Xsand_stoneout_2 && j == 0 ? 5 :
8249 i == Xsand_stoneout_2 && j == 1 ? 6 :
8250 i == Xsand_stoneout_2 && j == 2 ? 7 :
8251 i == Xsand_stoneout_2 && j == 3 ? 8 :
8252 i == Xsand_stoneout_2 && j == 4 ? 9 :
8253 i == Xsand_stoneout_2 && j == 5 ? 11 :
8254 i == Xsand_stoneout_2 && j == 6 ? 13 :
8255 i == Xsand_stoneout_2 && j == 7 ? 15 :
8256 i == Xboom_bug && j == 1 ? 2 :
8257 i == Xboom_bug && j == 2 ? 2 :
8258 i == Xboom_bug && j == 3 ? 4 :
8259 i == Xboom_bug && j == 4 ? 4 :
8260 i == Xboom_bug && j == 5 ? 2 :
8261 i == Xboom_bug && j == 6 ? 2 :
8262 i == Xboom_bug && j == 7 ? 0 :
8263 i == Xboom_bomb && j == 1 ? 2 :
8264 i == Xboom_bomb && j == 2 ? 2 :
8265 i == Xboom_bomb && j == 3 ? 4 :
8266 i == Xboom_bomb && j == 4 ? 4 :
8267 i == Xboom_bomb && j == 5 ? 2 :
8268 i == Xboom_bomb && j == 6 ? 2 :
8269 i == Xboom_bomb && j == 7 ? 0 :
8270 i == Xboom_android && j == 7 ? 6 :
8271 i == Xboom_1 && j == 1 ? 2 :
8272 i == Xboom_1 && j == 2 ? 2 :
8273 i == Xboom_1 && j == 3 ? 4 :
8274 i == Xboom_1 && j == 4 ? 4 :
8275 i == Xboom_1 && j == 5 ? 6 :
8276 i == Xboom_1 && j == 6 ? 6 :
8277 i == Xboom_1 && j == 7 ? 8 :
8278 i == Xboom_2 && j == 0 ? 8 :
8279 i == Xboom_2 && j == 1 ? 8 :
8280 i == Xboom_2 && j == 2 ? 10 :
8281 i == Xboom_2 && j == 3 ? 10 :
8282 i == Xboom_2 && j == 4 ? 10 :
8283 i == Xboom_2 && j == 5 ? 12 :
8284 i == Xboom_2 && j == 6 ? 12 :
8285 i == Xboom_2 && j == 7 ? 12 :
8286 special_animation && j == 4 ? 3 :
8287 effective_action != action ? 0 :
8291 Bitmap *debug_bitmap = g_em->bitmap;
8292 int debug_src_x = g_em->src_x;
8293 int debug_src_y = g_em->src_y;
8296 int frame = getAnimationFrame(g->anim_frames,
8299 g->anim_start_frame,
8302 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8303 g->double_movement && is_backside);
8305 g_em->bitmap = src_bitmap;
8306 g_em->src_x = src_x;
8307 g_em->src_y = src_y;
8308 g_em->src_offset_x = 0;
8309 g_em->src_offset_y = 0;
8310 g_em->dst_offset_x = 0;
8311 g_em->dst_offset_y = 0;
8312 g_em->width = TILEX;
8313 g_em->height = TILEY;
8315 g_em->preserve_background = FALSE;
8317 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8320 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8321 effective_action == ACTION_MOVING ||
8322 effective_action == ACTION_PUSHING ||
8323 effective_action == ACTION_EATING)) ||
8324 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8325 effective_action == ACTION_EMPTYING)))
8328 (effective_action == ACTION_FALLING ||
8329 effective_action == ACTION_FILLING ||
8330 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8331 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8332 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8333 int num_steps = (i == Ydrip_s1 ? 16 :
8334 i == Ydrip_s1B ? 16 :
8335 i == Ydrip_s2 ? 16 :
8336 i == Ydrip_s2B ? 16 :
8337 i == Xsand_stonein_1 ? 32 :
8338 i == Xsand_stonein_2 ? 32 :
8339 i == Xsand_stonein_3 ? 32 :
8340 i == Xsand_stonein_4 ? 32 :
8341 i == Xsand_stoneout_1 ? 16 :
8342 i == Xsand_stoneout_2 ? 16 : 8);
8343 int cx = ABS(dx) * (TILEX / num_steps);
8344 int cy = ABS(dy) * (TILEY / num_steps);
8345 int step_frame = (i == Ydrip_s2 ? j + 8 :
8346 i == Ydrip_s2B ? j + 8 :
8347 i == Xsand_stonein_2 ? j + 8 :
8348 i == Xsand_stonein_3 ? j + 16 :
8349 i == Xsand_stonein_4 ? j + 24 :
8350 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8351 int step = (is_backside ? step_frame : num_steps - step_frame);
8353 if (is_backside) /* tile where movement starts */
8355 if (dx < 0 || dy < 0)
8357 g_em->src_offset_x = cx * step;
8358 g_em->src_offset_y = cy * step;
8362 g_em->dst_offset_x = cx * step;
8363 g_em->dst_offset_y = cy * step;
8366 else /* tile where movement ends */
8368 if (dx < 0 || dy < 0)
8370 g_em->dst_offset_x = cx * step;
8371 g_em->dst_offset_y = cy * step;
8375 g_em->src_offset_x = cx * step;
8376 g_em->src_offset_y = cy * step;
8380 g_em->width = TILEX - cx * step;
8381 g_em->height = TILEY - cy * step;
8384 /* create unique graphic identifier to decide if tile must be redrawn */
8385 /* bit 31 - 16 (16 bit): EM style graphic
8386 bit 15 - 12 ( 4 bit): EM style frame
8387 bit 11 - 6 ( 6 bit): graphic width
8388 bit 5 - 0 ( 6 bit): graphic height */
8389 g_em->unique_identifier =
8390 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8394 /* skip check for EMC elements not contained in original EMC artwork */
8395 if (element == EL_EMC_FAKE_ACID)
8398 if (g_em->bitmap != debug_bitmap ||
8399 g_em->src_x != debug_src_x ||
8400 g_em->src_y != debug_src_y ||
8401 g_em->src_offset_x != 0 ||
8402 g_em->src_offset_y != 0 ||
8403 g_em->dst_offset_x != 0 ||
8404 g_em->dst_offset_y != 0 ||
8405 g_em->width != TILEX ||
8406 g_em->height != TILEY)
8408 static int last_i = -1;
8416 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8417 i, element, element_info[element].token_name,
8418 element_action_info[effective_action].suffix, direction);
8420 if (element != effective_element)
8421 printf(" [%d ('%s')]",
8423 element_info[effective_element].token_name);
8427 if (g_em->bitmap != debug_bitmap)
8428 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8429 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8431 if (g_em->src_x != debug_src_x ||
8432 g_em->src_y != debug_src_y)
8433 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8434 j, (is_backside ? 'B' : 'F'),
8435 g_em->src_x, g_em->src_y,
8436 g_em->src_x / 32, g_em->src_y / 32,
8437 debug_src_x, debug_src_y,
8438 debug_src_x / 32, debug_src_y / 32);
8440 if (g_em->src_offset_x != 0 ||
8441 g_em->src_offset_y != 0 ||
8442 g_em->dst_offset_x != 0 ||
8443 g_em->dst_offset_y != 0)
8444 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8446 g_em->src_offset_x, g_em->src_offset_y,
8447 g_em->dst_offset_x, g_em->dst_offset_y);
8449 if (g_em->width != TILEX ||
8450 g_em->height != TILEY)
8451 printf(" %d (%d): size %d,%d should be %d,%d\n",
8453 g_em->width, g_em->height, TILEX, TILEY);
8455 num_em_gfx_errors++;
8462 for (i = 0; i < TILE_MAX; i++)
8464 for (j = 0; j < 8; j++)
8466 int element = object_mapping[i].element_rnd;
8467 int action = object_mapping[i].action;
8468 int direction = object_mapping[i].direction;
8469 boolean is_backside = object_mapping[i].is_backside;
8470 int graphic_action = el_act_dir2img(element, action, direction);
8471 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8473 if ((action == ACTION_SMASHED_BY_ROCK ||
8474 action == ACTION_SMASHED_BY_SPRING ||
8475 action == ACTION_EATING) &&
8476 graphic_action == graphic_default)
8478 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8479 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8480 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8481 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8484 /* no separate animation for "smashed by rock" -- use rock instead */
8485 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8486 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8488 g_em->bitmap = g_xx->bitmap;
8489 g_em->src_x = g_xx->src_x;
8490 g_em->src_y = g_xx->src_y;
8491 g_em->src_offset_x = g_xx->src_offset_x;
8492 g_em->src_offset_y = g_xx->src_offset_y;
8493 g_em->dst_offset_x = g_xx->dst_offset_x;
8494 g_em->dst_offset_y = g_xx->dst_offset_y;
8495 g_em->width = g_xx->width;
8496 g_em->height = g_xx->height;
8497 g_em->unique_identifier = g_xx->unique_identifier;
8500 g_em->preserve_background = TRUE;
8505 for (p = 0; p < MAX_PLAYERS; p++)
8507 for (i = 0; i < SPR_MAX; i++)
8509 int element = player_mapping[p][i].element_rnd;
8510 int action = player_mapping[p][i].action;
8511 int direction = player_mapping[p][i].direction;
8513 for (j = 0; j < 8; j++)
8515 int effective_element = element;
8516 int effective_action = action;
8517 int graphic = (direction == MV_NONE ?
8518 el_act2img(effective_element, effective_action) :
8519 el_act_dir2img(effective_element, effective_action,
8521 struct GraphicInfo *g = &graphic_info[graphic];
8522 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8528 Bitmap *debug_bitmap = g_em->bitmap;
8529 int debug_src_x = g_em->src_x;
8530 int debug_src_y = g_em->src_y;
8533 int frame = getAnimationFrame(g->anim_frames,
8536 g->anim_start_frame,
8539 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8541 g_em->bitmap = src_bitmap;
8542 g_em->src_x = src_x;
8543 g_em->src_y = src_y;
8544 g_em->src_offset_x = 0;
8545 g_em->src_offset_y = 0;
8546 g_em->dst_offset_x = 0;
8547 g_em->dst_offset_y = 0;
8548 g_em->width = TILEX;
8549 g_em->height = TILEY;
8553 /* skip check for EMC elements not contained in original EMC artwork */
8554 if (element == EL_PLAYER_3 ||
8555 element == EL_PLAYER_4)
8558 if (g_em->bitmap != debug_bitmap ||
8559 g_em->src_x != debug_src_x ||
8560 g_em->src_y != debug_src_y)
8562 static int last_i = -1;
8570 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
8571 p, i, element, element_info[element].token_name,
8572 element_action_info[effective_action].suffix, direction);
8574 if (element != effective_element)
8575 printf(" [%d ('%s')]",
8577 element_info[effective_element].token_name);
8581 if (g_em->bitmap != debug_bitmap)
8582 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
8583 j, (int)(g_em->bitmap), (int)(debug_bitmap));
8585 if (g_em->src_x != debug_src_x ||
8586 g_em->src_y != debug_src_y)
8587 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8589 g_em->src_x, g_em->src_y,
8590 g_em->src_x / 32, g_em->src_y / 32,
8591 debug_src_x, debug_src_y,
8592 debug_src_x / 32, debug_src_y / 32);
8594 num_em_gfx_errors++;
8604 printf("::: [%d errors found]\n", num_em_gfx_errors);
8610 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
8611 boolean any_player_moving,
8612 boolean any_player_snapping,
8613 boolean any_player_dropping)
8615 if (frame == 0 && !any_player_dropping)
8617 if (!local_player->was_waiting)
8619 if (!CheckSaveEngineSnapshotToList())
8622 local_player->was_waiting = TRUE;
8625 else if (any_player_moving || any_player_snapping || any_player_dropping)
8627 local_player->was_waiting = FALSE;
8631 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8632 boolean murphy_is_dropping)
8634 if (murphy_is_waiting)
8636 if (!local_player->was_waiting)
8638 if (!CheckSaveEngineSnapshotToList())
8641 local_player->was_waiting = TRUE;
8646 local_player->was_waiting = FALSE;
8650 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8651 boolean any_player_moving,
8652 boolean any_player_snapping,
8653 boolean any_player_dropping)
8655 if (tape.single_step && tape.recording && !tape.pausing)
8656 if (frame == 0 && !any_player_dropping)
8657 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8659 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8660 any_player_snapping, any_player_dropping);
8663 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8664 boolean murphy_is_dropping)
8666 boolean murphy_starts_dropping = FALSE;
8669 for (i = 0; i < MAX_PLAYERS; i++)
8670 if (stored_player[i].force_dropping)
8671 murphy_starts_dropping = TRUE;
8673 if (tape.single_step && tape.recording && !tape.pausing)
8674 if (murphy_is_waiting && !murphy_starts_dropping)
8675 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8677 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8680 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8681 int graphic, int sync_frame, int x, int y)
8683 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8685 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8688 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8690 return (IS_NEXT_FRAME(sync_frame, graphic));
8693 int getGraphicInfo_Delay(int graphic)
8695 return graphic_info[graphic].anim_delay;
8698 void PlayMenuSoundExt(int sound)
8700 if (sound == SND_UNDEFINED)
8703 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8704 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8707 if (IS_LOOP_SOUND(sound))
8708 PlaySoundLoop(sound);
8713 void PlayMenuSound()
8715 PlayMenuSoundExt(menu.sound[game_status]);
8718 void PlayMenuSoundStereo(int sound, int stereo_position)
8720 if (sound == SND_UNDEFINED)
8723 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8724 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8727 if (IS_LOOP_SOUND(sound))
8728 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8730 PlaySoundStereo(sound, stereo_position);
8733 void PlayMenuSoundIfLoopExt(int sound)
8735 if (sound == SND_UNDEFINED)
8738 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8739 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8742 if (IS_LOOP_SOUND(sound))
8743 PlaySoundLoop(sound);
8746 void PlayMenuSoundIfLoop()
8748 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8751 void PlayMenuMusicExt(int music)
8753 if (music == MUS_UNDEFINED)
8756 if (!setup.sound_music)
8762 void PlayMenuMusic()
8764 char *curr_music = getCurrentlyPlayingMusicFilename();
8765 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8767 if (!strEqual(curr_music, next_music))
8768 PlayMenuMusicExt(menu.music[game_status]);
8771 void PlayMenuSoundsAndMusic()
8777 static void FadeMenuSounds()
8782 static void FadeMenuMusic()
8784 char *curr_music = getCurrentlyPlayingMusicFilename();
8785 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8787 if (!strEqual(curr_music, next_music))
8791 void FadeMenuSoundsAndMusic()
8797 void PlaySoundActivating()
8800 PlaySound(SND_MENU_ITEM_ACTIVATING);
8804 void PlaySoundSelecting()
8807 PlaySound(SND_MENU_ITEM_SELECTING);
8811 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8813 boolean change_fullscreen = (setup.fullscreen !=
8814 video.fullscreen_enabled);
8815 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8816 setup.window_scaling_percent !=
8817 video.window_scaling_percent);
8819 if (change_window_scaling_percent && video.fullscreen_enabled)
8822 if (!change_window_scaling_percent && !video.fullscreen_available)
8825 #if defined(TARGET_SDL2)
8826 if (change_window_scaling_percent)
8828 SDLSetWindowScaling(setup.window_scaling_percent);
8832 else if (change_fullscreen)
8834 SDLSetWindowFullscreen(setup.fullscreen);
8836 /* set setup value according to successfully changed fullscreen mode */
8837 setup.fullscreen = video.fullscreen_enabled;
8843 if (change_fullscreen ||
8844 change_window_scaling_percent)
8846 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8848 /* save backbuffer content which gets lost when toggling fullscreen mode */
8849 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8851 if (change_window_scaling_percent)
8853 /* keep window mode, but change window scaling */
8854 video.fullscreen_enabled = TRUE; /* force new window scaling */
8857 /* toggle fullscreen */
8858 ChangeVideoModeIfNeeded(setup.fullscreen);
8860 /* set setup value according to successfully changed fullscreen mode */
8861 setup.fullscreen = video.fullscreen_enabled;
8863 /* restore backbuffer content from temporary backbuffer backup bitmap */
8864 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8866 FreeBitmap(tmp_backbuffer);
8868 /* update visible window/screen */
8869 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8873 void JoinRectangles(int *x, int *y, int *width, int *height,
8874 int x2, int y2, int width2, int height2)
8876 // do not join with "off-screen" rectangle
8877 if (x2 == -1 || y2 == -1)
8882 *width = MAX(*width, width2);
8883 *height = MAX(*height, height2);
8886 void SetAnimStatus(int anim_status_new)
8888 if (anim_status_new == GAME_MODE_MAIN)
8889 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
8890 else if (anim_status_new == GAME_MODE_SCORES)
8891 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
8893 global.anim_status_next = anim_status_new;
8895 // directly set screen modes that are entered without fading
8896 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
8897 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
8898 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
8899 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
8900 global.anim_status = global.anim_status_next;
8903 void SetGameStatus(int game_status_new)
8905 if (game_status_new != game_status)
8906 game_status_last_screen = game_status;
8908 game_status = game_status_new;
8910 SetAnimStatus(game_status_new);
8913 void SetFontStatus(int game_status_new)
8915 static int last_game_status = -1;
8917 if (game_status_new != -1)
8919 // set game status for font use after storing last game status
8920 last_game_status = game_status;
8921 game_status = game_status_new;
8925 // reset game status after font use from last stored game status
8926 game_status = last_game_status;
8930 void ResetFontStatus()
8935 void ChangeViewportPropertiesIfNeeded()
8937 boolean use_mini_tilesize = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
8938 FALSE : setup.small_game_graphics);
8939 int gfx_game_mode = game_status;
8940 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8942 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
8943 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8944 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8945 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8946 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8947 int new_win_xsize = vp_window->width;
8948 int new_win_ysize = vp_window->height;
8949 int border_size = vp_playfield->border_size;
8950 int new_sx = vp_playfield->x + border_size;
8951 int new_sy = vp_playfield->y + border_size;
8952 int new_sxsize = vp_playfield->width - 2 * border_size;
8953 int new_sysize = vp_playfield->height - 2 * border_size;
8954 int new_real_sx = vp_playfield->x;
8955 int new_real_sy = vp_playfield->y;
8956 int new_full_sxsize = vp_playfield->width;
8957 int new_full_sysize = vp_playfield->height;
8958 int new_dx = vp_door_1->x;
8959 int new_dy = vp_door_1->y;
8960 int new_dxsize = vp_door_1->width;
8961 int new_dysize = vp_door_1->height;
8962 int new_vx = vp_door_2->x;
8963 int new_vy = vp_door_2->y;
8964 int new_vxsize = vp_door_2->width;
8965 int new_vysize = vp_door_2->height;
8966 int new_ex = vp_door_3->x;
8967 int new_ey = vp_door_3->y;
8968 int new_exsize = vp_door_3->width;
8969 int new_eysize = vp_door_3->height;
8970 int new_tilesize_var = (use_mini_tilesize ? MINI_TILESIZE : game.tile_size);
8971 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8972 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8973 int new_scr_fieldx = new_sxsize / tilesize;
8974 int new_scr_fieldy = new_sysize / tilesize;
8975 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8976 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8977 boolean init_gfx_buffers = FALSE;
8978 boolean init_video_buffer = FALSE;
8979 boolean init_gadgets_and_anims = FALSE;
8980 boolean init_em_graphics = FALSE;
8982 if (new_win_xsize != WIN_XSIZE ||
8983 new_win_ysize != WIN_YSIZE)
8985 WIN_XSIZE = new_win_xsize;
8986 WIN_YSIZE = new_win_ysize;
8988 init_video_buffer = TRUE;
8989 init_gfx_buffers = TRUE;
8990 init_gadgets_and_anims = TRUE;
8992 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8995 if (new_scr_fieldx != SCR_FIELDX ||
8996 new_scr_fieldy != SCR_FIELDY)
8998 /* this always toggles between MAIN and GAME when using small tile size */
9000 SCR_FIELDX = new_scr_fieldx;
9001 SCR_FIELDY = new_scr_fieldy;
9003 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
9014 new_sxsize != SXSIZE ||
9015 new_sysize != SYSIZE ||
9016 new_dxsize != DXSIZE ||
9017 new_dysize != DYSIZE ||
9018 new_vxsize != VXSIZE ||
9019 new_vysize != VYSIZE ||
9020 new_exsize != EXSIZE ||
9021 new_eysize != EYSIZE ||
9022 new_real_sx != REAL_SX ||
9023 new_real_sy != REAL_SY ||
9024 new_full_sxsize != FULL_SXSIZE ||
9025 new_full_sysize != FULL_SYSIZE ||
9026 new_tilesize_var != TILESIZE_VAR
9029 // ------------------------------------------------------------------------
9030 // determine next fading area for changed viewport definitions
9031 // ------------------------------------------------------------------------
9033 // start with current playfield area (default fading area)
9036 FADE_SXSIZE = FULL_SXSIZE;
9037 FADE_SYSIZE = FULL_SYSIZE;
9039 // add new playfield area if position or size has changed
9040 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
9041 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
9043 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9044 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
9047 // add current and new door 1 area if position or size has changed
9048 if (new_dx != DX || new_dy != DY ||
9049 new_dxsize != DXSIZE || new_dysize != DYSIZE)
9051 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9052 DX, DY, DXSIZE, DYSIZE);
9053 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9054 new_dx, new_dy, new_dxsize, new_dysize);
9057 // add current and new door 2 area if position or size has changed
9058 if (new_dx != VX || new_dy != VY ||
9059 new_dxsize != VXSIZE || new_dysize != VYSIZE)
9061 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9062 VX, VY, VXSIZE, VYSIZE);
9063 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
9064 new_vx, new_vy, new_vxsize, new_vysize);
9067 // ------------------------------------------------------------------------
9068 // handle changed tile size
9069 // ------------------------------------------------------------------------
9071 if (new_tilesize_var != TILESIZE_VAR)
9073 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
9075 // changing tile size invalidates scroll values of engine snapshots
9076 FreeEngineSnapshotSingle();
9078 // changing tile size requires update of graphic mapping for EM engine
9079 init_em_graphics = TRUE;
9090 SXSIZE = new_sxsize;
9091 SYSIZE = new_sysize;
9092 DXSIZE = new_dxsize;
9093 DYSIZE = new_dysize;
9094 VXSIZE = new_vxsize;
9095 VYSIZE = new_vysize;
9096 EXSIZE = new_exsize;
9097 EYSIZE = new_eysize;
9098 REAL_SX = new_real_sx;
9099 REAL_SY = new_real_sy;
9100 FULL_SXSIZE = new_full_sxsize;
9101 FULL_SYSIZE = new_full_sysize;
9102 TILESIZE_VAR = new_tilesize_var;
9104 init_gfx_buffers = TRUE;
9105 init_gadgets_and_anims = TRUE;
9107 // printf("::: viewports: init_gfx_buffers\n");
9108 // printf("::: viewports: init_gadgets_and_anims\n");
9111 if (init_gfx_buffers)
9113 // printf("::: init_gfx_buffers\n");
9115 SCR_FIELDX = new_scr_fieldx_buffers;
9116 SCR_FIELDY = new_scr_fieldy_buffers;
9120 SCR_FIELDX = new_scr_fieldx;
9121 SCR_FIELDY = new_scr_fieldy;
9123 SetDrawDeactivationMask(REDRAW_NONE);
9124 SetDrawBackgroundMask(REDRAW_FIELD);
9127 if (init_video_buffer)
9129 // printf("::: init_video_buffer\n");
9131 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9132 InitImageTextures();
9135 if (init_gadgets_and_anims)
9137 // printf("::: init_gadgets_and_anims\n");
9140 InitGlobalAnimations();
9143 if (init_em_graphics)
9145 InitGraphicInfo_EM();