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 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1365 for (x = 0; x < lev_fieldx; x++)
1367 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1368 BorderElement = EL_STEELWALL;
1370 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1376 void FloodFillLevel(int from_x, int from_y, int fill_element,
1377 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1378 int max_fieldx, int max_fieldy)
1382 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1383 static int safety = 0;
1385 /* check if starting field still has the desired content */
1386 if (field[from_x][from_y] == fill_element)
1391 if (safety > max_fieldx * max_fieldy)
1392 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1394 old_element = field[from_x][from_y];
1395 field[from_x][from_y] = fill_element;
1397 for (i = 0; i < 4; i++)
1399 x = from_x + check[i][0];
1400 y = from_y + check[i][1];
1402 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1403 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1409 void SetRandomAnimationValue(int x, int y)
1411 gfx.anim_random_frame = GfxRandom[x][y];
1414 int getGraphicAnimationFrame(int graphic, int sync_frame)
1416 /* animation synchronized with global frame counter, not move position */
1417 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1418 sync_frame = FrameCounter;
1420 return getAnimationFrame(graphic_info[graphic].anim_frames,
1421 graphic_info[graphic].anim_delay,
1422 graphic_info[graphic].anim_mode,
1423 graphic_info[graphic].anim_start_frame,
1427 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1429 struct GraphicInfo *g = &graphic_info[graphic];
1430 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1432 if (tilesize == gfx.standard_tile_size)
1433 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1434 else if (tilesize == game.tile_size)
1435 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1437 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1440 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1441 boolean get_backside)
1443 struct GraphicInfo *g = &graphic_info[graphic];
1444 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1445 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1447 if (g->offset_y == 0) /* frames are ordered horizontally */
1449 int max_width = g->anim_frames_per_line * g->width;
1450 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1452 *x = pos % max_width;
1453 *y = src_y % g->height + pos / max_width * g->height;
1455 else if (g->offset_x == 0) /* frames are ordered vertically */
1457 int max_height = g->anim_frames_per_line * g->height;
1458 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1460 *x = src_x % g->width + pos / max_height * g->width;
1461 *y = pos % max_height;
1463 else /* frames are ordered diagonally */
1465 *x = src_x + frame * g->offset_x;
1466 *y = src_y + frame * g->offset_y;
1470 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1471 Bitmap **bitmap, int *x, int *y,
1472 boolean get_backside)
1474 struct GraphicInfo *g = &graphic_info[graphic];
1476 // if no in-game graphics defined, always use standard graphic size
1477 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1478 tilesize = TILESIZE;
1480 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1481 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1483 *x = *x * tilesize / g->tile_size;
1484 *y = *y * tilesize / g->tile_size;
1487 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1488 Bitmap **bitmap, int *x, int *y)
1490 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1493 void getFixedGraphicSource(int graphic, int frame,
1494 Bitmap **bitmap, int *x, int *y)
1496 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1499 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1501 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1504 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1505 int *x, int *y, boolean get_backside)
1507 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1511 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1513 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1516 void DrawGraphic(int x, int y, int graphic, int frame)
1519 if (!IN_SCR_FIELD(x, y))
1521 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1522 printf("DrawGraphic(): This should never happen!\n");
1527 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1530 MarkTileDirty(x, y);
1533 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1536 if (!IN_SCR_FIELD(x, y))
1538 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1539 printf("DrawGraphic(): This should never happen!\n");
1544 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1546 MarkTileDirty(x, y);
1549 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1555 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1557 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1560 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1566 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1567 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1570 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1573 if (!IN_SCR_FIELD(x, y))
1575 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1576 printf("DrawGraphicThruMask(): This should never happen!\n");
1581 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1584 MarkTileDirty(x, y);
1587 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1590 if (!IN_SCR_FIELD(x, y))
1592 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1593 printf("DrawGraphicThruMask(): This should never happen!\n");
1598 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1600 MarkTileDirty(x, y);
1603 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1609 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1611 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1615 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1616 int graphic, int frame)
1621 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1623 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1627 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1629 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1631 MarkTileDirty(x / tilesize, y / tilesize);
1634 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1640 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1641 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1644 void DrawMiniGraphic(int x, int y, int graphic)
1646 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1647 MarkTileDirty(x / 2, y / 2);
1650 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1655 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1656 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1659 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1660 int graphic, int frame,
1661 int cut_mode, int mask_mode)
1666 int width = TILEX, height = TILEY;
1669 if (dx || dy) /* shifted graphic */
1671 if (x < BX1) /* object enters playfield from the left */
1678 else if (x > BX2) /* object enters playfield from the right */
1684 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1690 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1692 else if (dx) /* general horizontal movement */
1693 MarkTileDirty(x + SIGN(dx), y);
1695 if (y < BY1) /* object enters playfield from the top */
1697 if (cut_mode == CUT_BELOW) /* object completely above top border */
1705 else if (y > BY2) /* object enters playfield from the bottom */
1711 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1717 else if (dy > 0 && cut_mode == CUT_ABOVE)
1719 if (y == BY2) /* object completely above bottom border */
1725 MarkTileDirty(x, y + 1);
1726 } /* object leaves playfield to the bottom */
1727 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1729 else if (dy) /* general vertical movement */
1730 MarkTileDirty(x, y + SIGN(dy));
1734 if (!IN_SCR_FIELD(x, y))
1736 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1737 printf("DrawGraphicShifted(): This should never happen!\n");
1742 width = width * TILESIZE_VAR / TILESIZE;
1743 height = height * TILESIZE_VAR / TILESIZE;
1744 cx = cx * TILESIZE_VAR / TILESIZE;
1745 cy = cy * TILESIZE_VAR / TILESIZE;
1746 dx = dx * TILESIZE_VAR / TILESIZE;
1747 dy = dy * TILESIZE_VAR / TILESIZE;
1749 if (width > 0 && height > 0)
1751 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1756 dst_x = FX + x * TILEX_VAR + dx;
1757 dst_y = FY + y * TILEY_VAR + dy;
1759 if (mask_mode == USE_MASKING)
1760 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1763 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1766 MarkTileDirty(x, y);
1770 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1771 int graphic, int frame,
1772 int cut_mode, int mask_mode)
1777 int width = TILEX_VAR, height = TILEY_VAR;
1780 int x2 = x + SIGN(dx);
1781 int y2 = y + SIGN(dy);
1783 /* movement with two-tile animations must be sync'ed with movement position,
1784 not with current GfxFrame (which can be higher when using slow movement) */
1785 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1786 int anim_frames = graphic_info[graphic].anim_frames;
1788 /* (we also need anim_delay here for movement animations with less frames) */
1789 int anim_delay = graphic_info[graphic].anim_delay;
1790 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1792 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1793 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1795 /* re-calculate animation frame for two-tile movement animation */
1796 frame = getGraphicAnimationFrame(graphic, sync_frame);
1798 /* check if movement start graphic inside screen area and should be drawn */
1799 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1801 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1803 dst_x = FX + x1 * TILEX_VAR;
1804 dst_y = FY + y1 * TILEY_VAR;
1806 if (mask_mode == USE_MASKING)
1807 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1810 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1813 MarkTileDirty(x1, y1);
1816 /* check if movement end graphic inside screen area and should be drawn */
1817 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1819 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1821 dst_x = FX + x2 * TILEX_VAR;
1822 dst_y = FY + y2 * TILEY_VAR;
1824 if (mask_mode == USE_MASKING)
1825 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1828 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1831 MarkTileDirty(x2, y2);
1835 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1836 int graphic, int frame,
1837 int cut_mode, int mask_mode)
1841 DrawGraphic(x, y, graphic, frame);
1846 if (graphic_info[graphic].double_movement) /* EM style movement images */
1847 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1849 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1852 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1853 int frame, int cut_mode)
1855 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1858 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1859 int cut_mode, int mask_mode)
1861 int lx = LEVELX(x), ly = LEVELY(y);
1865 if (IN_LEV_FIELD(lx, ly))
1867 SetRandomAnimationValue(lx, ly);
1869 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1870 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1872 /* do not use double (EM style) movement graphic when not moving */
1873 if (graphic_info[graphic].double_movement && !dx && !dy)
1875 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1876 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1879 else /* border element */
1881 graphic = el2img(element);
1882 frame = getGraphicAnimationFrame(graphic, -1);
1885 if (element == EL_EXPANDABLE_WALL)
1887 boolean left_stopped = FALSE, right_stopped = FALSE;
1889 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1890 left_stopped = TRUE;
1891 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1892 right_stopped = TRUE;
1894 if (left_stopped && right_stopped)
1896 else if (left_stopped)
1898 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1899 frame = graphic_info[graphic].anim_frames - 1;
1901 else if (right_stopped)
1903 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1904 frame = graphic_info[graphic].anim_frames - 1;
1909 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1910 else if (mask_mode == USE_MASKING)
1911 DrawGraphicThruMask(x, y, graphic, frame);
1913 DrawGraphic(x, y, graphic, frame);
1916 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1917 int cut_mode, int mask_mode)
1919 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1920 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1921 cut_mode, mask_mode);
1924 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1927 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1930 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1933 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1936 void DrawLevelElementThruMask(int x, int y, int element)
1938 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1941 void DrawLevelFieldThruMask(int x, int y)
1943 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1946 /* !!! implementation of quicksand is totally broken !!! */
1947 #define IS_CRUMBLED_TILE(x, y, e) \
1948 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1949 !IS_MOVING(x, y) || \
1950 (e) == EL_QUICKSAND_EMPTYING || \
1951 (e) == EL_QUICKSAND_FAST_EMPTYING))
1953 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1958 int width, height, cx, cy;
1959 int sx = SCREENX(x), sy = SCREENY(y);
1960 int crumbled_border_size = graphic_info[graphic].border_size;
1961 int crumbled_tile_size = graphic_info[graphic].tile_size;
1962 int crumbled_border_size_var =
1963 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
1966 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1968 for (i = 1; i < 4; i++)
1970 int dxx = (i & 1 ? dx : 0);
1971 int dyy = (i & 2 ? dy : 0);
1974 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1977 /* check if neighbour field is of same crumble type */
1978 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1979 graphic_info[graphic].class ==
1980 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1982 /* return if check prevents inner corner */
1983 if (same == (dxx == dx && dyy == dy))
1987 /* if we reach this point, we have an inner corner */
1989 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1991 width = crumbled_border_size_var;
1992 height = crumbled_border_size_var;
1993 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1994 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1996 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1997 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2000 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2005 int width, height, bx, by, cx, cy;
2006 int sx = SCREENX(x), sy = SCREENY(y);
2007 int crumbled_border_size = graphic_info[graphic].border_size;
2008 int crumbled_tile_size = graphic_info[graphic].tile_size;
2009 int crumbled_border_size_var =
2010 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2011 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2014 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2016 /* draw simple, sloppy, non-corner-accurate crumbled border */
2018 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2019 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2020 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2021 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2023 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2024 FX + sx * TILEX_VAR + cx,
2025 FY + sy * TILEY_VAR + cy);
2027 /* (remaining middle border part must be at least as big as corner part) */
2028 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2029 crumbled_border_size_var >= TILESIZE_VAR / 3)
2032 /* correct corners of crumbled border, if needed */
2034 for (i = -1; i <= 1; i += 2)
2036 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2037 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2038 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2041 /* check if neighbour field is of same crumble type */
2042 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2043 graphic_info[graphic].class ==
2044 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2046 /* no crumbled corner, but continued crumbled border */
2048 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2049 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2050 int b1 = (i == 1 ? crumbled_border_size_var :
2051 TILESIZE_VAR - 2 * crumbled_border_size_var);
2053 width = crumbled_border_size_var;
2054 height = crumbled_border_size_var;
2056 if (dir == 1 || dir == 2)
2071 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2073 FX + sx * TILEX_VAR + cx,
2074 FY + sy * TILEY_VAR + cy);
2079 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2081 int sx = SCREENX(x), sy = SCREENY(y);
2084 static int xy[4][2] =
2092 if (!IN_LEV_FIELD(x, y))
2095 element = TILE_GFX_ELEMENT(x, y);
2097 if (IS_CRUMBLED_TILE(x, y, element)) /* crumble field itself */
2099 if (!IN_SCR_FIELD(sx, sy))
2102 /* crumble field borders towards direct neighbour fields */
2103 for (i = 0; i < 4; i++)
2105 int xx = x + xy[i][0];
2106 int yy = y + xy[i][1];
2108 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2111 /* check if neighbour field is of same crumble type */
2112 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2113 graphic_info[graphic].class ==
2114 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2117 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2120 /* crumble inner field corners towards corner neighbour fields */
2121 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2122 graphic_info[graphic].anim_frames == 2)
2124 for (i = 0; i < 4; i++)
2126 int dx = (i & 1 ? +1 : -1);
2127 int dy = (i & 2 ? +1 : -1);
2129 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2133 MarkTileDirty(sx, sy);
2135 else /* center field is not crumbled -- crumble neighbour fields */
2137 /* crumble field borders of direct neighbour fields */
2138 for (i = 0; i < 4; i++)
2140 int xx = x + xy[i][0];
2141 int yy = y + xy[i][1];
2142 int sxx = sx + xy[i][0];
2143 int syy = sy + xy[i][1];
2145 if (!IN_LEV_FIELD(xx, yy) ||
2146 !IN_SCR_FIELD(sxx, syy))
2149 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2152 element = TILE_GFX_ELEMENT(xx, yy);
2154 if (!IS_CRUMBLED_TILE(xx, yy, element))
2157 graphic = el_act2crm(element, ACTION_DEFAULT);
2159 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2161 MarkTileDirty(sxx, syy);
2164 /* crumble inner field corners of corner neighbour fields */
2165 for (i = 0; i < 4; i++)
2167 int dx = (i & 1 ? +1 : -1);
2168 int dy = (i & 2 ? +1 : -1);
2174 if (!IN_LEV_FIELD(xx, yy) ||
2175 !IN_SCR_FIELD(sxx, syy))
2178 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2181 element = TILE_GFX_ELEMENT(xx, yy);
2183 if (!IS_CRUMBLED_TILE(xx, yy, element))
2186 graphic = el_act2crm(element, ACTION_DEFAULT);
2188 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2189 graphic_info[graphic].anim_frames == 2)
2190 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2192 MarkTileDirty(sxx, syy);
2197 void DrawLevelFieldCrumbled(int x, int y)
2201 if (!IN_LEV_FIELD(x, y))
2204 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2205 GfxElement[x][y] != EL_UNDEFINED &&
2206 GFX_CRUMBLED(GfxElement[x][y]))
2208 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2213 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2215 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2218 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2221 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2222 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2223 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2224 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2225 int sx = SCREENX(x), sy = SCREENY(y);
2227 DrawGraphic(sx, sy, graphic1, frame1);
2228 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2231 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2233 int sx = SCREENX(x), sy = SCREENY(y);
2234 static int xy[4][2] =
2243 /* crumble direct neighbour fields (required for field borders) */
2244 for (i = 0; i < 4; i++)
2246 int xx = x + xy[i][0];
2247 int yy = y + xy[i][1];
2248 int sxx = sx + xy[i][0];
2249 int syy = sy + xy[i][1];
2251 if (!IN_LEV_FIELD(xx, yy) ||
2252 !IN_SCR_FIELD(sxx, syy) ||
2253 !GFX_CRUMBLED(Feld[xx][yy]) ||
2257 DrawLevelField(xx, yy);
2260 /* crumble corner neighbour fields (required for inner field corners) */
2261 for (i = 0; i < 4; i++)
2263 int dx = (i & 1 ? +1 : -1);
2264 int dy = (i & 2 ? +1 : -1);
2270 if (!IN_LEV_FIELD(xx, yy) ||
2271 !IN_SCR_FIELD(sxx, syy) ||
2272 !GFX_CRUMBLED(Feld[xx][yy]) ||
2276 int element = TILE_GFX_ELEMENT(xx, yy);
2277 int graphic = el_act2crm(element, ACTION_DEFAULT);
2279 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2280 graphic_info[graphic].anim_frames == 2)
2281 DrawLevelField(xx, yy);
2285 static int getBorderElement(int x, int y)
2289 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2290 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2291 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2292 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2293 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2294 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2295 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2297 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2298 int steel_position = (x == -1 && y == -1 ? 0 :
2299 x == lev_fieldx && y == -1 ? 1 :
2300 x == -1 && y == lev_fieldy ? 2 :
2301 x == lev_fieldx && y == lev_fieldy ? 3 :
2302 x == -1 || x == lev_fieldx ? 4 :
2303 y == -1 || y == lev_fieldy ? 5 : 6);
2305 return border[steel_position][steel_type];
2308 void DrawScreenElement(int x, int y, int element)
2310 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2311 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2314 void DrawLevelElement(int x, int y, int element)
2316 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2317 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2320 void DrawScreenField(int x, int y)
2322 int lx = LEVELX(x), ly = LEVELY(y);
2323 int element, content;
2325 if (!IN_LEV_FIELD(lx, ly))
2327 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2330 element = getBorderElement(lx, ly);
2332 DrawScreenElement(x, y, element);
2337 element = Feld[lx][ly];
2338 content = Store[lx][ly];
2340 if (IS_MOVING(lx, ly))
2342 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2343 boolean cut_mode = NO_CUTTING;
2345 if (element == EL_QUICKSAND_EMPTYING ||
2346 element == EL_QUICKSAND_FAST_EMPTYING ||
2347 element == EL_MAGIC_WALL_EMPTYING ||
2348 element == EL_BD_MAGIC_WALL_EMPTYING ||
2349 element == EL_DC_MAGIC_WALL_EMPTYING ||
2350 element == EL_AMOEBA_DROPPING)
2351 cut_mode = CUT_ABOVE;
2352 else if (element == EL_QUICKSAND_FILLING ||
2353 element == EL_QUICKSAND_FAST_FILLING ||
2354 element == EL_MAGIC_WALL_FILLING ||
2355 element == EL_BD_MAGIC_WALL_FILLING ||
2356 element == EL_DC_MAGIC_WALL_FILLING)
2357 cut_mode = CUT_BELOW;
2359 if (cut_mode == CUT_ABOVE)
2360 DrawScreenElement(x, y, element);
2362 DrawScreenElement(x, y, EL_EMPTY);
2365 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2366 else if (cut_mode == NO_CUTTING)
2367 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2370 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2372 if (cut_mode == CUT_BELOW &&
2373 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2374 DrawLevelElement(lx, ly + 1, element);
2377 if (content == EL_ACID)
2379 int dir = MovDir[lx][ly];
2380 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2381 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2383 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2385 // prevent target field from being drawn again (but without masking)
2386 // (this would happen if target field is scanned after moving element)
2387 Stop[newlx][newly] = TRUE;
2390 else if (IS_BLOCKED(lx, ly))
2395 boolean cut_mode = NO_CUTTING;
2396 int element_old, content_old;
2398 Blocked2Moving(lx, ly, &oldx, &oldy);
2401 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2402 MovDir[oldx][oldy] == MV_RIGHT);
2404 element_old = Feld[oldx][oldy];
2405 content_old = Store[oldx][oldy];
2407 if (element_old == EL_QUICKSAND_EMPTYING ||
2408 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2409 element_old == EL_MAGIC_WALL_EMPTYING ||
2410 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2411 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2412 element_old == EL_AMOEBA_DROPPING)
2413 cut_mode = CUT_ABOVE;
2415 DrawScreenElement(x, y, EL_EMPTY);
2418 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2420 else if (cut_mode == NO_CUTTING)
2421 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2424 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2427 else if (IS_DRAWABLE(element))
2428 DrawScreenElement(x, y, element);
2430 DrawScreenElement(x, y, EL_EMPTY);
2433 void DrawLevelField(int x, int y)
2435 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2436 DrawScreenField(SCREENX(x), SCREENY(y));
2437 else if (IS_MOVING(x, y))
2441 Moving2Blocked(x, y, &newx, &newy);
2442 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2443 DrawScreenField(SCREENX(newx), SCREENY(newy));
2445 else if (IS_BLOCKED(x, y))
2449 Blocked2Moving(x, y, &oldx, &oldy);
2450 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2451 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2455 void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2456 int (*el2img_function)(int))
2458 int element_base = map_mm_wall_element(element);
2459 int element_bits = (IS_DF_WALL(element) ?
2460 element - EL_DF_WALL_START :
2461 element - EL_MM_WALL_START) & 0x000f;
2462 int graphic = el2img_function(element_base);
2463 int tilesize_draw = tilesize / 2;
2468 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2470 for (i = 0; i < 4; i++)
2472 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2473 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2475 if (element_bits & (1 << i))
2476 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize_draw, tilesize_draw,
2477 dst_draw_x, dst_draw_y);
2479 ClearRectangle(drawto, dst_x, dst_y, tilesize_draw, tilesize_draw);
2483 void DrawSizedElement(int x, int y, int element, int tilesize)
2485 if (IS_MM_WALL(element))
2487 DrawSizedWall_MM(SX + x * tilesize, SY + y * tilesize,
2488 element, tilesize, el2edimg);
2492 int graphic = el2edimg(element);
2494 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2498 void DrawMiniElement(int x, int y, int element)
2502 graphic = el2edimg(element);
2503 DrawMiniGraphic(x, y, graphic);
2506 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2509 int x = sx + scroll_x, y = sy + scroll_y;
2511 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2512 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2513 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2514 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2516 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2519 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2521 int x = sx + scroll_x, y = sy + scroll_y;
2523 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2524 DrawMiniElement(sx, sy, EL_EMPTY);
2525 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2526 DrawMiniElement(sx, sy, Feld[x][y]);
2528 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2531 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2532 int x, int y, int xsize, int ysize,
2533 int tile_width, int tile_height)
2537 int dst_x = startx + x * tile_width;
2538 int dst_y = starty + y * tile_height;
2539 int width = graphic_info[graphic].width;
2540 int height = graphic_info[graphic].height;
2541 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2542 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2543 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2544 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2545 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2546 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2547 boolean draw_masked = graphic_info[graphic].draw_masked;
2549 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2551 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2553 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2557 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2558 inner_sx + (x - 1) * tile_width % inner_width);
2559 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2560 inner_sy + (y - 1) * tile_height % inner_height);
2563 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2566 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2570 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2571 int x, int y, int xsize, int ysize, int font_nr)
2573 int font_width = getFontWidth(font_nr);
2574 int font_height = getFontHeight(font_nr);
2576 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2577 font_width, font_height);
2580 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2582 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2583 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2584 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2585 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2586 boolean no_delay = (tape.warp_forward);
2587 unsigned int anim_delay = 0;
2588 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2589 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2590 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2591 int font_width = getFontWidth(font_nr);
2592 int font_height = getFontHeight(font_nr);
2593 int max_xsize = level.envelope[envelope_nr].xsize;
2594 int max_ysize = level.envelope[envelope_nr].ysize;
2595 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2596 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2597 int xend = max_xsize;
2598 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2599 int xstep = (xstart < xend ? 1 : 0);
2600 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2602 int end = MAX(xend - xstart, yend - ystart);
2605 for (i = start; i <= end; i++)
2607 int last_frame = end; // last frame of this "for" loop
2608 int x = xstart + i * xstep;
2609 int y = ystart + i * ystep;
2610 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2611 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2612 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2613 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2616 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2618 BlitScreenToBitmap(backbuffer);
2620 SetDrawtoField(DRAW_TO_BACKBUFFER);
2622 for (yy = 0; yy < ysize; yy++)
2623 for (xx = 0; xx < xsize; xx++)
2624 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2626 DrawTextBuffer(sx + font_width, sy + font_height,
2627 level.envelope[envelope_nr].text, font_nr, max_xsize,
2628 xsize - 2, ysize - 2, 0, mask_mode,
2629 level.envelope[envelope_nr].autowrap,
2630 level.envelope[envelope_nr].centered, FALSE);
2632 redraw_mask |= REDRAW_FIELD;
2635 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2639 void ShowEnvelope(int envelope_nr)
2641 int element = EL_ENVELOPE_1 + envelope_nr;
2642 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2643 int sound_opening = element_info[element].sound[ACTION_OPENING];
2644 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2645 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2646 boolean no_delay = (tape.warp_forward);
2647 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2648 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2649 int anim_mode = graphic_info[graphic].anim_mode;
2650 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2651 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2653 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2655 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2657 if (anim_mode == ANIM_DEFAULT)
2658 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2660 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2663 Delay(wait_delay_value);
2665 WaitForEventToContinue();
2667 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2669 if (anim_mode != ANIM_NONE)
2670 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2672 if (anim_mode == ANIM_DEFAULT)
2673 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2675 game.envelope_active = FALSE;
2677 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2679 redraw_mask |= REDRAW_FIELD;
2683 static void setRequestBasePosition(int *x, int *y)
2685 int sx_base, sy_base;
2687 if (request.x != -1)
2688 sx_base = request.x;
2689 else if (request.align == ALIGN_LEFT)
2691 else if (request.align == ALIGN_RIGHT)
2692 sx_base = SX + SXSIZE;
2694 sx_base = SX + SXSIZE / 2;
2696 if (request.y != -1)
2697 sy_base = request.y;
2698 else if (request.valign == VALIGN_TOP)
2700 else if (request.valign == VALIGN_BOTTOM)
2701 sy_base = SY + SYSIZE;
2703 sy_base = SY + SYSIZE / 2;
2709 static void setRequestPositionExt(int *x, int *y, int width, int height,
2710 boolean add_border_size)
2712 int border_size = request.border_size;
2713 int sx_base, sy_base;
2716 setRequestBasePosition(&sx_base, &sy_base);
2718 if (request.align == ALIGN_LEFT)
2720 else if (request.align == ALIGN_RIGHT)
2721 sx = sx_base - width;
2723 sx = sx_base - width / 2;
2725 if (request.valign == VALIGN_TOP)
2727 else if (request.valign == VALIGN_BOTTOM)
2728 sy = sy_base - height;
2730 sy = sy_base - height / 2;
2732 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2733 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2735 if (add_border_size)
2745 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2747 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2750 void DrawEnvelopeRequest(char *text)
2752 char *text_final = text;
2753 char *text_door_style = NULL;
2754 int graphic = IMG_BACKGROUND_REQUEST;
2755 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2756 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2757 int font_nr = FONT_REQUEST;
2758 int font_width = getFontWidth(font_nr);
2759 int font_height = getFontHeight(font_nr);
2760 int border_size = request.border_size;
2761 int line_spacing = request.line_spacing;
2762 int line_height = font_height + line_spacing;
2763 int max_text_width = request.width - 2 * border_size;
2764 int max_text_height = request.height - 2 * border_size;
2765 int line_length = max_text_width / font_width;
2766 int max_lines = max_text_height / line_height;
2767 int text_width = line_length * font_width;
2768 int width = request.width;
2769 int height = request.height;
2770 int tile_size = MAX(request.step_offset, 1);
2771 int x_steps = width / tile_size;
2772 int y_steps = height / tile_size;
2773 int sx_offset = border_size;
2774 int sy_offset = border_size;
2778 if (request.centered)
2779 sx_offset = (request.width - text_width) / 2;
2781 if (request.wrap_single_words && !request.autowrap)
2783 char *src_text_ptr, *dst_text_ptr;
2785 text_door_style = checked_malloc(2 * strlen(text) + 1);
2787 src_text_ptr = text;
2788 dst_text_ptr = text_door_style;
2790 while (*src_text_ptr)
2792 if (*src_text_ptr == ' ' ||
2793 *src_text_ptr == '?' ||
2794 *src_text_ptr == '!')
2795 *dst_text_ptr++ = '\n';
2797 if (*src_text_ptr != ' ')
2798 *dst_text_ptr++ = *src_text_ptr;
2803 *dst_text_ptr = '\0';
2805 text_final = text_door_style;
2808 setRequestPosition(&sx, &sy, FALSE);
2810 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2812 for (y = 0; y < y_steps; y++)
2813 for (x = 0; x < x_steps; x++)
2814 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2815 x, y, x_steps, y_steps,
2816 tile_size, tile_size);
2818 /* force DOOR font inside door area */
2819 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2821 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2822 line_length, -1, max_lines, line_spacing, mask_mode,
2823 request.autowrap, request.centered, FALSE);
2827 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2828 RedrawGadget(tool_gadget[i]);
2830 // store readily prepared envelope request for later use when animating
2831 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2833 if (text_door_style)
2834 free(text_door_style);
2837 void AnimateEnvelopeRequest(int anim_mode, int action)
2839 int graphic = IMG_BACKGROUND_REQUEST;
2840 boolean draw_masked = graphic_info[graphic].draw_masked;
2841 int delay_value_normal = request.step_delay;
2842 int delay_value_fast = delay_value_normal / 2;
2843 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2844 boolean no_delay = (tape.warp_forward);
2845 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2846 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
2847 unsigned int anim_delay = 0;
2849 int tile_size = MAX(request.step_offset, 1);
2850 int max_xsize = request.width / tile_size;
2851 int max_ysize = request.height / tile_size;
2852 int max_xsize_inner = max_xsize - 2;
2853 int max_ysize_inner = max_ysize - 2;
2855 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2856 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2857 int xend = max_xsize_inner;
2858 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2859 int xstep = (xstart < xend ? 1 : 0);
2860 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2862 int end = MAX(xend - xstart, yend - ystart);
2865 if (setup.quick_doors)
2872 for (i = start; i <= end; i++)
2874 int last_frame = end; // last frame of this "for" loop
2875 int x = xstart + i * xstep;
2876 int y = ystart + i * ystep;
2877 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2878 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2879 int xsize_size_left = (xsize - 1) * tile_size;
2880 int ysize_size_top = (ysize - 1) * tile_size;
2881 int max_xsize_pos = (max_xsize - 1) * tile_size;
2882 int max_ysize_pos = (max_ysize - 1) * tile_size;
2883 int width = xsize * tile_size;
2884 int height = ysize * tile_size;
2889 setRequestPosition(&src_x, &src_y, FALSE);
2890 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2892 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2894 for (yy = 0; yy < 2; yy++)
2896 for (xx = 0; xx < 2; xx++)
2898 int src_xx = src_x + xx * max_xsize_pos;
2899 int src_yy = src_y + yy * max_ysize_pos;
2900 int dst_xx = dst_x + xx * xsize_size_left;
2901 int dst_yy = dst_y + yy * ysize_size_top;
2902 int xx_size = (xx ? tile_size : xsize_size_left);
2903 int yy_size = (yy ? tile_size : ysize_size_top);
2906 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
2907 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2909 BlitBitmap(bitmap_db_store_2, backbuffer,
2910 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2914 redraw_mask |= REDRAW_FIELD;
2918 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2922 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2924 int graphic = IMG_BACKGROUND_REQUEST;
2925 int sound_opening = SND_REQUEST_OPENING;
2926 int sound_closing = SND_REQUEST_CLOSING;
2927 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2928 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2929 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2930 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2931 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2933 if (game_status == GAME_MODE_PLAYING)
2934 BlitScreenToBitmap(backbuffer);
2936 SetDrawtoField(DRAW_TO_BACKBUFFER);
2938 // SetDrawBackgroundMask(REDRAW_NONE);
2940 if (action == ACTION_OPENING)
2942 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2944 if (req_state & REQ_ASK)
2946 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2947 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2949 else if (req_state & REQ_CONFIRM)
2951 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2953 else if (req_state & REQ_PLAYER)
2955 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2956 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2957 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2958 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2961 DrawEnvelopeRequest(text);
2964 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2966 if (action == ACTION_OPENING)
2968 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2970 if (anim_mode == ANIM_DEFAULT)
2971 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2973 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2977 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2979 if (anim_mode != ANIM_NONE)
2980 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2982 if (anim_mode == ANIM_DEFAULT)
2983 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2986 game.envelope_active = FALSE;
2988 if (action == ACTION_CLOSING)
2989 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2991 // SetDrawBackgroundMask(last_draw_background_mask);
2993 redraw_mask |= REDRAW_FIELD;
2997 if (action == ACTION_CLOSING &&
2998 game_status == GAME_MODE_PLAYING &&
2999 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3000 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3003 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3005 if (IS_MM_WALL(element))
3007 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3013 int graphic = el2preimg(element);
3015 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3016 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3021 void DrawLevel(int draw_background_mask)
3025 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3026 SetDrawBackgroundMask(draw_background_mask);
3030 for (x = BX1; x <= BX2; x++)
3031 for (y = BY1; y <= BY2; y++)
3032 DrawScreenField(x, y);
3034 redraw_mask |= REDRAW_FIELD;
3037 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3042 for (x = 0; x < size_x; x++)
3043 for (y = 0; y < size_y; y++)
3044 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3046 redraw_mask |= REDRAW_FIELD;
3049 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3053 for (x = 0; x < size_x; x++)
3054 for (y = 0; y < size_y; y++)
3055 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3057 redraw_mask |= REDRAW_FIELD;
3060 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3062 boolean show_level_border = (BorderElement != EL_EMPTY);
3063 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3064 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3065 int tile_size = preview.tile_size;
3066 int preview_width = preview.xsize * tile_size;
3067 int preview_height = preview.ysize * tile_size;
3068 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3069 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3070 int real_preview_width = real_preview_xsize * tile_size;
3071 int real_preview_height = real_preview_ysize * tile_size;
3072 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3073 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3076 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3079 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3081 dst_x += (preview_width - real_preview_width) / 2;
3082 dst_y += (preview_height - real_preview_height) / 2;
3084 for (x = 0; x < real_preview_xsize; x++)
3086 for (y = 0; y < real_preview_ysize; y++)
3088 int lx = from_x + x + (show_level_border ? -1 : 0);
3089 int ly = from_y + y + (show_level_border ? -1 : 0);
3090 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3091 getBorderElement(lx, ly));
3093 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3094 element, tile_size);
3098 redraw_mask |= REDRAW_FIELD;
3101 #define MICROLABEL_EMPTY 0
3102 #define MICROLABEL_LEVEL_NAME 1
3103 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3104 #define MICROLABEL_LEVEL_AUTHOR 3
3105 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3106 #define MICROLABEL_IMPORTED_FROM 5
3107 #define MICROLABEL_IMPORTED_BY_HEAD 6
3108 #define MICROLABEL_IMPORTED_BY 7
3110 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3112 int max_text_width = SXSIZE;
3113 int font_width = getFontWidth(font_nr);
3115 if (pos->align == ALIGN_CENTER)
3116 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3117 else if (pos->align == ALIGN_RIGHT)
3118 max_text_width = pos->x;
3120 max_text_width = SXSIZE - pos->x;
3122 return max_text_width / font_width;
3125 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3127 char label_text[MAX_OUTPUT_LINESIZE + 1];
3128 int max_len_label_text;
3129 int font_nr = pos->font;
3132 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3135 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3136 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3137 mode == MICROLABEL_IMPORTED_BY_HEAD)
3138 font_nr = pos->font_alt;
3140 max_len_label_text = getMaxTextLength(pos, font_nr);
3142 if (pos->size != -1)
3143 max_len_label_text = pos->size;
3145 for (i = 0; i < max_len_label_text; i++)
3146 label_text[i] = ' ';
3147 label_text[max_len_label_text] = '\0';
3149 if (strlen(label_text) > 0)
3150 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3153 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3154 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3155 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3156 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3157 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3158 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3159 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3160 max_len_label_text);
3161 label_text[max_len_label_text] = '\0';
3163 if (strlen(label_text) > 0)
3164 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3166 redraw_mask |= REDRAW_FIELD;
3169 static void DrawPreviewLevelLabel(int mode)
3171 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3174 static void DrawPreviewLevelInfo(int mode)
3176 if (mode == MICROLABEL_LEVEL_NAME)
3177 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3178 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3179 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3182 static void DrawPreviewLevelExt(boolean restart)
3184 static unsigned int scroll_delay = 0;
3185 static unsigned int label_delay = 0;
3186 static int from_x, from_y, scroll_direction;
3187 static int label_state, label_counter;
3188 unsigned int scroll_delay_value = preview.step_delay;
3189 boolean show_level_border = (BorderElement != EL_EMPTY);
3190 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3191 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3198 if (preview.anim_mode == ANIM_CENTERED)
3200 if (level_xsize > preview.xsize)
3201 from_x = (level_xsize - preview.xsize) / 2;
3202 if (level_ysize > preview.ysize)
3203 from_y = (level_ysize - preview.ysize) / 2;
3206 from_x += preview.xoffset;
3207 from_y += preview.yoffset;
3209 scroll_direction = MV_RIGHT;
3213 DrawPreviewLevelPlayfield(from_x, from_y);
3214 DrawPreviewLevelLabel(label_state);
3216 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3217 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3219 /* initialize delay counters */
3220 DelayReached(&scroll_delay, 0);
3221 DelayReached(&label_delay, 0);
3223 if (leveldir_current->name)
3225 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3226 char label_text[MAX_OUTPUT_LINESIZE + 1];
3227 int font_nr = pos->font;
3228 int max_len_label_text = getMaxTextLength(pos, font_nr);
3230 if (pos->size != -1)
3231 max_len_label_text = pos->size;
3233 strncpy(label_text, leveldir_current->name, max_len_label_text);
3234 label_text[max_len_label_text] = '\0';
3236 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3237 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3243 /* scroll preview level, if needed */
3244 if (preview.anim_mode != ANIM_NONE &&
3245 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3246 DelayReached(&scroll_delay, scroll_delay_value))
3248 switch (scroll_direction)
3253 from_x -= preview.step_offset;
3254 from_x = (from_x < 0 ? 0 : from_x);
3257 scroll_direction = MV_UP;
3261 if (from_x < level_xsize - preview.xsize)
3263 from_x += preview.step_offset;
3264 from_x = (from_x > level_xsize - preview.xsize ?
3265 level_xsize - preview.xsize : from_x);
3268 scroll_direction = MV_DOWN;
3274 from_y -= preview.step_offset;
3275 from_y = (from_y < 0 ? 0 : from_y);
3278 scroll_direction = MV_RIGHT;
3282 if (from_y < level_ysize - preview.ysize)
3284 from_y += preview.step_offset;
3285 from_y = (from_y > level_ysize - preview.ysize ?
3286 level_ysize - preview.ysize : from_y);
3289 scroll_direction = MV_LEFT;
3296 DrawPreviewLevelPlayfield(from_x, from_y);
3299 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
3300 /* redraw micro level label, if needed */
3301 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3302 !strEqual(level.author, ANONYMOUS_NAME) &&
3303 !strEqual(level.author, leveldir_current->name) &&
3304 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3306 int max_label_counter = 23;
3308 if (leveldir_current->imported_from != NULL &&
3309 strlen(leveldir_current->imported_from) > 0)
3310 max_label_counter += 14;
3311 if (leveldir_current->imported_by != NULL &&
3312 strlen(leveldir_current->imported_by) > 0)
3313 max_label_counter += 14;
3315 label_counter = (label_counter + 1) % max_label_counter;
3316 label_state = (label_counter >= 0 && label_counter <= 7 ?
3317 MICROLABEL_LEVEL_NAME :
3318 label_counter >= 9 && label_counter <= 12 ?
3319 MICROLABEL_LEVEL_AUTHOR_HEAD :
3320 label_counter >= 14 && label_counter <= 21 ?
3321 MICROLABEL_LEVEL_AUTHOR :
3322 label_counter >= 23 && label_counter <= 26 ?
3323 MICROLABEL_IMPORTED_FROM_HEAD :
3324 label_counter >= 28 && label_counter <= 35 ?
3325 MICROLABEL_IMPORTED_FROM :
3326 label_counter >= 37 && label_counter <= 40 ?
3327 MICROLABEL_IMPORTED_BY_HEAD :
3328 label_counter >= 42 && label_counter <= 49 ?
3329 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3331 if (leveldir_current->imported_from == NULL &&
3332 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3333 label_state == MICROLABEL_IMPORTED_FROM))
3334 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3335 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3337 DrawPreviewLevelLabel(label_state);
3341 void DrawPreviewLevelInitial()
3343 DrawPreviewLevelExt(TRUE);
3346 void DrawPreviewLevelAnimation()
3348 DrawPreviewLevelExt(FALSE);
3351 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3352 int graphic, int sync_frame,
3355 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3357 if (mask_mode == USE_MASKING)
3358 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3360 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3363 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3364 int graphic, int sync_frame, int mask_mode)
3366 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3368 if (mask_mode == USE_MASKING)
3369 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3371 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3374 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3376 int lx = LEVELX(x), ly = LEVELY(y);
3378 if (!IN_SCR_FIELD(x, y))
3381 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3382 graphic, GfxFrame[lx][ly], NO_MASKING);
3384 MarkTileDirty(x, y);
3387 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3389 int lx = LEVELX(x), ly = LEVELY(y);
3391 if (!IN_SCR_FIELD(x, y))
3394 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3395 graphic, GfxFrame[lx][ly], NO_MASKING);
3396 MarkTileDirty(x, y);
3399 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3401 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3404 void DrawLevelElementAnimation(int x, int y, int element)
3406 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3408 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3411 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3413 int sx = SCREENX(x), sy = SCREENY(y);
3415 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3418 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3421 DrawGraphicAnimation(sx, sy, graphic);
3424 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3425 DrawLevelFieldCrumbled(x, y);
3427 if (GFX_CRUMBLED(Feld[x][y]))
3428 DrawLevelFieldCrumbled(x, y);
3432 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3434 int sx = SCREENX(x), sy = SCREENY(y);
3437 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3440 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3442 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3445 DrawGraphicAnimation(sx, sy, graphic);
3447 if (GFX_CRUMBLED(element))
3448 DrawLevelFieldCrumbled(x, y);
3451 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3453 if (player->use_murphy)
3455 /* this works only because currently only one player can be "murphy" ... */
3456 static int last_horizontal_dir = MV_LEFT;
3457 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3459 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3460 last_horizontal_dir = move_dir;
3462 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3464 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3466 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3472 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3475 static boolean equalGraphics(int graphic1, int graphic2)
3477 struct GraphicInfo *g1 = &graphic_info[graphic1];
3478 struct GraphicInfo *g2 = &graphic_info[graphic2];
3480 return (g1->bitmap == g2->bitmap &&
3481 g1->src_x == g2->src_x &&
3482 g1->src_y == g2->src_y &&
3483 g1->anim_frames == g2->anim_frames &&
3484 g1->anim_delay == g2->anim_delay &&
3485 g1->anim_mode == g2->anim_mode);
3488 void DrawAllPlayers()
3492 for (i = 0; i < MAX_PLAYERS; i++)
3493 if (stored_player[i].active)
3494 DrawPlayer(&stored_player[i]);
3497 void DrawPlayerField(int x, int y)
3499 if (!IS_PLAYER(x, y))
3502 DrawPlayer(PLAYERINFO(x, y));
3505 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3507 void DrawPlayer(struct PlayerInfo *player)
3509 int jx = player->jx;
3510 int jy = player->jy;
3511 int move_dir = player->MovDir;
3512 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3513 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3514 int last_jx = (player->is_moving ? jx - dx : jx);
3515 int last_jy = (player->is_moving ? jy - dy : jy);
3516 int next_jx = jx + dx;
3517 int next_jy = jy + dy;
3518 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3519 boolean player_is_opaque = FALSE;
3520 int sx = SCREENX(jx), sy = SCREENY(jy);
3521 int sxx = 0, syy = 0;
3522 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3524 int action = ACTION_DEFAULT;
3525 int last_player_graphic = getPlayerGraphic(player, move_dir);
3526 int last_player_frame = player->Frame;
3529 /* GfxElement[][] is set to the element the player is digging or collecting;
3530 remove also for off-screen player if the player is not moving anymore */
3531 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3532 GfxElement[jx][jy] = EL_UNDEFINED;
3534 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3538 if (!IN_LEV_FIELD(jx, jy))
3540 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3541 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3542 printf("DrawPlayerField(): This should never happen!\n");
3547 if (element == EL_EXPLOSION)
3550 action = (player->is_pushing ? ACTION_PUSHING :
3551 player->is_digging ? ACTION_DIGGING :
3552 player->is_collecting ? ACTION_COLLECTING :
3553 player->is_moving ? ACTION_MOVING :
3554 player->is_snapping ? ACTION_SNAPPING :
3555 player->is_dropping ? ACTION_DROPPING :
3556 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3558 if (player->is_waiting)
3559 move_dir = player->dir_waiting;
3561 InitPlayerGfxAnimation(player, action, move_dir);
3563 /* ----------------------------------------------------------------------- */
3564 /* draw things in the field the player is leaving, if needed */
3565 /* ----------------------------------------------------------------------- */
3567 if (player->is_moving)
3569 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3571 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3573 if (last_element == EL_DYNAMITE_ACTIVE ||
3574 last_element == EL_EM_DYNAMITE_ACTIVE ||
3575 last_element == EL_SP_DISK_RED_ACTIVE)
3576 DrawDynamite(last_jx, last_jy);
3578 DrawLevelFieldThruMask(last_jx, last_jy);
3580 else if (last_element == EL_DYNAMITE_ACTIVE ||
3581 last_element == EL_EM_DYNAMITE_ACTIVE ||
3582 last_element == EL_SP_DISK_RED_ACTIVE)
3583 DrawDynamite(last_jx, last_jy);
3585 /* !!! this is not enough to prevent flickering of players which are
3586 moving next to each others without a free tile between them -- this
3587 can only be solved by drawing all players layer by layer (first the
3588 background, then the foreground etc.) !!! => TODO */
3589 else if (!IS_PLAYER(last_jx, last_jy))
3590 DrawLevelField(last_jx, last_jy);
3593 DrawLevelField(last_jx, last_jy);
3596 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3597 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3600 if (!IN_SCR_FIELD(sx, sy))
3603 /* ----------------------------------------------------------------------- */
3604 /* draw things behind the player, if needed */
3605 /* ----------------------------------------------------------------------- */
3608 DrawLevelElement(jx, jy, Back[jx][jy]);
3609 else if (IS_ACTIVE_BOMB(element))
3610 DrawLevelElement(jx, jy, EL_EMPTY);
3613 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3615 int old_element = GfxElement[jx][jy];
3616 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3617 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3619 if (GFX_CRUMBLED(old_element))
3620 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3622 DrawGraphic(sx, sy, old_graphic, frame);
3624 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3625 player_is_opaque = TRUE;
3629 GfxElement[jx][jy] = EL_UNDEFINED;
3631 /* make sure that pushed elements are drawn with correct frame rate */
3632 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3634 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3635 GfxFrame[jx][jy] = player->StepFrame;
3637 DrawLevelField(jx, jy);
3641 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3642 /* ----------------------------------------------------------------------- */
3643 /* draw player himself */
3644 /* ----------------------------------------------------------------------- */
3646 graphic = getPlayerGraphic(player, move_dir);
3648 /* in the case of changed player action or direction, prevent the current
3649 animation frame from being restarted for identical animations */
3650 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3651 player->Frame = last_player_frame;
3653 frame = getGraphicAnimationFrame(graphic, player->Frame);
3657 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3658 sxx = player->GfxPos;
3660 syy = player->GfxPos;
3663 if (player_is_opaque)
3664 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3666 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3668 if (SHIELD_ON(player))
3670 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3671 IMG_SHIELD_NORMAL_ACTIVE);
3672 int frame = getGraphicAnimationFrame(graphic, -1);
3674 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3678 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3681 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3682 sxx = player->GfxPos;
3684 syy = player->GfxPos;
3688 /* ----------------------------------------------------------------------- */
3689 /* draw things the player is pushing, if needed */
3690 /* ----------------------------------------------------------------------- */
3692 if (player->is_pushing && player->is_moving)
3694 int px = SCREENX(jx), py = SCREENY(jy);
3695 int pxx = (TILEX - ABS(sxx)) * dx;
3696 int pyy = (TILEY - ABS(syy)) * dy;
3697 int gfx_frame = GfxFrame[jx][jy];
3703 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3705 element = Feld[next_jx][next_jy];
3706 gfx_frame = GfxFrame[next_jx][next_jy];
3709 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3711 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3712 frame = getGraphicAnimationFrame(graphic, sync_frame);
3714 /* draw background element under pushed element (like the Sokoban field) */
3715 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3717 /* this allows transparent pushing animation over non-black background */
3720 DrawLevelElement(jx, jy, Back[jx][jy]);
3722 DrawLevelElement(jx, jy, EL_EMPTY);
3724 if (Back[next_jx][next_jy])
3725 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3727 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3729 else if (Back[next_jx][next_jy])
3730 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3733 /* do not draw (EM style) pushing animation when pushing is finished */
3734 /* (two-tile animations usually do not contain start and end frame) */
3735 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3736 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3738 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3740 /* masked drawing is needed for EMC style (double) movement graphics */
3741 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3742 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3746 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3747 /* ----------------------------------------------------------------------- */
3748 /* draw player himself */
3749 /* ----------------------------------------------------------------------- */
3751 graphic = getPlayerGraphic(player, move_dir);
3753 /* in the case of changed player action or direction, prevent the current
3754 animation frame from being restarted for identical animations */
3755 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3756 player->Frame = last_player_frame;
3758 frame = getGraphicAnimationFrame(graphic, player->Frame);
3762 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3763 sxx = player->GfxPos;
3765 syy = player->GfxPos;
3768 if (player_is_opaque)
3769 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3771 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3773 if (SHIELD_ON(player))
3775 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3776 IMG_SHIELD_NORMAL_ACTIVE);
3777 int frame = getGraphicAnimationFrame(graphic, -1);
3779 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3783 /* ----------------------------------------------------------------------- */
3784 /* draw things in front of player (active dynamite or dynabombs) */
3785 /* ----------------------------------------------------------------------- */
3787 if (IS_ACTIVE_BOMB(element))
3789 graphic = el2img(element);
3790 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3792 if (game.emulation == EMU_SUPAPLEX)
3793 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3795 DrawGraphicThruMask(sx, sy, graphic, frame);
3798 if (player_is_moving && last_element == EL_EXPLOSION)
3800 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3801 GfxElement[last_jx][last_jy] : EL_EMPTY);
3802 int graphic = el_act2img(element, ACTION_EXPLODING);
3803 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3804 int phase = ExplodePhase[last_jx][last_jy] - 1;
3805 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3808 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3811 /* ----------------------------------------------------------------------- */
3812 /* draw elements the player is just walking/passing through/under */
3813 /* ----------------------------------------------------------------------- */
3815 if (player_is_moving)
3817 /* handle the field the player is leaving ... */
3818 if (IS_ACCESSIBLE_INSIDE(last_element))
3819 DrawLevelField(last_jx, last_jy);
3820 else if (IS_ACCESSIBLE_UNDER(last_element))
3821 DrawLevelFieldThruMask(last_jx, last_jy);
3824 /* do not redraw accessible elements if the player is just pushing them */
3825 if (!player_is_moving || !player->is_pushing)
3827 /* ... and the field the player is entering */
3828 if (IS_ACCESSIBLE_INSIDE(element))
3829 DrawLevelField(jx, jy);
3830 else if (IS_ACCESSIBLE_UNDER(element))
3831 DrawLevelFieldThruMask(jx, jy);
3834 MarkTileDirty(sx, sy);
3837 /* ------------------------------------------------------------------------- */
3839 void WaitForEventToContinue()
3841 boolean still_wait = TRUE;
3843 if (program.headless)
3846 /* simulate releasing mouse button over last gadget, if still pressed */
3848 HandleGadgets(-1, -1, 0);
3850 button_status = MB_RELEASED;
3858 if (NextValidEvent(&event))
3862 case EVENT_BUTTONPRESS:
3863 case EVENT_KEYPRESS:
3864 #if defined(TARGET_SDL2)
3865 case SDL_CONTROLLERBUTTONDOWN:
3867 case SDL_JOYBUTTONDOWN:
3871 case EVENT_KEYRELEASE:
3872 ClearPlayerAction();
3876 HandleOtherEvents(&event);
3880 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3889 #define MAX_REQUEST_LINES 13
3890 #define MAX_REQUEST_LINE_FONT1_LEN 7
3891 #define MAX_REQUEST_LINE_FONT2_LEN 10
3893 static int RequestHandleEvents(unsigned int req_state)
3895 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3896 local_player->LevelSolved_GameEnd);
3897 int width = request.width;
3898 int height = request.height;
3902 setRequestPosition(&sx, &sy, FALSE);
3904 button_status = MB_RELEASED;
3906 request_gadget_id = -1;
3913 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3915 HandleGameActions();
3917 SetDrawtoField(DRAW_TO_BACKBUFFER);
3919 if (global.use_envelope_request)
3921 /* copy current state of request area to middle of playfield area */
3922 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
3930 while (NextValidEvent(&event))
3934 case EVENT_BUTTONPRESS:
3935 case EVENT_BUTTONRELEASE:
3936 case EVENT_MOTIONNOTIFY:
3940 if (event.type == EVENT_MOTIONNOTIFY)
3945 motion_status = TRUE;
3946 mx = ((MotionEvent *) &event)->x;
3947 my = ((MotionEvent *) &event)->y;
3951 motion_status = FALSE;
3952 mx = ((ButtonEvent *) &event)->x;
3953 my = ((ButtonEvent *) &event)->y;
3954 if (event.type == EVENT_BUTTONPRESS)
3955 button_status = ((ButtonEvent *) &event)->button;
3957 button_status = MB_RELEASED;
3960 /* this sets 'request_gadget_id' */
3961 HandleGadgets(mx, my, button_status);
3963 switch (request_gadget_id)
3965 case TOOL_CTRL_ID_YES:
3968 case TOOL_CTRL_ID_NO:
3971 case TOOL_CTRL_ID_CONFIRM:
3972 result = TRUE | FALSE;
3975 case TOOL_CTRL_ID_PLAYER_1:
3978 case TOOL_CTRL_ID_PLAYER_2:
3981 case TOOL_CTRL_ID_PLAYER_3:
3984 case TOOL_CTRL_ID_PLAYER_4:
3995 #if defined(TARGET_SDL2)
3996 case SDL_WINDOWEVENT:
3997 HandleWindowEvent((WindowEvent *) &event);
4000 case SDL_APP_WILLENTERBACKGROUND:
4001 case SDL_APP_DIDENTERBACKGROUND:
4002 case SDL_APP_WILLENTERFOREGROUND:
4003 case SDL_APP_DIDENTERFOREGROUND:
4004 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4008 case EVENT_KEYPRESS:
4010 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4015 if (req_state & REQ_CONFIRM)
4020 #if defined(TARGET_SDL2)
4023 #if defined(KSYM_Rewind)
4024 case KSYM_Rewind: /* for Amazon Fire TV remote */
4031 #if defined(TARGET_SDL2)
4033 #if defined(KSYM_FastForward)
4034 case KSYM_FastForward: /* for Amazon Fire TV remote */
4041 HandleKeysDebug(key);
4045 if (req_state & REQ_PLAYER)
4051 case EVENT_KEYRELEASE:
4052 ClearPlayerAction();
4055 #if defined(TARGET_SDL2)
4056 case SDL_CONTROLLERBUTTONDOWN:
4057 switch (event.cbutton.button)
4059 case SDL_CONTROLLER_BUTTON_A:
4060 case SDL_CONTROLLER_BUTTON_X:
4061 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4065 case SDL_CONTROLLER_BUTTON_B:
4066 case SDL_CONTROLLER_BUTTON_Y:
4067 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4068 case SDL_CONTROLLER_BUTTON_BACK:
4073 if (req_state & REQ_PLAYER)
4078 case SDL_CONTROLLERBUTTONUP:
4079 HandleJoystickEvent(&event);
4080 ClearPlayerAction();
4085 HandleOtherEvents(&event);
4090 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4092 int joy = AnyJoystick();
4094 if (joy & JOY_BUTTON_1)
4096 else if (joy & JOY_BUTTON_2)
4102 if (global.use_envelope_request)
4104 /* copy back current state of pressed buttons inside request area */
4105 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4115 static boolean RequestDoor(char *text, unsigned int req_state)
4117 unsigned int old_door_state;
4118 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4119 int font_nr = FONT_TEXT_2;
4124 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4126 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4127 font_nr = FONT_TEXT_1;
4130 if (game_status == GAME_MODE_PLAYING)
4131 BlitScreenToBitmap(backbuffer);
4133 /* disable deactivated drawing when quick-loading level tape recording */
4134 if (tape.playing && tape.deactivate_display)
4135 TapeDeactivateDisplayOff(TRUE);
4137 SetMouseCursor(CURSOR_DEFAULT);
4139 #if defined(NETWORK_AVALIABLE)
4140 /* pause network game while waiting for request to answer */
4141 if (options.network &&
4142 game_status == GAME_MODE_PLAYING &&
4143 req_state & REQUEST_WAIT_FOR_INPUT)
4144 SendToServer_PausePlaying();
4147 old_door_state = GetDoorState();
4149 /* simulate releasing mouse button over last gadget, if still pressed */
4151 HandleGadgets(-1, -1, 0);
4155 /* draw released gadget before proceeding */
4158 if (old_door_state & DOOR_OPEN_1)
4160 CloseDoor(DOOR_CLOSE_1);
4162 /* save old door content */
4163 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4164 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4167 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4168 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4170 /* clear door drawing field */
4171 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4173 /* force DOOR font inside door area */
4174 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4176 /* write text for request */
4177 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4179 char text_line[max_request_line_len + 1];
4185 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4187 tc = *(text_ptr + tx);
4188 // if (!tc || tc == ' ')
4189 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4193 if ((tc == '?' || tc == '!') && tl == 0)
4203 strncpy(text_line, text_ptr, tl);
4206 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4207 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4208 text_line, font_nr);
4210 text_ptr += tl + (tc == ' ' ? 1 : 0);
4211 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4216 if (req_state & REQ_ASK)
4218 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4219 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4221 else if (req_state & REQ_CONFIRM)
4223 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4225 else if (req_state & REQ_PLAYER)
4227 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4228 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4229 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4230 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4233 /* copy request gadgets to door backbuffer */
4234 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4236 OpenDoor(DOOR_OPEN_1);
4238 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4240 if (game_status == GAME_MODE_PLAYING)
4242 SetPanelBackground();
4243 SetDrawBackgroundMask(REDRAW_DOOR_1);
4247 SetDrawBackgroundMask(REDRAW_FIELD);
4253 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4255 // ---------- handle request buttons ----------
4256 result = RequestHandleEvents(req_state);
4260 if (!(req_state & REQ_STAY_OPEN))
4262 CloseDoor(DOOR_CLOSE_1);
4264 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4265 (req_state & REQ_REOPEN))
4266 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4271 if (game_status == GAME_MODE_PLAYING)
4273 SetPanelBackground();
4274 SetDrawBackgroundMask(REDRAW_DOOR_1);
4278 SetDrawBackgroundMask(REDRAW_FIELD);
4281 #if defined(NETWORK_AVALIABLE)
4282 /* continue network game after request */
4283 if (options.network &&
4284 game_status == GAME_MODE_PLAYING &&
4285 req_state & REQUEST_WAIT_FOR_INPUT)
4286 SendToServer_ContinuePlaying();
4289 /* restore deactivated drawing when quick-loading level tape recording */
4290 if (tape.playing && tape.deactivate_display)
4291 TapeDeactivateDisplayOn();
4296 static boolean RequestEnvelope(char *text, unsigned int req_state)
4300 if (game_status == GAME_MODE_PLAYING)
4301 BlitScreenToBitmap(backbuffer);
4303 /* disable deactivated drawing when quick-loading level tape recording */
4304 if (tape.playing && tape.deactivate_display)
4305 TapeDeactivateDisplayOff(TRUE);
4307 SetMouseCursor(CURSOR_DEFAULT);
4309 #if defined(NETWORK_AVALIABLE)
4310 /* pause network game while waiting for request to answer */
4311 if (options.network &&
4312 game_status == GAME_MODE_PLAYING &&
4313 req_state & REQUEST_WAIT_FOR_INPUT)
4314 SendToServer_PausePlaying();
4317 /* simulate releasing mouse button over last gadget, if still pressed */
4319 HandleGadgets(-1, -1, 0);
4323 // (replace with setting corresponding request background)
4324 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4325 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4327 /* clear door drawing field */
4328 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4330 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4332 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4334 if (game_status == GAME_MODE_PLAYING)
4336 SetPanelBackground();
4337 SetDrawBackgroundMask(REDRAW_DOOR_1);
4341 SetDrawBackgroundMask(REDRAW_FIELD);
4347 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4349 // ---------- handle request buttons ----------
4350 result = RequestHandleEvents(req_state);
4354 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4358 if (game_status == GAME_MODE_PLAYING)
4360 SetPanelBackground();
4361 SetDrawBackgroundMask(REDRAW_DOOR_1);
4365 SetDrawBackgroundMask(REDRAW_FIELD);
4368 #if defined(NETWORK_AVALIABLE)
4369 /* continue network game after request */
4370 if (options.network &&
4371 game_status == GAME_MODE_PLAYING &&
4372 req_state & REQUEST_WAIT_FOR_INPUT)
4373 SendToServer_ContinuePlaying();
4376 /* restore deactivated drawing when quick-loading level tape recording */
4377 if (tape.playing && tape.deactivate_display)
4378 TapeDeactivateDisplayOn();
4383 boolean Request(char *text, unsigned int req_state)
4385 boolean overlay_active = GetOverlayActive();
4388 SetOverlayActive(FALSE);
4390 if (global.use_envelope_request)
4391 result = RequestEnvelope(text, req_state);
4393 result = RequestDoor(text, req_state);
4395 SetOverlayActive(overlay_active);
4400 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4402 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4403 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4406 if (dpo1->sort_priority != dpo2->sort_priority)
4407 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4409 compare_result = dpo1->nr - dpo2->nr;
4411 return compare_result;
4414 void InitGraphicCompatibilityInfo_Doors()
4420 struct DoorInfo *door;
4424 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4425 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4427 { -1, -1, -1, NULL }
4429 struct Rect door_rect_list[] =
4431 { DX, DY, DXSIZE, DYSIZE },
4432 { VX, VY, VXSIZE, VYSIZE }
4436 for (i = 0; doors[i].door_token != -1; i++)
4438 int door_token = doors[i].door_token;
4439 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4440 int part_1 = doors[i].part_1;
4441 int part_8 = doors[i].part_8;
4442 int part_2 = part_1 + 1;
4443 int part_3 = part_1 + 2;
4444 struct DoorInfo *door = doors[i].door;
4445 struct Rect *door_rect = &door_rect_list[door_index];
4446 boolean door_gfx_redefined = FALSE;
4448 /* check if any door part graphic definitions have been redefined */
4450 for (j = 0; door_part_controls[j].door_token != -1; j++)
4452 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4453 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4455 if (dpc->door_token == door_token && fi->redefined)
4456 door_gfx_redefined = TRUE;
4459 /* check for old-style door graphic/animation modifications */
4461 if (!door_gfx_redefined)
4463 if (door->anim_mode & ANIM_STATIC_PANEL)
4465 door->panel.step_xoffset = 0;
4466 door->panel.step_yoffset = 0;
4469 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4471 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4472 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4473 int num_door_steps, num_panel_steps;
4475 /* remove door part graphics other than the two default wings */
4477 for (j = 0; door_part_controls[j].door_token != -1; j++)
4479 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4480 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4482 if (dpc->graphic >= part_3 &&
4483 dpc->graphic <= part_8)
4487 /* set graphics and screen positions of the default wings */
4489 g_part_1->width = door_rect->width;
4490 g_part_1->height = door_rect->height;
4491 g_part_2->width = door_rect->width;
4492 g_part_2->height = door_rect->height;
4493 g_part_2->src_x = door_rect->width;
4494 g_part_2->src_y = g_part_1->src_y;
4496 door->part_2.x = door->part_1.x;
4497 door->part_2.y = door->part_1.y;
4499 if (door->width != -1)
4501 g_part_1->width = door->width;
4502 g_part_2->width = door->width;
4504 // special treatment for graphics and screen position of right wing
4505 g_part_2->src_x += door_rect->width - door->width;
4506 door->part_2.x += door_rect->width - door->width;
4509 if (door->height != -1)
4511 g_part_1->height = door->height;
4512 g_part_2->height = door->height;
4514 // special treatment for graphics and screen position of bottom wing
4515 g_part_2->src_y += door_rect->height - door->height;
4516 door->part_2.y += door_rect->height - door->height;
4519 /* set animation delays for the default wings and panels */
4521 door->part_1.step_delay = door->step_delay;
4522 door->part_2.step_delay = door->step_delay;
4523 door->panel.step_delay = door->step_delay;
4525 /* set animation draw order for the default wings */
4527 door->part_1.sort_priority = 2; /* draw left wing over ... */
4528 door->part_2.sort_priority = 1; /* ... right wing */
4530 /* set animation draw offset for the default wings */
4532 if (door->anim_mode & ANIM_HORIZONTAL)
4534 door->part_1.step_xoffset = door->step_offset;
4535 door->part_1.step_yoffset = 0;
4536 door->part_2.step_xoffset = door->step_offset * -1;
4537 door->part_2.step_yoffset = 0;
4539 num_door_steps = g_part_1->width / door->step_offset;
4541 else // ANIM_VERTICAL
4543 door->part_1.step_xoffset = 0;
4544 door->part_1.step_yoffset = door->step_offset;
4545 door->part_2.step_xoffset = 0;
4546 door->part_2.step_yoffset = door->step_offset * -1;
4548 num_door_steps = g_part_1->height / door->step_offset;
4551 /* set animation draw offset for the default panels */
4553 if (door->step_offset > 1)
4555 num_panel_steps = 2 * door_rect->height / door->step_offset;
4556 door->panel.start_step = num_panel_steps - num_door_steps;
4557 door->panel.start_step_closing = door->panel.start_step;
4561 num_panel_steps = door_rect->height / door->step_offset;
4562 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4563 door->panel.start_step_closing = door->panel.start_step;
4564 door->panel.step_delay *= 2;
4575 for (i = 0; door_part_controls[i].door_token != -1; i++)
4577 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4578 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4580 /* initialize "start_step_opening" and "start_step_closing", if needed */
4581 if (dpc->pos->start_step_opening == 0 &&
4582 dpc->pos->start_step_closing == 0)
4584 // dpc->pos->start_step_opening = dpc->pos->start_step;
4585 dpc->pos->start_step_closing = dpc->pos->start_step;
4588 /* fill structure for door part draw order (sorted below) */
4590 dpo->sort_priority = dpc->pos->sort_priority;
4593 /* sort door part controls according to sort_priority and graphic number */
4594 qsort(door_part_order, MAX_DOOR_PARTS,
4595 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4598 unsigned int OpenDoor(unsigned int door_state)
4600 if (door_state & DOOR_COPY_BACK)
4602 if (door_state & DOOR_OPEN_1)
4603 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4604 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4606 if (door_state & DOOR_OPEN_2)
4607 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4608 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4610 door_state &= ~DOOR_COPY_BACK;
4613 return MoveDoor(door_state);
4616 unsigned int CloseDoor(unsigned int door_state)
4618 unsigned int old_door_state = GetDoorState();
4620 if (!(door_state & DOOR_NO_COPY_BACK))
4622 if (old_door_state & DOOR_OPEN_1)
4623 BlitBitmap(backbuffer, bitmap_db_door_1,
4624 DX, DY, DXSIZE, DYSIZE, 0, 0);
4626 if (old_door_state & DOOR_OPEN_2)
4627 BlitBitmap(backbuffer, bitmap_db_door_2,
4628 VX, VY, VXSIZE, VYSIZE, 0, 0);
4630 door_state &= ~DOOR_NO_COPY_BACK;
4633 return MoveDoor(door_state);
4636 unsigned int GetDoorState()
4638 return MoveDoor(DOOR_GET_STATE);
4641 unsigned int SetDoorState(unsigned int door_state)
4643 return MoveDoor(door_state | DOOR_SET_STATE);
4646 int euclid(int a, int b)
4648 return (b ? euclid(b, a % b) : a);
4651 unsigned int MoveDoor(unsigned int door_state)
4653 struct Rect door_rect_list[] =
4655 { DX, DY, DXSIZE, DYSIZE },
4656 { VX, VY, VXSIZE, VYSIZE }
4658 static int door1 = DOOR_CLOSE_1;
4659 static int door2 = DOOR_CLOSE_2;
4660 unsigned int door_delay = 0;
4661 unsigned int door_delay_value;
4664 if (door_state == DOOR_GET_STATE)
4665 return (door1 | door2);
4667 if (door_state & DOOR_SET_STATE)
4669 if (door_state & DOOR_ACTION_1)
4670 door1 = door_state & DOOR_ACTION_1;
4671 if (door_state & DOOR_ACTION_2)
4672 door2 = door_state & DOOR_ACTION_2;
4674 return (door1 | door2);
4677 if (!(door_state & DOOR_FORCE_REDRAW))
4679 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4680 door_state &= ~DOOR_OPEN_1;
4681 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4682 door_state &= ~DOOR_CLOSE_1;
4683 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4684 door_state &= ~DOOR_OPEN_2;
4685 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4686 door_state &= ~DOOR_CLOSE_2;
4689 if (global.autoplay_leveldir)
4691 door_state |= DOOR_NO_DELAY;
4692 door_state &= ~DOOR_CLOSE_ALL;
4695 if (game_status == GAME_MODE_EDITOR)
4696 door_state |= DOOR_NO_DELAY;
4698 if (door_state & DOOR_ACTION)
4700 boolean door_panel_drawn[NUM_DOORS];
4701 boolean panel_has_doors[NUM_DOORS];
4702 boolean door_part_skip[MAX_DOOR_PARTS];
4703 boolean door_part_done[MAX_DOOR_PARTS];
4704 boolean door_part_done_all;
4705 int num_steps[MAX_DOOR_PARTS];
4706 int max_move_delay = 0; // delay for complete animations of all doors
4707 int max_step_delay = 0; // delay (ms) between two animation frames
4708 int num_move_steps = 0; // number of animation steps for all doors
4709 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4710 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4711 int current_move_delay = 0;
4715 for (i = 0; i < NUM_DOORS; i++)
4716 panel_has_doors[i] = FALSE;
4718 for (i = 0; i < MAX_DOOR_PARTS; i++)
4720 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4721 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4722 int door_token = dpc->door_token;
4724 door_part_done[i] = FALSE;
4725 door_part_skip[i] = (!(door_state & door_token) ||
4729 for (i = 0; i < MAX_DOOR_PARTS; i++)
4731 int nr = door_part_order[i].nr;
4732 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4733 struct DoorPartPosInfo *pos = dpc->pos;
4734 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4735 int door_token = dpc->door_token;
4736 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4737 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4738 int step_xoffset = ABS(pos->step_xoffset);
4739 int step_yoffset = ABS(pos->step_yoffset);
4740 int step_delay = pos->step_delay;
4741 int current_door_state = door_state & door_token;
4742 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4743 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4744 boolean part_opening = (is_panel ? door_closing : door_opening);
4745 int start_step = (part_opening ? pos->start_step_opening :
4746 pos->start_step_closing);
4747 float move_xsize = (step_xoffset ? g->width : 0);
4748 float move_ysize = (step_yoffset ? g->height : 0);
4749 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4750 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4751 int move_steps = (move_xsteps && move_ysteps ?
4752 MIN(move_xsteps, move_ysteps) :
4753 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4754 int move_delay = move_steps * step_delay;
4756 if (door_part_skip[nr])
4759 max_move_delay = MAX(max_move_delay, move_delay);
4760 max_step_delay = (max_step_delay == 0 ? step_delay :
4761 euclid(max_step_delay, step_delay));
4762 num_steps[nr] = move_steps;
4766 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4768 panel_has_doors[door_index] = TRUE;
4772 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4774 num_move_steps = max_move_delay / max_step_delay;
4775 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4777 door_delay_value = max_step_delay;
4779 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4781 start = num_move_steps - 1;
4785 /* opening door sound has priority over simultaneously closing door */
4786 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4787 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4788 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4789 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4792 for (k = start; k < num_move_steps; k++)
4794 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4796 door_part_done_all = TRUE;
4798 for (i = 0; i < NUM_DOORS; i++)
4799 door_panel_drawn[i] = FALSE;
4801 for (i = 0; i < MAX_DOOR_PARTS; i++)
4803 int nr = door_part_order[i].nr;
4804 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4805 struct DoorPartPosInfo *pos = dpc->pos;
4806 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4807 int door_token = dpc->door_token;
4808 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4809 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4810 boolean is_panel_and_door_has_closed = FALSE;
4811 struct Rect *door_rect = &door_rect_list[door_index];
4812 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4814 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4815 int current_door_state = door_state & door_token;
4816 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4817 boolean door_closing = !door_opening;
4818 boolean part_opening = (is_panel ? door_closing : door_opening);
4819 boolean part_closing = !part_opening;
4820 int start_step = (part_opening ? pos->start_step_opening :
4821 pos->start_step_closing);
4822 int step_delay = pos->step_delay;
4823 int step_factor = step_delay / max_step_delay;
4824 int k1 = (step_factor ? k / step_factor + 1 : k);
4825 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4826 int kk = MAX(0, k2);
4829 int src_x, src_y, src_xx, src_yy;
4830 int dst_x, dst_y, dst_xx, dst_yy;
4833 if (door_part_skip[nr])
4836 if (!(door_state & door_token))
4844 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4845 int kk_door = MAX(0, k2_door);
4846 int sync_frame = kk_door * door_delay_value;
4847 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4849 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
4850 &g_src_x, &g_src_y);
4855 if (!door_panel_drawn[door_index])
4857 ClearRectangle(drawto, door_rect->x, door_rect->y,
4858 door_rect->width, door_rect->height);
4860 door_panel_drawn[door_index] = TRUE;
4863 // draw opening or closing door parts
4865 if (pos->step_xoffset < 0) // door part on right side
4868 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4871 if (dst_xx + width > door_rect->width)
4872 width = door_rect->width - dst_xx;
4874 else // door part on left side
4877 dst_xx = pos->x - kk * pos->step_xoffset;
4881 src_xx = ABS(dst_xx);
4885 width = g->width - src_xx;
4887 if (width > door_rect->width)
4888 width = door_rect->width;
4890 // printf("::: k == %d [%d] \n", k, start_step);
4893 if (pos->step_yoffset < 0) // door part on bottom side
4896 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4899 if (dst_yy + height > door_rect->height)
4900 height = door_rect->height - dst_yy;
4902 else // door part on top side
4905 dst_yy = pos->y - kk * pos->step_yoffset;
4909 src_yy = ABS(dst_yy);
4913 height = g->height - src_yy;
4916 src_x = g_src_x + src_xx;
4917 src_y = g_src_y + src_yy;
4919 dst_x = door_rect->x + dst_xx;
4920 dst_y = door_rect->y + dst_yy;
4922 is_panel_and_door_has_closed =
4925 panel_has_doors[door_index] &&
4926 k >= num_move_steps_doors_only - 1);
4928 if (width >= 0 && width <= g->width &&
4929 height >= 0 && height <= g->height &&
4930 !is_panel_and_door_has_closed)
4932 if (is_panel || !pos->draw_masked)
4933 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4936 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4940 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4942 if ((part_opening && (width < 0 || height < 0)) ||
4943 (part_closing && (width >= g->width && height >= g->height)))
4944 door_part_done[nr] = TRUE;
4946 // continue door part animations, but not panel after door has closed
4947 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4948 door_part_done_all = FALSE;
4951 if (!(door_state & DOOR_NO_DELAY))
4955 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4957 current_move_delay += max_step_delay;
4959 /* prevent OS (Windows) from complaining about program not responding */
4963 if (door_part_done_all)
4968 if (door_state & DOOR_ACTION_1)
4969 door1 = door_state & DOOR_ACTION_1;
4970 if (door_state & DOOR_ACTION_2)
4971 door2 = door_state & DOOR_ACTION_2;
4973 // draw masked border over door area
4974 DrawMaskedBorder(REDRAW_DOOR_1);
4975 DrawMaskedBorder(REDRAW_DOOR_2);
4977 return (door1 | door2);
4980 static boolean useSpecialEditorDoor()
4982 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4983 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4985 // do not draw special editor door if editor border defined or redefined
4986 if (graphic_info[graphic].bitmap != NULL || redefined)
4989 // do not draw special editor door if global border defined to be empty
4990 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4993 // do not draw special editor door if viewport definitions do not match
4997 EY + EYSIZE != VY + VYSIZE)
5003 void DrawSpecialEditorDoor()
5005 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5006 int top_border_width = gfx1->width;
5007 int top_border_height = gfx1->height;
5008 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5009 int ex = EX - outer_border;
5010 int ey = EY - outer_border;
5011 int vy = VY - outer_border;
5012 int exsize = EXSIZE + 2 * outer_border;
5014 if (!useSpecialEditorDoor())
5017 /* draw bigger level editor toolbox window */
5018 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5019 top_border_width, top_border_height, ex, ey - top_border_height);
5020 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5021 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5023 redraw_mask |= REDRAW_ALL;
5026 void UndrawSpecialEditorDoor()
5028 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5029 int top_border_width = gfx1->width;
5030 int top_border_height = gfx1->height;
5031 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5032 int ex = EX - outer_border;
5033 int ey = EY - outer_border;
5034 int ey_top = ey - top_border_height;
5035 int exsize = EXSIZE + 2 * outer_border;
5036 int eysize = EYSIZE + 2 * outer_border;
5038 if (!useSpecialEditorDoor())
5041 /* draw normal tape recorder window */
5042 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5044 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5045 ex, ey_top, top_border_width, top_border_height,
5047 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5048 ex, ey, exsize, eysize, ex, ey);
5052 // if screen background is set to "[NONE]", clear editor toolbox window
5053 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5054 ClearRectangle(drawto, ex, ey, exsize, eysize);
5057 redraw_mask |= REDRAW_ALL;
5061 /* ---------- new tool button stuff ---------------------------------------- */
5066 struct TextPosInfo *pos;
5069 } toolbutton_info[NUM_TOOL_BUTTONS] =
5072 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5073 TOOL_CTRL_ID_YES, "yes"
5076 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5077 TOOL_CTRL_ID_NO, "no"
5080 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5081 TOOL_CTRL_ID_CONFIRM, "confirm"
5084 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5085 TOOL_CTRL_ID_PLAYER_1, "player 1"
5088 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5089 TOOL_CTRL_ID_PLAYER_2, "player 2"
5092 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5093 TOOL_CTRL_ID_PLAYER_3, "player 3"
5096 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5097 TOOL_CTRL_ID_PLAYER_4, "player 4"
5101 void CreateToolButtons()
5105 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5107 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
5108 struct TextPosInfo *pos = toolbutton_info[i].pos;
5109 struct GadgetInfo *gi;
5110 Bitmap *deco_bitmap = None;
5111 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5112 unsigned int event_mask = GD_EVENT_RELEASED;
5115 int gd_x = gfx->src_x;
5116 int gd_y = gfx->src_y;
5117 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5118 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5121 if (global.use_envelope_request)
5122 setRequestPosition(&dx, &dy, TRUE);
5124 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5126 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5128 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5129 pos->size, &deco_bitmap, &deco_x, &deco_y);
5130 deco_xpos = (gfx->width - pos->size) / 2;
5131 deco_ypos = (gfx->height - pos->size) / 2;
5134 gi = CreateGadget(GDI_CUSTOM_ID, id,
5135 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5136 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
5137 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
5138 GDI_WIDTH, gfx->width,
5139 GDI_HEIGHT, gfx->height,
5140 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5141 GDI_STATE, GD_BUTTON_UNPRESSED,
5142 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5143 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5144 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5145 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5146 GDI_DECORATION_SIZE, pos->size, pos->size,
5147 GDI_DECORATION_SHIFTING, 1, 1,
5148 GDI_DIRECT_DRAW, FALSE,
5149 GDI_EVENT_MASK, event_mask,
5150 GDI_CALLBACK_ACTION, HandleToolButtons,
5154 Error(ERR_EXIT, "cannot create gadget");
5156 tool_gadget[id] = gi;
5160 void FreeToolButtons()
5164 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5165 FreeGadget(tool_gadget[i]);
5168 static void UnmapToolButtons()
5172 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5173 UnmapGadget(tool_gadget[i]);
5176 static void HandleToolButtons(struct GadgetInfo *gi)
5178 request_gadget_id = gi->custom_id;
5181 static struct Mapping_EM_to_RND_object
5184 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
5185 boolean is_backside; /* backside of moving element */
5191 em_object_mapping_list[] =
5194 Xblank, TRUE, FALSE,
5198 Yacid_splash_eB, FALSE, FALSE,
5199 EL_ACID_SPLASH_RIGHT, -1, -1
5202 Yacid_splash_wB, FALSE, FALSE,
5203 EL_ACID_SPLASH_LEFT, -1, -1
5206 #ifdef EM_ENGINE_BAD_ROLL
5208 Xstone_force_e, FALSE, FALSE,
5209 EL_ROCK, -1, MV_BIT_RIGHT
5212 Xstone_force_w, FALSE, FALSE,
5213 EL_ROCK, -1, MV_BIT_LEFT
5216 Xnut_force_e, FALSE, FALSE,
5217 EL_NUT, -1, MV_BIT_RIGHT
5220 Xnut_force_w, FALSE, FALSE,
5221 EL_NUT, -1, MV_BIT_LEFT
5224 Xspring_force_e, FALSE, FALSE,
5225 EL_SPRING, -1, MV_BIT_RIGHT
5228 Xspring_force_w, FALSE, FALSE,
5229 EL_SPRING, -1, MV_BIT_LEFT
5232 Xemerald_force_e, FALSE, FALSE,
5233 EL_EMERALD, -1, MV_BIT_RIGHT
5236 Xemerald_force_w, FALSE, FALSE,
5237 EL_EMERALD, -1, MV_BIT_LEFT
5240 Xdiamond_force_e, FALSE, FALSE,
5241 EL_DIAMOND, -1, MV_BIT_RIGHT
5244 Xdiamond_force_w, FALSE, FALSE,
5245 EL_DIAMOND, -1, MV_BIT_LEFT
5248 Xbomb_force_e, FALSE, FALSE,
5249 EL_BOMB, -1, MV_BIT_RIGHT
5252 Xbomb_force_w, FALSE, FALSE,
5253 EL_BOMB, -1, MV_BIT_LEFT
5255 #endif /* EM_ENGINE_BAD_ROLL */
5258 Xstone, TRUE, FALSE,
5262 Xstone_pause, FALSE, FALSE,
5266 Xstone_fall, FALSE, FALSE,
5270 Ystone_s, FALSE, FALSE,
5271 EL_ROCK, ACTION_FALLING, -1
5274 Ystone_sB, FALSE, TRUE,
5275 EL_ROCK, ACTION_FALLING, -1
5278 Ystone_e, FALSE, FALSE,
5279 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5282 Ystone_eB, FALSE, TRUE,
5283 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5286 Ystone_w, FALSE, FALSE,
5287 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5290 Ystone_wB, FALSE, TRUE,
5291 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5298 Xnut_pause, FALSE, FALSE,
5302 Xnut_fall, FALSE, FALSE,
5306 Ynut_s, FALSE, FALSE,
5307 EL_NUT, ACTION_FALLING, -1
5310 Ynut_sB, FALSE, TRUE,
5311 EL_NUT, ACTION_FALLING, -1
5314 Ynut_e, FALSE, FALSE,
5315 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5318 Ynut_eB, FALSE, TRUE,
5319 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5322 Ynut_w, FALSE, FALSE,
5323 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5326 Ynut_wB, FALSE, TRUE,
5327 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5330 Xbug_n, TRUE, FALSE,
5334 Xbug_e, TRUE, FALSE,
5335 EL_BUG_RIGHT, -1, -1
5338 Xbug_s, TRUE, FALSE,
5342 Xbug_w, TRUE, FALSE,
5346 Xbug_gon, FALSE, FALSE,
5350 Xbug_goe, FALSE, FALSE,
5351 EL_BUG_RIGHT, -1, -1
5354 Xbug_gos, FALSE, FALSE,
5358 Xbug_gow, FALSE, FALSE,
5362 Ybug_n, FALSE, FALSE,
5363 EL_BUG, ACTION_MOVING, MV_BIT_UP
5366 Ybug_nB, FALSE, TRUE,
5367 EL_BUG, ACTION_MOVING, MV_BIT_UP
5370 Ybug_e, FALSE, FALSE,
5371 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5374 Ybug_eB, FALSE, TRUE,
5375 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5378 Ybug_s, FALSE, FALSE,
5379 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5382 Ybug_sB, FALSE, TRUE,
5383 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5386 Ybug_w, FALSE, FALSE,
5387 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5390 Ybug_wB, FALSE, TRUE,
5391 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5394 Ybug_w_n, FALSE, FALSE,
5395 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5398 Ybug_n_e, FALSE, FALSE,
5399 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5402 Ybug_e_s, FALSE, FALSE,
5403 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5406 Ybug_s_w, FALSE, FALSE,
5407 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5410 Ybug_e_n, FALSE, FALSE,
5411 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5414 Ybug_s_e, FALSE, FALSE,
5415 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5418 Ybug_w_s, FALSE, FALSE,
5419 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5422 Ybug_n_w, FALSE, FALSE,
5423 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5426 Ybug_stone, FALSE, FALSE,
5427 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5430 Ybug_spring, FALSE, FALSE,
5431 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5434 Xtank_n, TRUE, FALSE,
5435 EL_SPACESHIP_UP, -1, -1
5438 Xtank_e, TRUE, FALSE,
5439 EL_SPACESHIP_RIGHT, -1, -1
5442 Xtank_s, TRUE, FALSE,
5443 EL_SPACESHIP_DOWN, -1, -1
5446 Xtank_w, TRUE, FALSE,
5447 EL_SPACESHIP_LEFT, -1, -1
5450 Xtank_gon, FALSE, FALSE,
5451 EL_SPACESHIP_UP, -1, -1
5454 Xtank_goe, FALSE, FALSE,
5455 EL_SPACESHIP_RIGHT, -1, -1
5458 Xtank_gos, FALSE, FALSE,
5459 EL_SPACESHIP_DOWN, -1, -1
5462 Xtank_gow, FALSE, FALSE,
5463 EL_SPACESHIP_LEFT, -1, -1
5466 Ytank_n, FALSE, FALSE,
5467 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5470 Ytank_nB, FALSE, TRUE,
5471 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5474 Ytank_e, FALSE, FALSE,
5475 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5478 Ytank_eB, FALSE, TRUE,
5479 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5482 Ytank_s, FALSE, FALSE,
5483 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5486 Ytank_sB, FALSE, TRUE,
5487 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5490 Ytank_w, FALSE, FALSE,
5491 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5494 Ytank_wB, FALSE, TRUE,
5495 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5498 Ytank_w_n, FALSE, FALSE,
5499 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5502 Ytank_n_e, FALSE, FALSE,
5503 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5506 Ytank_e_s, FALSE, FALSE,
5507 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5510 Ytank_s_w, FALSE, FALSE,
5511 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5514 Ytank_e_n, FALSE, FALSE,
5515 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5518 Ytank_s_e, FALSE, FALSE,
5519 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5522 Ytank_w_s, FALSE, FALSE,
5523 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5526 Ytank_n_w, FALSE, FALSE,
5527 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5530 Ytank_stone, FALSE, FALSE,
5531 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5534 Ytank_spring, FALSE, FALSE,
5535 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5538 Xandroid, TRUE, FALSE,
5539 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5542 Xandroid_1_n, FALSE, FALSE,
5543 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5546 Xandroid_2_n, FALSE, FALSE,
5547 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5550 Xandroid_1_e, FALSE, FALSE,
5551 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5554 Xandroid_2_e, FALSE, FALSE,
5555 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5558 Xandroid_1_w, FALSE, FALSE,
5559 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5562 Xandroid_2_w, FALSE, FALSE,
5563 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5566 Xandroid_1_s, FALSE, FALSE,
5567 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5570 Xandroid_2_s, FALSE, FALSE,
5571 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5574 Yandroid_n, FALSE, FALSE,
5575 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5578 Yandroid_nB, FALSE, TRUE,
5579 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5582 Yandroid_ne, FALSE, FALSE,
5583 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5586 Yandroid_neB, FALSE, TRUE,
5587 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5590 Yandroid_e, FALSE, FALSE,
5591 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5594 Yandroid_eB, FALSE, TRUE,
5595 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5598 Yandroid_se, FALSE, FALSE,
5599 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5602 Yandroid_seB, FALSE, TRUE,
5603 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5606 Yandroid_s, FALSE, FALSE,
5607 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5610 Yandroid_sB, FALSE, TRUE,
5611 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5614 Yandroid_sw, FALSE, FALSE,
5615 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5618 Yandroid_swB, FALSE, TRUE,
5619 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5622 Yandroid_w, FALSE, FALSE,
5623 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5626 Yandroid_wB, FALSE, TRUE,
5627 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5630 Yandroid_nw, FALSE, FALSE,
5631 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5634 Yandroid_nwB, FALSE, TRUE,
5635 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5638 Xspring, TRUE, FALSE,
5642 Xspring_pause, FALSE, FALSE,
5646 Xspring_e, FALSE, FALSE,
5650 Xspring_w, FALSE, FALSE,
5654 Xspring_fall, FALSE, FALSE,
5658 Yspring_s, FALSE, FALSE,
5659 EL_SPRING, ACTION_FALLING, -1
5662 Yspring_sB, FALSE, TRUE,
5663 EL_SPRING, ACTION_FALLING, -1
5666 Yspring_e, FALSE, FALSE,
5667 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5670 Yspring_eB, FALSE, TRUE,
5671 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5674 Yspring_w, FALSE, FALSE,
5675 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5678 Yspring_wB, FALSE, TRUE,
5679 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5682 Yspring_kill_e, FALSE, FALSE,
5683 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5686 Yspring_kill_eB, FALSE, TRUE,
5687 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5690 Yspring_kill_w, FALSE, FALSE,
5691 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5694 Yspring_kill_wB, FALSE, TRUE,
5695 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5698 Xeater_n, TRUE, FALSE,
5699 EL_YAMYAM_UP, -1, -1
5702 Xeater_e, TRUE, FALSE,
5703 EL_YAMYAM_RIGHT, -1, -1
5706 Xeater_w, TRUE, FALSE,
5707 EL_YAMYAM_LEFT, -1, -1
5710 Xeater_s, TRUE, FALSE,
5711 EL_YAMYAM_DOWN, -1, -1
5714 Yeater_n, FALSE, FALSE,
5715 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5718 Yeater_nB, FALSE, TRUE,
5719 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5722 Yeater_e, FALSE, FALSE,
5723 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5726 Yeater_eB, FALSE, TRUE,
5727 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5730 Yeater_s, FALSE, FALSE,
5731 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5734 Yeater_sB, FALSE, TRUE,
5735 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5738 Yeater_w, FALSE, FALSE,
5739 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5742 Yeater_wB, FALSE, TRUE,
5743 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5746 Yeater_stone, FALSE, FALSE,
5747 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5750 Yeater_spring, FALSE, FALSE,
5751 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5754 Xalien, TRUE, FALSE,
5758 Xalien_pause, FALSE, FALSE,
5762 Yalien_n, FALSE, FALSE,
5763 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5766 Yalien_nB, FALSE, TRUE,
5767 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5770 Yalien_e, FALSE, FALSE,
5771 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5774 Yalien_eB, FALSE, TRUE,
5775 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5778 Yalien_s, FALSE, FALSE,
5779 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5782 Yalien_sB, FALSE, TRUE,
5783 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5786 Yalien_w, FALSE, FALSE,
5787 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5790 Yalien_wB, FALSE, TRUE,
5791 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5794 Yalien_stone, FALSE, FALSE,
5795 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5798 Yalien_spring, FALSE, FALSE,
5799 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5802 Xemerald, TRUE, FALSE,
5806 Xemerald_pause, FALSE, FALSE,
5810 Xemerald_fall, FALSE, FALSE,
5814 Xemerald_shine, FALSE, FALSE,
5815 EL_EMERALD, ACTION_TWINKLING, -1
5818 Yemerald_s, FALSE, FALSE,
5819 EL_EMERALD, ACTION_FALLING, -1
5822 Yemerald_sB, FALSE, TRUE,
5823 EL_EMERALD, ACTION_FALLING, -1
5826 Yemerald_e, FALSE, FALSE,
5827 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5830 Yemerald_eB, FALSE, TRUE,
5831 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5834 Yemerald_w, FALSE, FALSE,
5835 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5838 Yemerald_wB, FALSE, TRUE,
5839 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5842 Yemerald_eat, FALSE, FALSE,
5843 EL_EMERALD, ACTION_COLLECTING, -1
5846 Yemerald_stone, FALSE, FALSE,
5847 EL_NUT, ACTION_BREAKING, -1
5850 Xdiamond, TRUE, FALSE,
5854 Xdiamond_pause, FALSE, FALSE,
5858 Xdiamond_fall, FALSE, FALSE,
5862 Xdiamond_shine, FALSE, FALSE,
5863 EL_DIAMOND, ACTION_TWINKLING, -1
5866 Ydiamond_s, FALSE, FALSE,
5867 EL_DIAMOND, ACTION_FALLING, -1
5870 Ydiamond_sB, FALSE, TRUE,
5871 EL_DIAMOND, ACTION_FALLING, -1
5874 Ydiamond_e, FALSE, FALSE,
5875 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5878 Ydiamond_eB, FALSE, TRUE,
5879 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5882 Ydiamond_w, FALSE, FALSE,
5883 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5886 Ydiamond_wB, FALSE, TRUE,
5887 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5890 Ydiamond_eat, FALSE, FALSE,
5891 EL_DIAMOND, ACTION_COLLECTING, -1
5894 Ydiamond_stone, FALSE, FALSE,
5895 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5898 Xdrip_fall, TRUE, FALSE,
5899 EL_AMOEBA_DROP, -1, -1
5902 Xdrip_stretch, FALSE, FALSE,
5903 EL_AMOEBA_DROP, ACTION_FALLING, -1
5906 Xdrip_stretchB, FALSE, TRUE,
5907 EL_AMOEBA_DROP, ACTION_FALLING, -1
5910 Xdrip_eat, FALSE, FALSE,
5911 EL_AMOEBA_DROP, ACTION_GROWING, -1
5914 Ydrip_s1, FALSE, FALSE,
5915 EL_AMOEBA_DROP, ACTION_FALLING, -1
5918 Ydrip_s1B, FALSE, TRUE,
5919 EL_AMOEBA_DROP, ACTION_FALLING, -1
5922 Ydrip_s2, FALSE, FALSE,
5923 EL_AMOEBA_DROP, ACTION_FALLING, -1
5926 Ydrip_s2B, FALSE, TRUE,
5927 EL_AMOEBA_DROP, ACTION_FALLING, -1
5934 Xbomb_pause, FALSE, FALSE,
5938 Xbomb_fall, FALSE, FALSE,
5942 Ybomb_s, FALSE, FALSE,
5943 EL_BOMB, ACTION_FALLING, -1
5946 Ybomb_sB, FALSE, TRUE,
5947 EL_BOMB, ACTION_FALLING, -1
5950 Ybomb_e, FALSE, FALSE,
5951 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5954 Ybomb_eB, FALSE, TRUE,
5955 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5958 Ybomb_w, FALSE, FALSE,
5959 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5962 Ybomb_wB, FALSE, TRUE,
5963 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5966 Ybomb_eat, FALSE, FALSE,
5967 EL_BOMB, ACTION_ACTIVATING, -1
5970 Xballoon, TRUE, FALSE,
5974 Yballoon_n, FALSE, FALSE,
5975 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5978 Yballoon_nB, FALSE, TRUE,
5979 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5982 Yballoon_e, FALSE, FALSE,
5983 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5986 Yballoon_eB, FALSE, TRUE,
5987 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5990 Yballoon_s, FALSE, FALSE,
5991 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5994 Yballoon_sB, FALSE, TRUE,
5995 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5998 Yballoon_w, FALSE, FALSE,
5999 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6002 Yballoon_wB, FALSE, TRUE,
6003 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6006 Xgrass, TRUE, FALSE,
6007 EL_EMC_GRASS, -1, -1
6010 Ygrass_nB, FALSE, FALSE,
6011 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6014 Ygrass_eB, FALSE, FALSE,
6015 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6018 Ygrass_sB, FALSE, FALSE,
6019 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6022 Ygrass_wB, FALSE, FALSE,
6023 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6030 Ydirt_nB, FALSE, FALSE,
6031 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6034 Ydirt_eB, FALSE, FALSE,
6035 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6038 Ydirt_sB, FALSE, FALSE,
6039 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6042 Ydirt_wB, FALSE, FALSE,
6043 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6046 Xacid_ne, TRUE, FALSE,
6047 EL_ACID_POOL_TOPRIGHT, -1, -1
6050 Xacid_se, TRUE, FALSE,
6051 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6054 Xacid_s, TRUE, FALSE,
6055 EL_ACID_POOL_BOTTOM, -1, -1
6058 Xacid_sw, TRUE, FALSE,
6059 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6062 Xacid_nw, TRUE, FALSE,
6063 EL_ACID_POOL_TOPLEFT, -1, -1
6066 Xacid_1, TRUE, FALSE,
6070 Xacid_2, FALSE, FALSE,
6074 Xacid_3, FALSE, FALSE,
6078 Xacid_4, FALSE, FALSE,
6082 Xacid_5, FALSE, FALSE,
6086 Xacid_6, FALSE, FALSE,
6090 Xacid_7, FALSE, FALSE,
6094 Xacid_8, FALSE, FALSE,
6098 Xball_1, TRUE, FALSE,
6099 EL_EMC_MAGIC_BALL, -1, -1
6102 Xball_1B, FALSE, FALSE,
6103 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6106 Xball_2, FALSE, FALSE,
6107 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6110 Xball_2B, FALSE, FALSE,
6111 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6114 Yball_eat, FALSE, FALSE,
6115 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6118 Ykey_1_eat, FALSE, FALSE,
6119 EL_EM_KEY_1, ACTION_COLLECTING, -1
6122 Ykey_2_eat, FALSE, FALSE,
6123 EL_EM_KEY_2, ACTION_COLLECTING, -1
6126 Ykey_3_eat, FALSE, FALSE,
6127 EL_EM_KEY_3, ACTION_COLLECTING, -1
6130 Ykey_4_eat, FALSE, FALSE,
6131 EL_EM_KEY_4, ACTION_COLLECTING, -1
6134 Ykey_5_eat, FALSE, FALSE,
6135 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6138 Ykey_6_eat, FALSE, FALSE,
6139 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6142 Ykey_7_eat, FALSE, FALSE,
6143 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6146 Ykey_8_eat, FALSE, FALSE,
6147 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6150 Ylenses_eat, FALSE, FALSE,
6151 EL_EMC_LENSES, ACTION_COLLECTING, -1
6154 Ymagnify_eat, FALSE, FALSE,
6155 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6158 Ygrass_eat, FALSE, FALSE,
6159 EL_EMC_GRASS, ACTION_SNAPPING, -1
6162 Ydirt_eat, FALSE, FALSE,
6163 EL_SAND, ACTION_SNAPPING, -1
6166 Xgrow_ns, TRUE, FALSE,
6167 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6170 Ygrow_ns_eat, FALSE, FALSE,
6171 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6174 Xgrow_ew, TRUE, FALSE,
6175 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6178 Ygrow_ew_eat, FALSE, FALSE,
6179 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6182 Xwonderwall, TRUE, FALSE,
6183 EL_MAGIC_WALL, -1, -1
6186 XwonderwallB, FALSE, FALSE,
6187 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6190 Xamoeba_1, TRUE, FALSE,
6191 EL_AMOEBA_DRY, ACTION_OTHER, -1
6194 Xamoeba_2, FALSE, FALSE,
6195 EL_AMOEBA_DRY, ACTION_OTHER, -1
6198 Xamoeba_3, FALSE, FALSE,
6199 EL_AMOEBA_DRY, ACTION_OTHER, -1
6202 Xamoeba_4, FALSE, FALSE,
6203 EL_AMOEBA_DRY, ACTION_OTHER, -1
6206 Xamoeba_5, TRUE, FALSE,
6207 EL_AMOEBA_WET, ACTION_OTHER, -1
6210 Xamoeba_6, FALSE, FALSE,
6211 EL_AMOEBA_WET, ACTION_OTHER, -1
6214 Xamoeba_7, FALSE, FALSE,
6215 EL_AMOEBA_WET, ACTION_OTHER, -1
6218 Xamoeba_8, FALSE, FALSE,
6219 EL_AMOEBA_WET, ACTION_OTHER, -1
6222 Xdoor_1, TRUE, FALSE,
6223 EL_EM_GATE_1, -1, -1
6226 Xdoor_2, TRUE, FALSE,
6227 EL_EM_GATE_2, -1, -1
6230 Xdoor_3, TRUE, FALSE,
6231 EL_EM_GATE_3, -1, -1
6234 Xdoor_4, TRUE, FALSE,
6235 EL_EM_GATE_4, -1, -1
6238 Xdoor_5, TRUE, FALSE,
6239 EL_EMC_GATE_5, -1, -1
6242 Xdoor_6, TRUE, FALSE,
6243 EL_EMC_GATE_6, -1, -1
6246 Xdoor_7, TRUE, FALSE,
6247 EL_EMC_GATE_7, -1, -1
6250 Xdoor_8, TRUE, FALSE,
6251 EL_EMC_GATE_8, -1, -1
6254 Xkey_1, TRUE, FALSE,
6258 Xkey_2, TRUE, FALSE,
6262 Xkey_3, TRUE, FALSE,
6266 Xkey_4, TRUE, FALSE,
6270 Xkey_5, TRUE, FALSE,
6271 EL_EMC_KEY_5, -1, -1
6274 Xkey_6, TRUE, FALSE,
6275 EL_EMC_KEY_6, -1, -1
6278 Xkey_7, TRUE, FALSE,
6279 EL_EMC_KEY_7, -1, -1
6282 Xkey_8, TRUE, FALSE,
6283 EL_EMC_KEY_8, -1, -1
6286 Xwind_n, TRUE, FALSE,
6287 EL_BALLOON_SWITCH_UP, -1, -1
6290 Xwind_e, TRUE, FALSE,
6291 EL_BALLOON_SWITCH_RIGHT, -1, -1
6294 Xwind_s, TRUE, FALSE,
6295 EL_BALLOON_SWITCH_DOWN, -1, -1
6298 Xwind_w, TRUE, FALSE,
6299 EL_BALLOON_SWITCH_LEFT, -1, -1
6302 Xwind_nesw, TRUE, FALSE,
6303 EL_BALLOON_SWITCH_ANY, -1, -1
6306 Xwind_stop, TRUE, FALSE,
6307 EL_BALLOON_SWITCH_NONE, -1, -1
6311 EL_EM_EXIT_CLOSED, -1, -1
6314 Xexit_1, TRUE, FALSE,
6315 EL_EM_EXIT_OPEN, -1, -1
6318 Xexit_2, FALSE, FALSE,
6319 EL_EM_EXIT_OPEN, -1, -1
6322 Xexit_3, FALSE, FALSE,
6323 EL_EM_EXIT_OPEN, -1, -1
6326 Xdynamite, TRUE, FALSE,
6327 EL_EM_DYNAMITE, -1, -1
6330 Ydynamite_eat, FALSE, FALSE,
6331 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6334 Xdynamite_1, TRUE, FALSE,
6335 EL_EM_DYNAMITE_ACTIVE, -1, -1
6338 Xdynamite_2, FALSE, FALSE,
6339 EL_EM_DYNAMITE_ACTIVE, -1, -1
6342 Xdynamite_3, FALSE, FALSE,
6343 EL_EM_DYNAMITE_ACTIVE, -1, -1
6346 Xdynamite_4, FALSE, FALSE,
6347 EL_EM_DYNAMITE_ACTIVE, -1, -1
6350 Xbumper, TRUE, FALSE,
6351 EL_EMC_SPRING_BUMPER, -1, -1
6354 XbumperB, FALSE, FALSE,
6355 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6358 Xwheel, TRUE, FALSE,
6359 EL_ROBOT_WHEEL, -1, -1
6362 XwheelB, FALSE, FALSE,
6363 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6366 Xswitch, TRUE, FALSE,
6367 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6370 XswitchB, FALSE, FALSE,
6371 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6375 EL_QUICKSAND_EMPTY, -1, -1
6378 Xsand_stone, TRUE, FALSE,
6379 EL_QUICKSAND_FULL, -1, -1
6382 Xsand_stonein_1, FALSE, TRUE,
6383 EL_ROCK, ACTION_FILLING, -1
6386 Xsand_stonein_2, FALSE, TRUE,
6387 EL_ROCK, ACTION_FILLING, -1
6390 Xsand_stonein_3, FALSE, TRUE,
6391 EL_ROCK, ACTION_FILLING, -1
6394 Xsand_stonein_4, FALSE, TRUE,
6395 EL_ROCK, ACTION_FILLING, -1
6398 Xsand_stonesand_1, FALSE, FALSE,
6399 EL_QUICKSAND_EMPTYING, -1, -1
6402 Xsand_stonesand_2, FALSE, FALSE,
6403 EL_QUICKSAND_EMPTYING, -1, -1
6406 Xsand_stonesand_3, FALSE, FALSE,
6407 EL_QUICKSAND_EMPTYING, -1, -1
6410 Xsand_stonesand_4, FALSE, FALSE,
6411 EL_QUICKSAND_EMPTYING, -1, -1
6414 Xsand_stonesand_quickout_1, FALSE, FALSE,
6415 EL_QUICKSAND_EMPTYING, -1, -1
6418 Xsand_stonesand_quickout_2, FALSE, FALSE,
6419 EL_QUICKSAND_EMPTYING, -1, -1
6422 Xsand_stoneout_1, FALSE, FALSE,
6423 EL_ROCK, ACTION_EMPTYING, -1
6426 Xsand_stoneout_2, FALSE, FALSE,
6427 EL_ROCK, ACTION_EMPTYING, -1
6430 Xsand_sandstone_1, FALSE, FALSE,
6431 EL_QUICKSAND_FILLING, -1, -1
6434 Xsand_sandstone_2, FALSE, FALSE,
6435 EL_QUICKSAND_FILLING, -1, -1
6438 Xsand_sandstone_3, FALSE, FALSE,
6439 EL_QUICKSAND_FILLING, -1, -1
6442 Xsand_sandstone_4, FALSE, FALSE,
6443 EL_QUICKSAND_FILLING, -1, -1
6446 Xplant, TRUE, FALSE,
6447 EL_EMC_PLANT, -1, -1
6450 Yplant, FALSE, FALSE,
6451 EL_EMC_PLANT, -1, -1
6454 Xlenses, TRUE, FALSE,
6455 EL_EMC_LENSES, -1, -1
6458 Xmagnify, TRUE, FALSE,
6459 EL_EMC_MAGNIFIER, -1, -1
6462 Xdripper, TRUE, FALSE,
6463 EL_EMC_DRIPPER, -1, -1
6466 XdripperB, FALSE, FALSE,
6467 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6470 Xfake_blank, TRUE, FALSE,
6471 EL_INVISIBLE_WALL, -1, -1
6474 Xfake_blankB, FALSE, FALSE,
6475 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6478 Xfake_grass, TRUE, FALSE,
6479 EL_EMC_FAKE_GRASS, -1, -1
6482 Xfake_grassB, FALSE, FALSE,
6483 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6486 Xfake_door_1, TRUE, FALSE,
6487 EL_EM_GATE_1_GRAY, -1, -1
6490 Xfake_door_2, TRUE, FALSE,
6491 EL_EM_GATE_2_GRAY, -1, -1
6494 Xfake_door_3, TRUE, FALSE,
6495 EL_EM_GATE_3_GRAY, -1, -1
6498 Xfake_door_4, TRUE, FALSE,
6499 EL_EM_GATE_4_GRAY, -1, -1
6502 Xfake_door_5, TRUE, FALSE,
6503 EL_EMC_GATE_5_GRAY, -1, -1
6506 Xfake_door_6, TRUE, FALSE,
6507 EL_EMC_GATE_6_GRAY, -1, -1
6510 Xfake_door_7, TRUE, FALSE,
6511 EL_EMC_GATE_7_GRAY, -1, -1
6514 Xfake_door_8, TRUE, FALSE,
6515 EL_EMC_GATE_8_GRAY, -1, -1
6518 Xfake_acid_1, TRUE, FALSE,
6519 EL_EMC_FAKE_ACID, -1, -1
6522 Xfake_acid_2, FALSE, FALSE,
6523 EL_EMC_FAKE_ACID, -1, -1
6526 Xfake_acid_3, FALSE, FALSE,
6527 EL_EMC_FAKE_ACID, -1, -1
6530 Xfake_acid_4, FALSE, FALSE,
6531 EL_EMC_FAKE_ACID, -1, -1
6534 Xfake_acid_5, FALSE, FALSE,
6535 EL_EMC_FAKE_ACID, -1, -1
6538 Xfake_acid_6, FALSE, FALSE,
6539 EL_EMC_FAKE_ACID, -1, -1
6542 Xfake_acid_7, FALSE, FALSE,
6543 EL_EMC_FAKE_ACID, -1, -1
6546 Xfake_acid_8, FALSE, FALSE,
6547 EL_EMC_FAKE_ACID, -1, -1
6550 Xsteel_1, TRUE, FALSE,
6551 EL_STEELWALL, -1, -1
6554 Xsteel_2, TRUE, FALSE,
6555 EL_EMC_STEELWALL_2, -1, -1
6558 Xsteel_3, TRUE, FALSE,
6559 EL_EMC_STEELWALL_3, -1, -1
6562 Xsteel_4, TRUE, FALSE,
6563 EL_EMC_STEELWALL_4, -1, -1
6566 Xwall_1, TRUE, FALSE,
6570 Xwall_2, TRUE, FALSE,
6571 EL_EMC_WALL_14, -1, -1
6574 Xwall_3, TRUE, FALSE,
6575 EL_EMC_WALL_15, -1, -1
6578 Xwall_4, TRUE, FALSE,
6579 EL_EMC_WALL_16, -1, -1
6582 Xround_wall_1, TRUE, FALSE,
6583 EL_WALL_SLIPPERY, -1, -1
6586 Xround_wall_2, TRUE, FALSE,
6587 EL_EMC_WALL_SLIPPERY_2, -1, -1
6590 Xround_wall_3, TRUE, FALSE,
6591 EL_EMC_WALL_SLIPPERY_3, -1, -1
6594 Xround_wall_4, TRUE, FALSE,
6595 EL_EMC_WALL_SLIPPERY_4, -1, -1
6598 Xdecor_1, TRUE, FALSE,
6599 EL_EMC_WALL_8, -1, -1
6602 Xdecor_2, TRUE, FALSE,
6603 EL_EMC_WALL_6, -1, -1
6606 Xdecor_3, TRUE, FALSE,
6607 EL_EMC_WALL_4, -1, -1
6610 Xdecor_4, TRUE, FALSE,
6611 EL_EMC_WALL_7, -1, -1
6614 Xdecor_5, TRUE, FALSE,
6615 EL_EMC_WALL_5, -1, -1
6618 Xdecor_6, TRUE, FALSE,
6619 EL_EMC_WALL_9, -1, -1
6622 Xdecor_7, TRUE, FALSE,
6623 EL_EMC_WALL_10, -1, -1
6626 Xdecor_8, TRUE, FALSE,
6627 EL_EMC_WALL_1, -1, -1
6630 Xdecor_9, TRUE, FALSE,
6631 EL_EMC_WALL_2, -1, -1
6634 Xdecor_10, TRUE, FALSE,
6635 EL_EMC_WALL_3, -1, -1
6638 Xdecor_11, TRUE, FALSE,
6639 EL_EMC_WALL_11, -1, -1
6642 Xdecor_12, TRUE, FALSE,
6643 EL_EMC_WALL_12, -1, -1
6646 Xalpha_0, TRUE, FALSE,
6647 EL_CHAR('0'), -1, -1
6650 Xalpha_1, TRUE, FALSE,
6651 EL_CHAR('1'), -1, -1
6654 Xalpha_2, TRUE, FALSE,
6655 EL_CHAR('2'), -1, -1
6658 Xalpha_3, TRUE, FALSE,
6659 EL_CHAR('3'), -1, -1
6662 Xalpha_4, TRUE, FALSE,
6663 EL_CHAR('4'), -1, -1
6666 Xalpha_5, TRUE, FALSE,
6667 EL_CHAR('5'), -1, -1
6670 Xalpha_6, TRUE, FALSE,
6671 EL_CHAR('6'), -1, -1
6674 Xalpha_7, TRUE, FALSE,
6675 EL_CHAR('7'), -1, -1
6678 Xalpha_8, TRUE, FALSE,
6679 EL_CHAR('8'), -1, -1
6682 Xalpha_9, TRUE, FALSE,
6683 EL_CHAR('9'), -1, -1
6686 Xalpha_excla, TRUE, FALSE,
6687 EL_CHAR('!'), -1, -1
6690 Xalpha_quote, TRUE, FALSE,
6691 EL_CHAR('"'), -1, -1
6694 Xalpha_comma, TRUE, FALSE,
6695 EL_CHAR(','), -1, -1
6698 Xalpha_minus, TRUE, FALSE,
6699 EL_CHAR('-'), -1, -1
6702 Xalpha_perio, TRUE, FALSE,
6703 EL_CHAR('.'), -1, -1
6706 Xalpha_colon, TRUE, FALSE,
6707 EL_CHAR(':'), -1, -1
6710 Xalpha_quest, TRUE, FALSE,
6711 EL_CHAR('?'), -1, -1
6714 Xalpha_a, TRUE, FALSE,
6715 EL_CHAR('A'), -1, -1
6718 Xalpha_b, TRUE, FALSE,
6719 EL_CHAR('B'), -1, -1
6722 Xalpha_c, TRUE, FALSE,
6723 EL_CHAR('C'), -1, -1
6726 Xalpha_d, TRUE, FALSE,
6727 EL_CHAR('D'), -1, -1
6730 Xalpha_e, TRUE, FALSE,
6731 EL_CHAR('E'), -1, -1
6734 Xalpha_f, TRUE, FALSE,
6735 EL_CHAR('F'), -1, -1
6738 Xalpha_g, TRUE, FALSE,
6739 EL_CHAR('G'), -1, -1
6742 Xalpha_h, TRUE, FALSE,
6743 EL_CHAR('H'), -1, -1
6746 Xalpha_i, TRUE, FALSE,
6747 EL_CHAR('I'), -1, -1
6750 Xalpha_j, TRUE, FALSE,
6751 EL_CHAR('J'), -1, -1
6754 Xalpha_k, TRUE, FALSE,
6755 EL_CHAR('K'), -1, -1
6758 Xalpha_l, TRUE, FALSE,
6759 EL_CHAR('L'), -1, -1
6762 Xalpha_m, TRUE, FALSE,
6763 EL_CHAR('M'), -1, -1
6766 Xalpha_n, TRUE, FALSE,
6767 EL_CHAR('N'), -1, -1
6770 Xalpha_o, TRUE, FALSE,
6771 EL_CHAR('O'), -1, -1
6774 Xalpha_p, TRUE, FALSE,
6775 EL_CHAR('P'), -1, -1
6778 Xalpha_q, TRUE, FALSE,
6779 EL_CHAR('Q'), -1, -1
6782 Xalpha_r, TRUE, FALSE,
6783 EL_CHAR('R'), -1, -1
6786 Xalpha_s, TRUE, FALSE,
6787 EL_CHAR('S'), -1, -1
6790 Xalpha_t, TRUE, FALSE,
6791 EL_CHAR('T'), -1, -1
6794 Xalpha_u, TRUE, FALSE,
6795 EL_CHAR('U'), -1, -1
6798 Xalpha_v, TRUE, FALSE,
6799 EL_CHAR('V'), -1, -1
6802 Xalpha_w, TRUE, FALSE,
6803 EL_CHAR('W'), -1, -1
6806 Xalpha_x, TRUE, FALSE,
6807 EL_CHAR('X'), -1, -1
6810 Xalpha_y, TRUE, FALSE,
6811 EL_CHAR('Y'), -1, -1
6814 Xalpha_z, TRUE, FALSE,
6815 EL_CHAR('Z'), -1, -1
6818 Xalpha_arrow_e, TRUE, FALSE,
6819 EL_CHAR('>'), -1, -1
6822 Xalpha_arrow_w, TRUE, FALSE,
6823 EL_CHAR('<'), -1, -1
6826 Xalpha_copyr, TRUE, FALSE,
6827 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6831 Xboom_bug, FALSE, FALSE,
6832 EL_BUG, ACTION_EXPLODING, -1
6835 Xboom_bomb, FALSE, FALSE,
6836 EL_BOMB, ACTION_EXPLODING, -1
6839 Xboom_android, FALSE, FALSE,
6840 EL_EMC_ANDROID, ACTION_OTHER, -1
6843 Xboom_1, FALSE, FALSE,
6844 EL_DEFAULT, ACTION_EXPLODING, -1
6847 Xboom_2, FALSE, FALSE,
6848 EL_DEFAULT, ACTION_EXPLODING, -1
6851 Znormal, FALSE, FALSE,
6855 Zdynamite, FALSE, FALSE,
6859 Zplayer, FALSE, FALSE,
6863 ZBORDER, FALSE, FALSE,
6873 static struct Mapping_EM_to_RND_player
6882 em_player_mapping_list[] =
6886 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6890 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6894 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6898 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6902 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6906 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6910 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6914 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6918 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6922 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6926 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6930 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6934 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6938 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6942 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6946 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6950 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6954 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6958 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6962 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6966 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6970 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6974 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6978 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6982 EL_PLAYER_1, ACTION_DEFAULT, -1,
6986 EL_PLAYER_2, ACTION_DEFAULT, -1,
6990 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6994 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6998 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7002 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7006 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7010 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7014 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7018 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7022 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7026 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7030 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7034 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7038 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7042 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7046 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7050 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7054 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7058 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7062 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7066 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7070 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7074 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7078 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7082 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7086 EL_PLAYER_3, ACTION_DEFAULT, -1,
7090 EL_PLAYER_4, ACTION_DEFAULT, -1,
7099 int map_element_RND_to_EM(int element_rnd)
7101 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7102 static boolean mapping_initialized = FALSE;
7104 if (!mapping_initialized)
7108 /* return "Xalpha_quest" for all undefined elements in mapping array */
7109 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7110 mapping_RND_to_EM[i] = Xalpha_quest;
7112 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7113 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7114 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7115 em_object_mapping_list[i].element_em;
7117 mapping_initialized = TRUE;
7120 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7121 return mapping_RND_to_EM[element_rnd];
7123 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7128 int map_element_EM_to_RND(int element_em)
7130 static unsigned short mapping_EM_to_RND[TILE_MAX];
7131 static boolean mapping_initialized = FALSE;
7133 if (!mapping_initialized)
7137 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
7138 for (i = 0; i < TILE_MAX; i++)
7139 mapping_EM_to_RND[i] = EL_UNKNOWN;
7141 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7142 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7143 em_object_mapping_list[i].element_rnd;
7145 mapping_initialized = TRUE;
7148 if (element_em >= 0 && element_em < TILE_MAX)
7149 return mapping_EM_to_RND[element_em];
7151 Error(ERR_WARN, "invalid EM level element %d", element_em);
7156 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7158 struct LevelInfo_EM *level_em = level->native_em_level;
7159 struct LEVEL *lev = level_em->lev;
7162 for (i = 0; i < TILE_MAX; i++)
7163 lev->android_array[i] = Xblank;
7165 for (i = 0; i < level->num_android_clone_elements; i++)
7167 int element_rnd = level->android_clone_element[i];
7168 int element_em = map_element_RND_to_EM(element_rnd);
7170 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7171 if (em_object_mapping_list[j].element_rnd == element_rnd)
7172 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7176 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7178 struct LevelInfo_EM *level_em = level->native_em_level;
7179 struct LEVEL *lev = level_em->lev;
7182 level->num_android_clone_elements = 0;
7184 for (i = 0; i < TILE_MAX; i++)
7186 int element_em = lev->android_array[i];
7188 boolean element_found = FALSE;
7190 if (element_em == Xblank)
7193 element_rnd = map_element_EM_to_RND(element_em);
7195 for (j = 0; j < level->num_android_clone_elements; j++)
7196 if (level->android_clone_element[j] == element_rnd)
7197 element_found = TRUE;
7201 level->android_clone_element[level->num_android_clone_elements++] =
7204 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7209 if (level->num_android_clone_elements == 0)
7211 level->num_android_clone_elements = 1;
7212 level->android_clone_element[0] = EL_EMPTY;
7216 int map_direction_RND_to_EM(int direction)
7218 return (direction == MV_UP ? 0 :
7219 direction == MV_RIGHT ? 1 :
7220 direction == MV_DOWN ? 2 :
7221 direction == MV_LEFT ? 3 :
7225 int map_direction_EM_to_RND(int direction)
7227 return (direction == 0 ? MV_UP :
7228 direction == 1 ? MV_RIGHT :
7229 direction == 2 ? MV_DOWN :
7230 direction == 3 ? MV_LEFT :
7234 int map_element_RND_to_SP(int element_rnd)
7236 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
7238 if (element_rnd >= EL_SP_START &&
7239 element_rnd <= EL_SP_END)
7240 element_sp = element_rnd - EL_SP_START;
7241 else if (element_rnd == EL_EMPTY_SPACE)
7243 else if (element_rnd == EL_INVISIBLE_WALL)
7249 int map_element_SP_to_RND(int element_sp)
7251 int element_rnd = EL_UNKNOWN;
7253 if (element_sp >= 0x00 &&
7255 element_rnd = EL_SP_START + element_sp;
7256 else if (element_sp == 0x28)
7257 element_rnd = EL_INVISIBLE_WALL;
7262 int map_action_SP_to_RND(int action_sp)
7266 case actActive: return ACTION_ACTIVE;
7267 case actImpact: return ACTION_IMPACT;
7268 case actExploding: return ACTION_EXPLODING;
7269 case actDigging: return ACTION_DIGGING;
7270 case actSnapping: return ACTION_SNAPPING;
7271 case actCollecting: return ACTION_COLLECTING;
7272 case actPassing: return ACTION_PASSING;
7273 case actPushing: return ACTION_PUSHING;
7274 case actDropping: return ACTION_DROPPING;
7276 default: return ACTION_DEFAULT;
7280 int map_element_RND_to_MM(int element_rnd)
7282 return (element_rnd >= EL_MM_START_1 &&
7283 element_rnd <= EL_MM_END_1 ?
7284 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7286 element_rnd >= EL_MM_START_2 &&
7287 element_rnd <= EL_MM_END_2 ?
7288 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7290 element_rnd >= EL_CHAR_START &&
7291 element_rnd <= EL_CHAR_END ?
7292 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7294 element_rnd >= EL_MM_RUNTIME_START &&
7295 element_rnd <= EL_MM_RUNTIME_END ?
7296 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7298 element_rnd >= EL_MM_DUMMY_START &&
7299 element_rnd <= EL_MM_DUMMY_END ?
7300 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7302 EL_MM_EMPTY_NATIVE);
7305 int map_element_MM_to_RND(int element_mm)
7307 return (element_mm == EL_MM_EMPTY_NATIVE ||
7308 element_mm == EL_DF_EMPTY_NATIVE ?
7311 element_mm >= EL_MM_START_1_NATIVE &&
7312 element_mm <= EL_MM_END_1_NATIVE ?
7313 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7315 element_mm >= EL_MM_START_2_NATIVE &&
7316 element_mm <= EL_MM_END_2_NATIVE ?
7317 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7319 element_mm >= EL_MM_CHAR_START_NATIVE &&
7320 element_mm <= EL_MM_CHAR_END_NATIVE ?
7321 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7323 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7324 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7325 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7327 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7328 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7329 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7334 int map_mm_wall_element(int element)
7336 return (element >= EL_MM_STEEL_WALL_START &&
7337 element <= EL_MM_STEEL_WALL_END ?
7340 element >= EL_MM_WOODEN_WALL_START &&
7341 element <= EL_MM_WOODEN_WALL_END ?
7344 element >= EL_MM_ICE_WALL_START &&
7345 element <= EL_MM_ICE_WALL_END ?
7348 element >= EL_MM_AMOEBA_WALL_START &&
7349 element <= EL_MM_AMOEBA_WALL_END ?
7352 element >= EL_DF_STEEL_WALL_START &&
7353 element <= EL_DF_STEEL_WALL_END ?
7356 element >= EL_DF_WOODEN_WALL_START &&
7357 element <= EL_DF_WOODEN_WALL_END ?
7363 int get_next_element(int element)
7367 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7368 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7369 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7370 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7371 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7372 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7373 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7374 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7375 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7376 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7377 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7379 default: return element;
7383 int el2img_mm(int element_mm)
7385 return el2img(map_element_MM_to_RND(element_mm));
7388 int el_act_dir2img(int element, int action, int direction)
7390 element = GFX_ELEMENT(element);
7391 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7393 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7394 return element_info[element].direction_graphic[action][direction];
7397 static int el_act_dir2crm(int element, int action, int direction)
7399 element = GFX_ELEMENT(element);
7400 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7402 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7403 return element_info[element].direction_crumbled[action][direction];
7406 int el_act2img(int element, int action)
7408 element = GFX_ELEMENT(element);
7410 return element_info[element].graphic[action];
7413 int el_act2crm(int element, int action)
7415 element = GFX_ELEMENT(element);
7417 return element_info[element].crumbled[action];
7420 int el_dir2img(int element, int direction)
7422 element = GFX_ELEMENT(element);
7424 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7427 int el2baseimg(int element)
7429 return element_info[element].graphic[ACTION_DEFAULT];
7432 int el2img(int element)
7434 element = GFX_ELEMENT(element);
7436 return element_info[element].graphic[ACTION_DEFAULT];
7439 int el2edimg(int element)
7441 element = GFX_ELEMENT(element);
7443 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7446 int el2preimg(int element)
7448 element = GFX_ELEMENT(element);
7450 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7453 int el2panelimg(int element)
7455 element = GFX_ELEMENT(element);
7457 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7460 int font2baseimg(int font_nr)
7462 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7465 int getBeltNrFromBeltElement(int element)
7467 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7468 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7469 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
7472 int getBeltNrFromBeltActiveElement(int element)
7474 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
7475 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
7476 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
7479 int getBeltNrFromBeltSwitchElement(int element)
7481 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
7482 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
7483 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
7486 int getBeltDirNrFromBeltElement(int element)
7488 static int belt_base_element[4] =
7490 EL_CONVEYOR_BELT_1_LEFT,
7491 EL_CONVEYOR_BELT_2_LEFT,
7492 EL_CONVEYOR_BELT_3_LEFT,
7493 EL_CONVEYOR_BELT_4_LEFT
7496 int belt_nr = getBeltNrFromBeltElement(element);
7497 int belt_dir_nr = element - belt_base_element[belt_nr];
7499 return (belt_dir_nr % 3);
7502 int getBeltDirNrFromBeltSwitchElement(int element)
7504 static int belt_base_element[4] =
7506 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7507 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7508 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7509 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7512 int belt_nr = getBeltNrFromBeltSwitchElement(element);
7513 int belt_dir_nr = element - belt_base_element[belt_nr];
7515 return (belt_dir_nr % 3);
7518 int getBeltDirFromBeltElement(int element)
7520 static int belt_move_dir[3] =
7527 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7529 return belt_move_dir[belt_dir_nr];
7532 int getBeltDirFromBeltSwitchElement(int element)
7534 static int belt_move_dir[3] =
7541 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7543 return belt_move_dir[belt_dir_nr];
7546 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7548 static int belt_base_element[4] =
7550 EL_CONVEYOR_BELT_1_LEFT,
7551 EL_CONVEYOR_BELT_2_LEFT,
7552 EL_CONVEYOR_BELT_3_LEFT,
7553 EL_CONVEYOR_BELT_4_LEFT
7556 return belt_base_element[belt_nr] + belt_dir_nr;
7559 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7561 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7563 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7566 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7568 static int belt_base_element[4] =
7570 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7571 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7572 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7573 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7576 return belt_base_element[belt_nr] + belt_dir_nr;
7579 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7581 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7583 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7586 boolean getTeamMode_EM()
7588 return game.team_mode;
7591 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7593 int game_frame_delay_value;
7595 game_frame_delay_value =
7596 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7597 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7600 if (tape.playing && tape.warp_forward && !tape.pausing)
7601 game_frame_delay_value = 0;
7603 return game_frame_delay_value;
7606 unsigned int InitRND(int seed)
7608 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7609 return InitEngineRandom_EM(seed);
7610 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7611 return InitEngineRandom_SP(seed);
7612 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
7613 return InitEngineRandom_MM(seed);
7615 return InitEngineRandom_RND(seed);
7618 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7619 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7621 inline static int get_effective_element_EM(int tile, int frame_em)
7623 int element = object_mapping[tile].element_rnd;
7624 int action = object_mapping[tile].action;
7625 boolean is_backside = object_mapping[tile].is_backside;
7626 boolean action_removing = (action == ACTION_DIGGING ||
7627 action == ACTION_SNAPPING ||
7628 action == ACTION_COLLECTING);
7634 case Yacid_splash_eB:
7635 case Yacid_splash_wB:
7636 return (frame_em > 5 ? EL_EMPTY : element);
7642 else /* frame_em == 7 */
7646 case Yacid_splash_eB:
7647 case Yacid_splash_wB:
7650 case Yemerald_stone:
7653 case Ydiamond_stone:
7657 case Xdrip_stretchB:
7676 case Xsand_stonein_1:
7677 case Xsand_stonein_2:
7678 case Xsand_stonein_3:
7679 case Xsand_stonein_4:
7683 return (is_backside || action_removing ? EL_EMPTY : element);
7688 inline static boolean check_linear_animation_EM(int tile)
7692 case Xsand_stonesand_1:
7693 case Xsand_stonesand_quickout_1:
7694 case Xsand_sandstone_1:
7695 case Xsand_stonein_1:
7696 case Xsand_stoneout_1:
7715 case Yacid_splash_eB:
7716 case Yacid_splash_wB:
7717 case Yemerald_stone:
7724 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7725 boolean has_crumbled_graphics,
7726 int crumbled, int sync_frame)
7728 /* if element can be crumbled, but certain action graphics are just empty
7729 space (like instantly snapping sand to empty space in 1 frame), do not
7730 treat these empty space graphics as crumbled graphics in EMC engine */
7731 if (crumbled == IMG_EMPTY_SPACE)
7732 has_crumbled_graphics = FALSE;
7734 if (has_crumbled_graphics)
7736 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7737 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7738 g_crumbled->anim_delay,
7739 g_crumbled->anim_mode,
7740 g_crumbled->anim_start_frame,
7743 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7744 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7746 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7747 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
7749 g_em->has_crumbled_graphics = TRUE;
7753 g_em->crumbled_bitmap = NULL;
7754 g_em->crumbled_src_x = 0;
7755 g_em->crumbled_src_y = 0;
7756 g_em->crumbled_border_size = 0;
7757 g_em->crumbled_tile_size = 0;
7759 g_em->has_crumbled_graphics = FALSE;
7763 void ResetGfxAnimation_EM(int x, int y, int tile)
7768 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7769 int tile, int frame_em, int x, int y)
7771 int action = object_mapping[tile].action;
7772 int direction = object_mapping[tile].direction;
7773 int effective_element = get_effective_element_EM(tile, frame_em);
7774 int graphic = (direction == MV_NONE ?
7775 el_act2img(effective_element, action) :
7776 el_act_dir2img(effective_element, action, direction));
7777 struct GraphicInfo *g = &graphic_info[graphic];
7779 boolean action_removing = (action == ACTION_DIGGING ||
7780 action == ACTION_SNAPPING ||
7781 action == ACTION_COLLECTING);
7782 boolean action_moving = (action == ACTION_FALLING ||
7783 action == ACTION_MOVING ||
7784 action == ACTION_PUSHING ||
7785 action == ACTION_EATING ||
7786 action == ACTION_FILLING ||
7787 action == ACTION_EMPTYING);
7788 boolean action_falling = (action == ACTION_FALLING ||
7789 action == ACTION_FILLING ||
7790 action == ACTION_EMPTYING);
7792 /* special case: graphic uses "2nd movement tile" and has defined
7793 7 frames for movement animation (or less) => use default graphic
7794 for last (8th) frame which ends the movement animation */
7795 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7797 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7798 graphic = (direction == MV_NONE ?
7799 el_act2img(effective_element, action) :
7800 el_act_dir2img(effective_element, action, direction));
7802 g = &graphic_info[graphic];
7805 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7809 else if (action_moving)
7811 boolean is_backside = object_mapping[tile].is_backside;
7815 int direction = object_mapping[tile].direction;
7816 int move_dir = (action_falling ? MV_DOWN : direction);
7821 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7822 if (g->double_movement && frame_em == 0)
7826 if (move_dir == MV_LEFT)
7827 GfxFrame[x - 1][y] = GfxFrame[x][y];
7828 else if (move_dir == MV_RIGHT)
7829 GfxFrame[x + 1][y] = GfxFrame[x][y];
7830 else if (move_dir == MV_UP)
7831 GfxFrame[x][y - 1] = GfxFrame[x][y];
7832 else if (move_dir == MV_DOWN)
7833 GfxFrame[x][y + 1] = GfxFrame[x][y];
7840 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7841 if (tile == Xsand_stonesand_quickout_1 ||
7842 tile == Xsand_stonesand_quickout_2)
7846 if (graphic_info[graphic].anim_global_sync)
7847 sync_frame = FrameCounter;
7848 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7849 sync_frame = GfxFrame[x][y];
7851 sync_frame = 0; /* playfield border (pseudo steel) */
7853 SetRandomAnimationValue(x, y);
7855 int frame = getAnimationFrame(g->anim_frames,
7858 g->anim_start_frame,
7861 g_em->unique_identifier =
7862 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7865 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7866 int tile, int frame_em, int x, int y)
7868 int action = object_mapping[tile].action;
7869 int direction = object_mapping[tile].direction;
7870 boolean is_backside = object_mapping[tile].is_backside;
7871 int effective_element = get_effective_element_EM(tile, frame_em);
7872 int effective_action = action;
7873 int graphic = (direction == MV_NONE ?
7874 el_act2img(effective_element, effective_action) :
7875 el_act_dir2img(effective_element, effective_action,
7877 int crumbled = (direction == MV_NONE ?
7878 el_act2crm(effective_element, effective_action) :
7879 el_act_dir2crm(effective_element, effective_action,
7881 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7882 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7883 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7884 struct GraphicInfo *g = &graphic_info[graphic];
7887 /* special case: graphic uses "2nd movement tile" and has defined
7888 7 frames for movement animation (or less) => use default graphic
7889 for last (8th) frame which ends the movement animation */
7890 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7892 effective_action = ACTION_DEFAULT;
7893 graphic = (direction == MV_NONE ?
7894 el_act2img(effective_element, effective_action) :
7895 el_act_dir2img(effective_element, effective_action,
7897 crumbled = (direction == MV_NONE ?
7898 el_act2crm(effective_element, effective_action) :
7899 el_act_dir2crm(effective_element, effective_action,
7902 g = &graphic_info[graphic];
7905 if (graphic_info[graphic].anim_global_sync)
7906 sync_frame = FrameCounter;
7907 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7908 sync_frame = GfxFrame[x][y];
7910 sync_frame = 0; /* playfield border (pseudo steel) */
7912 SetRandomAnimationValue(x, y);
7914 int frame = getAnimationFrame(g->anim_frames,
7917 g->anim_start_frame,
7920 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7921 g->double_movement && is_backside);
7923 /* (updating the "crumbled" graphic definitions is probably not really needed,
7924 as animations for crumbled graphics can't be longer than one EMC cycle) */
7925 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7929 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7930 int player_nr, int anim, int frame_em)
7932 int element = player_mapping[player_nr][anim].element_rnd;
7933 int action = player_mapping[player_nr][anim].action;
7934 int direction = player_mapping[player_nr][anim].direction;
7935 int graphic = (direction == MV_NONE ?
7936 el_act2img(element, action) :
7937 el_act_dir2img(element, action, direction));
7938 struct GraphicInfo *g = &graphic_info[graphic];
7941 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7943 stored_player[player_nr].StepFrame = frame_em;
7945 sync_frame = stored_player[player_nr].Frame;
7947 int frame = getAnimationFrame(g->anim_frames,
7950 g->anim_start_frame,
7953 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7954 &g_em->src_x, &g_em->src_y, FALSE);
7957 void InitGraphicInfo_EM(void)
7962 int num_em_gfx_errors = 0;
7964 if (graphic_info_em_object[0][0].bitmap == NULL)
7966 /* EM graphics not yet initialized in em_open_all() */
7971 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7974 /* always start with reliable default values */
7975 for (i = 0; i < TILE_MAX; i++)
7977 object_mapping[i].element_rnd = EL_UNKNOWN;
7978 object_mapping[i].is_backside = FALSE;
7979 object_mapping[i].action = ACTION_DEFAULT;
7980 object_mapping[i].direction = MV_NONE;
7983 /* always start with reliable default values */
7984 for (p = 0; p < MAX_PLAYERS; p++)
7986 for (i = 0; i < SPR_MAX; i++)
7988 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7989 player_mapping[p][i].action = ACTION_DEFAULT;
7990 player_mapping[p][i].direction = MV_NONE;
7994 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7996 int e = em_object_mapping_list[i].element_em;
7998 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7999 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8001 if (em_object_mapping_list[i].action != -1)
8002 object_mapping[e].action = em_object_mapping_list[i].action;
8004 if (em_object_mapping_list[i].direction != -1)
8005 object_mapping[e].direction =
8006 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8009 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8011 int a = em_player_mapping_list[i].action_em;
8012 int p = em_player_mapping_list[i].player_nr;
8014 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8016 if (em_player_mapping_list[i].action != -1)
8017 player_mapping[p][a].action = em_player_mapping_list[i].action;
8019 if (em_player_mapping_list[i].direction != -1)
8020 player_mapping[p][a].direction =
8021 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8024 for (i = 0; i < TILE_MAX; i++)
8026 int element = object_mapping[i].element_rnd;
8027 int action = object_mapping[i].action;
8028 int direction = object_mapping[i].direction;
8029 boolean is_backside = object_mapping[i].is_backside;
8030 boolean action_exploding = ((action == ACTION_EXPLODING ||
8031 action == ACTION_SMASHED_BY_ROCK ||
8032 action == ACTION_SMASHED_BY_SPRING) &&
8033 element != EL_DIAMOND);
8034 boolean action_active = (action == ACTION_ACTIVE);
8035 boolean action_other = (action == ACTION_OTHER);
8037 for (j = 0; j < 8; j++)
8039 int effective_element = get_effective_element_EM(i, j);
8040 int effective_action = (j < 7 ? action :
8041 i == Xdrip_stretch ? action :
8042 i == Xdrip_stretchB ? action :
8043 i == Ydrip_s1 ? action :
8044 i == Ydrip_s1B ? action :
8045 i == Xball_1B ? action :
8046 i == Xball_2 ? action :
8047 i == Xball_2B ? action :
8048 i == Yball_eat ? action :
8049 i == Ykey_1_eat ? action :
8050 i == Ykey_2_eat ? action :
8051 i == Ykey_3_eat ? action :
8052 i == Ykey_4_eat ? action :
8053 i == Ykey_5_eat ? action :
8054 i == Ykey_6_eat ? action :
8055 i == Ykey_7_eat ? action :
8056 i == Ykey_8_eat ? action :
8057 i == Ylenses_eat ? action :
8058 i == Ymagnify_eat ? action :
8059 i == Ygrass_eat ? action :
8060 i == Ydirt_eat ? action :
8061 i == Xsand_stonein_1 ? action :
8062 i == Xsand_stonein_2 ? action :
8063 i == Xsand_stonein_3 ? action :
8064 i == Xsand_stonein_4 ? action :
8065 i == Xsand_stoneout_1 ? action :
8066 i == Xsand_stoneout_2 ? action :
8067 i == Xboom_android ? ACTION_EXPLODING :
8068 action_exploding ? ACTION_EXPLODING :
8069 action_active ? action :
8070 action_other ? action :
8072 int graphic = (el_act_dir2img(effective_element, effective_action,
8074 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8076 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8077 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8078 boolean has_action_graphics = (graphic != base_graphic);
8079 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8080 struct GraphicInfo *g = &graphic_info[graphic];
8081 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8084 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
8085 boolean special_animation = (action != ACTION_DEFAULT &&
8086 g->anim_frames == 3 &&
8087 g->anim_delay == 2 &&
8088 g->anim_mode & ANIM_LINEAR);
8089 int sync_frame = (i == Xdrip_stretch ? 7 :
8090 i == Xdrip_stretchB ? 7 :
8091 i == Ydrip_s2 ? j + 8 :
8092 i == Ydrip_s2B ? j + 8 :
8101 i == Xfake_acid_1 ? 0 :
8102 i == Xfake_acid_2 ? 10 :
8103 i == Xfake_acid_3 ? 20 :
8104 i == Xfake_acid_4 ? 30 :
8105 i == Xfake_acid_5 ? 40 :
8106 i == Xfake_acid_6 ? 50 :
8107 i == Xfake_acid_7 ? 60 :
8108 i == Xfake_acid_8 ? 70 :
8110 i == Xball_2B ? j + 8 :
8111 i == Yball_eat ? j + 1 :
8112 i == Ykey_1_eat ? j + 1 :
8113 i == Ykey_2_eat ? j + 1 :
8114 i == Ykey_3_eat ? j + 1 :
8115 i == Ykey_4_eat ? j + 1 :
8116 i == Ykey_5_eat ? j + 1 :
8117 i == Ykey_6_eat ? j + 1 :
8118 i == Ykey_7_eat ? j + 1 :
8119 i == Ykey_8_eat ? j + 1 :
8120 i == Ylenses_eat ? j + 1 :
8121 i == Ymagnify_eat ? j + 1 :
8122 i == Ygrass_eat ? j + 1 :
8123 i == Ydirt_eat ? j + 1 :
8124 i == Xamoeba_1 ? 0 :
8125 i == Xamoeba_2 ? 1 :
8126 i == Xamoeba_3 ? 2 :
8127 i == Xamoeba_4 ? 3 :
8128 i == Xamoeba_5 ? 0 :
8129 i == Xamoeba_6 ? 1 :
8130 i == Xamoeba_7 ? 2 :
8131 i == Xamoeba_8 ? 3 :
8132 i == Xexit_2 ? j + 8 :
8133 i == Xexit_3 ? j + 16 :
8134 i == Xdynamite_1 ? 0 :
8135 i == Xdynamite_2 ? 8 :
8136 i == Xdynamite_3 ? 16 :
8137 i == Xdynamite_4 ? 24 :
8138 i == Xsand_stonein_1 ? j + 1 :
8139 i == Xsand_stonein_2 ? j + 9 :
8140 i == Xsand_stonein_3 ? j + 17 :
8141 i == Xsand_stonein_4 ? j + 25 :
8142 i == Xsand_stoneout_1 && j == 0 ? 0 :
8143 i == Xsand_stoneout_1 && j == 1 ? 0 :
8144 i == Xsand_stoneout_1 && j == 2 ? 1 :
8145 i == Xsand_stoneout_1 && j == 3 ? 2 :
8146 i == Xsand_stoneout_1 && j == 4 ? 2 :
8147 i == Xsand_stoneout_1 && j == 5 ? 3 :
8148 i == Xsand_stoneout_1 && j == 6 ? 4 :
8149 i == Xsand_stoneout_1 && j == 7 ? 4 :
8150 i == Xsand_stoneout_2 && j == 0 ? 5 :
8151 i == Xsand_stoneout_2 && j == 1 ? 6 :
8152 i == Xsand_stoneout_2 && j == 2 ? 7 :
8153 i == Xsand_stoneout_2 && j == 3 ? 8 :
8154 i == Xsand_stoneout_2 && j == 4 ? 9 :
8155 i == Xsand_stoneout_2 && j == 5 ? 11 :
8156 i == Xsand_stoneout_2 && j == 6 ? 13 :
8157 i == Xsand_stoneout_2 && j == 7 ? 15 :
8158 i == Xboom_bug && j == 1 ? 2 :
8159 i == Xboom_bug && j == 2 ? 2 :
8160 i == Xboom_bug && j == 3 ? 4 :
8161 i == Xboom_bug && j == 4 ? 4 :
8162 i == Xboom_bug && j == 5 ? 2 :
8163 i == Xboom_bug && j == 6 ? 2 :
8164 i == Xboom_bug && j == 7 ? 0 :
8165 i == Xboom_bomb && j == 1 ? 2 :
8166 i == Xboom_bomb && j == 2 ? 2 :
8167 i == Xboom_bomb && j == 3 ? 4 :
8168 i == Xboom_bomb && j == 4 ? 4 :
8169 i == Xboom_bomb && j == 5 ? 2 :
8170 i == Xboom_bomb && j == 6 ? 2 :
8171 i == Xboom_bomb && j == 7 ? 0 :
8172 i == Xboom_android && j == 7 ? 6 :
8173 i == Xboom_1 && j == 1 ? 2 :
8174 i == Xboom_1 && j == 2 ? 2 :
8175 i == Xboom_1 && j == 3 ? 4 :
8176 i == Xboom_1 && j == 4 ? 4 :
8177 i == Xboom_1 && j == 5 ? 6 :
8178 i == Xboom_1 && j == 6 ? 6 :
8179 i == Xboom_1 && j == 7 ? 8 :
8180 i == Xboom_2 && j == 0 ? 8 :
8181 i == Xboom_2 && j == 1 ? 8 :
8182 i == Xboom_2 && j == 2 ? 10 :
8183 i == Xboom_2 && j == 3 ? 10 :
8184 i == Xboom_2 && j == 4 ? 10 :
8185 i == Xboom_2 && j == 5 ? 12 :
8186 i == Xboom_2 && j == 6 ? 12 :
8187 i == Xboom_2 && j == 7 ? 12 :
8188 special_animation && j == 4 ? 3 :
8189 effective_action != action ? 0 :
8193 Bitmap *debug_bitmap = g_em->bitmap;
8194 int debug_src_x = g_em->src_x;
8195 int debug_src_y = g_em->src_y;
8198 int frame = getAnimationFrame(g->anim_frames,
8201 g->anim_start_frame,
8204 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8205 g->double_movement && is_backside);
8207 g_em->bitmap = src_bitmap;
8208 g_em->src_x = src_x;
8209 g_em->src_y = src_y;
8210 g_em->src_offset_x = 0;
8211 g_em->src_offset_y = 0;
8212 g_em->dst_offset_x = 0;
8213 g_em->dst_offset_y = 0;
8214 g_em->width = TILEX;
8215 g_em->height = TILEY;
8217 g_em->preserve_background = FALSE;
8219 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8222 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8223 effective_action == ACTION_MOVING ||
8224 effective_action == ACTION_PUSHING ||
8225 effective_action == ACTION_EATING)) ||
8226 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8227 effective_action == ACTION_EMPTYING)))
8230 (effective_action == ACTION_FALLING ||
8231 effective_action == ACTION_FILLING ||
8232 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8233 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8234 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8235 int num_steps = (i == Ydrip_s1 ? 16 :
8236 i == Ydrip_s1B ? 16 :
8237 i == Ydrip_s2 ? 16 :
8238 i == Ydrip_s2B ? 16 :
8239 i == Xsand_stonein_1 ? 32 :
8240 i == Xsand_stonein_2 ? 32 :
8241 i == Xsand_stonein_3 ? 32 :
8242 i == Xsand_stonein_4 ? 32 :
8243 i == Xsand_stoneout_1 ? 16 :
8244 i == Xsand_stoneout_2 ? 16 : 8);
8245 int cx = ABS(dx) * (TILEX / num_steps);
8246 int cy = ABS(dy) * (TILEY / num_steps);
8247 int step_frame = (i == Ydrip_s2 ? j + 8 :
8248 i == Ydrip_s2B ? j + 8 :
8249 i == Xsand_stonein_2 ? j + 8 :
8250 i == Xsand_stonein_3 ? j + 16 :
8251 i == Xsand_stonein_4 ? j + 24 :
8252 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8253 int step = (is_backside ? step_frame : num_steps - step_frame);
8255 if (is_backside) /* tile where movement starts */
8257 if (dx < 0 || dy < 0)
8259 g_em->src_offset_x = cx * step;
8260 g_em->src_offset_y = cy * step;
8264 g_em->dst_offset_x = cx * step;
8265 g_em->dst_offset_y = cy * step;
8268 else /* tile where movement ends */
8270 if (dx < 0 || dy < 0)
8272 g_em->dst_offset_x = cx * step;
8273 g_em->dst_offset_y = cy * step;
8277 g_em->src_offset_x = cx * step;
8278 g_em->src_offset_y = cy * step;
8282 g_em->width = TILEX - cx * step;
8283 g_em->height = TILEY - cy * step;
8286 /* create unique graphic identifier to decide if tile must be redrawn */
8287 /* bit 31 - 16 (16 bit): EM style graphic
8288 bit 15 - 12 ( 4 bit): EM style frame
8289 bit 11 - 6 ( 6 bit): graphic width
8290 bit 5 - 0 ( 6 bit): graphic height */
8291 g_em->unique_identifier =
8292 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8296 /* skip check for EMC elements not contained in original EMC artwork */
8297 if (element == EL_EMC_FAKE_ACID)
8300 if (g_em->bitmap != debug_bitmap ||
8301 g_em->src_x != debug_src_x ||
8302 g_em->src_y != debug_src_y ||
8303 g_em->src_offset_x != 0 ||
8304 g_em->src_offset_y != 0 ||
8305 g_em->dst_offset_x != 0 ||
8306 g_em->dst_offset_y != 0 ||
8307 g_em->width != TILEX ||
8308 g_em->height != TILEY)
8310 static int last_i = -1;
8318 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8319 i, element, element_info[element].token_name,
8320 element_action_info[effective_action].suffix, direction);
8322 if (element != effective_element)
8323 printf(" [%d ('%s')]",
8325 element_info[effective_element].token_name);
8329 if (g_em->bitmap != debug_bitmap)
8330 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8331 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8333 if (g_em->src_x != debug_src_x ||
8334 g_em->src_y != debug_src_y)
8335 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8336 j, (is_backside ? 'B' : 'F'),
8337 g_em->src_x, g_em->src_y,
8338 g_em->src_x / 32, g_em->src_y / 32,
8339 debug_src_x, debug_src_y,
8340 debug_src_x / 32, debug_src_y / 32);
8342 if (g_em->src_offset_x != 0 ||
8343 g_em->src_offset_y != 0 ||
8344 g_em->dst_offset_x != 0 ||
8345 g_em->dst_offset_y != 0)
8346 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8348 g_em->src_offset_x, g_em->src_offset_y,
8349 g_em->dst_offset_x, g_em->dst_offset_y);
8351 if (g_em->width != TILEX ||
8352 g_em->height != TILEY)
8353 printf(" %d (%d): size %d,%d should be %d,%d\n",
8355 g_em->width, g_em->height, TILEX, TILEY);
8357 num_em_gfx_errors++;
8364 for (i = 0; i < TILE_MAX; i++)
8366 for (j = 0; j < 8; j++)
8368 int element = object_mapping[i].element_rnd;
8369 int action = object_mapping[i].action;
8370 int direction = object_mapping[i].direction;
8371 boolean is_backside = object_mapping[i].is_backside;
8372 int graphic_action = el_act_dir2img(element, action, direction);
8373 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8375 if ((action == ACTION_SMASHED_BY_ROCK ||
8376 action == ACTION_SMASHED_BY_SPRING ||
8377 action == ACTION_EATING) &&
8378 graphic_action == graphic_default)
8380 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8381 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8382 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8383 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8386 /* no separate animation for "smashed by rock" -- use rock instead */
8387 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8388 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8390 g_em->bitmap = g_xx->bitmap;
8391 g_em->src_x = g_xx->src_x;
8392 g_em->src_y = g_xx->src_y;
8393 g_em->src_offset_x = g_xx->src_offset_x;
8394 g_em->src_offset_y = g_xx->src_offset_y;
8395 g_em->dst_offset_x = g_xx->dst_offset_x;
8396 g_em->dst_offset_y = g_xx->dst_offset_y;
8397 g_em->width = g_xx->width;
8398 g_em->height = g_xx->height;
8399 g_em->unique_identifier = g_xx->unique_identifier;
8402 g_em->preserve_background = TRUE;
8407 for (p = 0; p < MAX_PLAYERS; p++)
8409 for (i = 0; i < SPR_MAX; i++)
8411 int element = player_mapping[p][i].element_rnd;
8412 int action = player_mapping[p][i].action;
8413 int direction = player_mapping[p][i].direction;
8415 for (j = 0; j < 8; j++)
8417 int effective_element = element;
8418 int effective_action = action;
8419 int graphic = (direction == MV_NONE ?
8420 el_act2img(effective_element, effective_action) :
8421 el_act_dir2img(effective_element, effective_action,
8423 struct GraphicInfo *g = &graphic_info[graphic];
8424 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8430 Bitmap *debug_bitmap = g_em->bitmap;
8431 int debug_src_x = g_em->src_x;
8432 int debug_src_y = g_em->src_y;
8435 int frame = getAnimationFrame(g->anim_frames,
8438 g->anim_start_frame,
8441 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8443 g_em->bitmap = src_bitmap;
8444 g_em->src_x = src_x;
8445 g_em->src_y = src_y;
8446 g_em->src_offset_x = 0;
8447 g_em->src_offset_y = 0;
8448 g_em->dst_offset_x = 0;
8449 g_em->dst_offset_y = 0;
8450 g_em->width = TILEX;
8451 g_em->height = TILEY;
8455 /* skip check for EMC elements not contained in original EMC artwork */
8456 if (element == EL_PLAYER_3 ||
8457 element == EL_PLAYER_4)
8460 if (g_em->bitmap != debug_bitmap ||
8461 g_em->src_x != debug_src_x ||
8462 g_em->src_y != debug_src_y)
8464 static int last_i = -1;
8472 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
8473 p, i, element, element_info[element].token_name,
8474 element_action_info[effective_action].suffix, direction);
8476 if (element != effective_element)
8477 printf(" [%d ('%s')]",
8479 element_info[effective_element].token_name);
8483 if (g_em->bitmap != debug_bitmap)
8484 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
8485 j, (int)(g_em->bitmap), (int)(debug_bitmap));
8487 if (g_em->src_x != debug_src_x ||
8488 g_em->src_y != debug_src_y)
8489 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8491 g_em->src_x, g_em->src_y,
8492 g_em->src_x / 32, g_em->src_y / 32,
8493 debug_src_x, debug_src_y,
8494 debug_src_x / 32, debug_src_y / 32);
8496 num_em_gfx_errors++;
8506 printf("::: [%d errors found]\n", num_em_gfx_errors);
8512 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
8513 boolean any_player_moving,
8514 boolean any_player_snapping,
8515 boolean any_player_dropping)
8517 if (frame == 0 && !any_player_dropping)
8519 if (!local_player->was_waiting)
8521 if (!CheckSaveEngineSnapshotToList())
8524 local_player->was_waiting = TRUE;
8527 else if (any_player_moving || any_player_snapping || any_player_dropping)
8529 local_player->was_waiting = FALSE;
8533 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8534 boolean murphy_is_dropping)
8536 if (murphy_is_waiting)
8538 if (!local_player->was_waiting)
8540 if (!CheckSaveEngineSnapshotToList())
8543 local_player->was_waiting = TRUE;
8548 local_player->was_waiting = FALSE;
8552 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8553 boolean any_player_moving,
8554 boolean any_player_snapping,
8555 boolean any_player_dropping)
8557 if (tape.single_step && tape.recording && !tape.pausing)
8558 if (frame == 0 && !any_player_dropping)
8559 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8561 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8562 any_player_snapping, any_player_dropping);
8565 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8566 boolean murphy_is_dropping)
8568 boolean murphy_starts_dropping = FALSE;
8571 for (i = 0; i < MAX_PLAYERS; i++)
8572 if (stored_player[i].force_dropping)
8573 murphy_starts_dropping = TRUE;
8575 if (tape.single_step && tape.recording && !tape.pausing)
8576 if (murphy_is_waiting && !murphy_starts_dropping)
8577 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8579 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8582 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8583 int graphic, int sync_frame, int x, int y)
8585 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8587 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8590 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8592 return (IS_NEXT_FRAME(sync_frame, graphic));
8595 int getGraphicInfo_Delay(int graphic)
8597 return graphic_info[graphic].anim_delay;
8600 void PlayMenuSoundExt(int sound)
8602 if (sound == SND_UNDEFINED)
8605 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8606 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8609 if (IS_LOOP_SOUND(sound))
8610 PlaySoundLoop(sound);
8615 void PlayMenuSound()
8617 PlayMenuSoundExt(menu.sound[game_status]);
8620 void PlayMenuSoundStereo(int sound, int stereo_position)
8622 if (sound == SND_UNDEFINED)
8625 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8626 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8629 if (IS_LOOP_SOUND(sound))
8630 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8632 PlaySoundStereo(sound, stereo_position);
8635 void PlayMenuSoundIfLoopExt(int sound)
8637 if (sound == SND_UNDEFINED)
8640 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8641 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8644 if (IS_LOOP_SOUND(sound))
8645 PlaySoundLoop(sound);
8648 void PlayMenuSoundIfLoop()
8650 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8653 void PlayMenuMusicExt(int music)
8655 if (music == MUS_UNDEFINED)
8658 if (!setup.sound_music)
8664 void PlayMenuMusic()
8666 char *curr_music = getCurrentlyPlayingMusicFilename();
8667 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8669 if (!strEqual(curr_music, next_music))
8670 PlayMenuMusicExt(menu.music[game_status]);
8673 void PlayMenuSoundsAndMusic()
8679 static void FadeMenuSounds()
8684 static void FadeMenuMusic()
8686 char *curr_music = getCurrentlyPlayingMusicFilename();
8687 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8689 if (!strEqual(curr_music, next_music))
8693 void FadeMenuSoundsAndMusic()
8699 void PlaySoundActivating()
8702 PlaySound(SND_MENU_ITEM_ACTIVATING);
8706 void PlaySoundSelecting()
8709 PlaySound(SND_MENU_ITEM_SELECTING);
8713 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8715 boolean change_fullscreen = (setup.fullscreen !=
8716 video.fullscreen_enabled);
8717 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8718 setup.window_scaling_percent !=
8719 video.window_scaling_percent);
8721 if (change_window_scaling_percent && video.fullscreen_enabled)
8724 if (!change_window_scaling_percent && !video.fullscreen_available)
8727 #if defined(TARGET_SDL2)
8728 if (change_window_scaling_percent)
8730 SDLSetWindowScaling(setup.window_scaling_percent);
8734 else if (change_fullscreen)
8736 SDLSetWindowFullscreen(setup.fullscreen);
8738 /* set setup value according to successfully changed fullscreen mode */
8739 setup.fullscreen = video.fullscreen_enabled;
8745 if (change_fullscreen ||
8746 change_window_scaling_percent)
8748 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8750 /* save backbuffer content which gets lost when toggling fullscreen mode */
8751 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8753 if (change_window_scaling_percent)
8755 /* keep window mode, but change window scaling */
8756 video.fullscreen_enabled = TRUE; /* force new window scaling */
8759 /* toggle fullscreen */
8760 ChangeVideoModeIfNeeded(setup.fullscreen);
8762 /* set setup value according to successfully changed fullscreen mode */
8763 setup.fullscreen = video.fullscreen_enabled;
8765 /* restore backbuffer content from temporary backbuffer backup bitmap */
8766 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8768 FreeBitmap(tmp_backbuffer);
8770 /* update visible window/screen */
8771 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8775 void JoinRectangles(int *x, int *y, int *width, int *height,
8776 int x2, int y2, int width2, int height2)
8778 // do not join with "off-screen" rectangle
8779 if (x2 == -1 || y2 == -1)
8784 *width = MAX(*width, width2);
8785 *height = MAX(*height, height2);
8788 void SetAnimStatus(int anim_status_new)
8790 if (anim_status_new == GAME_MODE_MAIN)
8791 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
8792 else if (anim_status_new == GAME_MODE_SCORES)
8793 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
8795 global.anim_status_next = anim_status_new;
8797 // directly set screen modes that are entered without fading
8798 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
8799 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
8800 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
8801 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
8802 global.anim_status = global.anim_status_next;
8805 void SetGameStatus(int game_status_new)
8807 if (game_status_new != game_status)
8808 game_status_last_screen = game_status;
8810 game_status = game_status_new;
8812 SetAnimStatus(game_status_new);
8815 void SetFontStatus(int game_status_new)
8817 static int last_game_status = -1;
8819 if (game_status_new != -1)
8821 // set game status for font use after storing last game status
8822 last_game_status = game_status;
8823 game_status = game_status_new;
8827 // reset game status after font use from last stored game status
8828 game_status = last_game_status;
8832 void ResetFontStatus()
8837 void ChangeViewportPropertiesIfNeeded()
8839 int gfx_game_mode = game_status;
8840 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8842 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
8843 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8844 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8845 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8846 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8847 int new_win_xsize = vp_window->width;
8848 int new_win_ysize = vp_window->height;
8849 int border_size = vp_playfield->border_size;
8850 int new_sx = vp_playfield->x + border_size;
8851 int new_sy = vp_playfield->y + border_size;
8852 int new_sxsize = vp_playfield->width - 2 * border_size;
8853 int new_sysize = vp_playfield->height - 2 * border_size;
8854 int new_real_sx = vp_playfield->x;
8855 int new_real_sy = vp_playfield->y;
8856 int new_full_sxsize = vp_playfield->width;
8857 int new_full_sysize = vp_playfield->height;
8858 int new_dx = vp_door_1->x;
8859 int new_dy = vp_door_1->y;
8860 int new_dxsize = vp_door_1->width;
8861 int new_dysize = vp_door_1->height;
8862 int new_vx = vp_door_2->x;
8863 int new_vy = vp_door_2->y;
8864 int new_vxsize = vp_door_2->width;
8865 int new_vysize = vp_door_2->height;
8866 int new_ex = vp_door_3->x;
8867 int new_ey = vp_door_3->y;
8868 int new_exsize = vp_door_3->width;
8869 int new_eysize = vp_door_3->height;
8870 int new_tilesize_var =
8871 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8873 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8874 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8875 int new_scr_fieldx = new_sxsize / tilesize;
8876 int new_scr_fieldy = new_sysize / tilesize;
8877 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8878 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8879 boolean init_gfx_buffers = FALSE;
8880 boolean init_video_buffer = FALSE;
8881 boolean init_gadgets_and_anims = FALSE;
8882 boolean init_em_graphics = FALSE;
8884 if (new_win_xsize != WIN_XSIZE ||
8885 new_win_ysize != WIN_YSIZE)
8887 WIN_XSIZE = new_win_xsize;
8888 WIN_YSIZE = new_win_ysize;
8890 init_video_buffer = TRUE;
8891 init_gfx_buffers = TRUE;
8892 init_gadgets_and_anims = TRUE;
8894 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8897 if (new_scr_fieldx != SCR_FIELDX ||
8898 new_scr_fieldy != SCR_FIELDY)
8900 /* this always toggles between MAIN and GAME when using small tile size */
8902 SCR_FIELDX = new_scr_fieldx;
8903 SCR_FIELDY = new_scr_fieldy;
8905 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8916 new_sxsize != SXSIZE ||
8917 new_sysize != SYSIZE ||
8918 new_dxsize != DXSIZE ||
8919 new_dysize != DYSIZE ||
8920 new_vxsize != VXSIZE ||
8921 new_vysize != VYSIZE ||
8922 new_exsize != EXSIZE ||
8923 new_eysize != EYSIZE ||
8924 new_real_sx != REAL_SX ||
8925 new_real_sy != REAL_SY ||
8926 new_full_sxsize != FULL_SXSIZE ||
8927 new_full_sysize != FULL_SYSIZE ||
8928 new_tilesize_var != TILESIZE_VAR
8931 // ------------------------------------------------------------------------
8932 // determine next fading area for changed viewport definitions
8933 // ------------------------------------------------------------------------
8935 // start with current playfield area (default fading area)
8938 FADE_SXSIZE = FULL_SXSIZE;
8939 FADE_SYSIZE = FULL_SYSIZE;
8941 // add new playfield area if position or size has changed
8942 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8943 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8945 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8946 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8949 // add current and new door 1 area if position or size has changed
8950 if (new_dx != DX || new_dy != DY ||
8951 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8953 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8954 DX, DY, DXSIZE, DYSIZE);
8955 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8956 new_dx, new_dy, new_dxsize, new_dysize);
8959 // add current and new door 2 area if position or size has changed
8960 if (new_dx != VX || new_dy != VY ||
8961 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8963 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8964 VX, VY, VXSIZE, VYSIZE);
8965 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8966 new_vx, new_vy, new_vxsize, new_vysize);
8969 // ------------------------------------------------------------------------
8970 // handle changed tile size
8971 // ------------------------------------------------------------------------
8973 if (new_tilesize_var != TILESIZE_VAR)
8975 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8977 // changing tile size invalidates scroll values of engine snapshots
8978 FreeEngineSnapshotSingle();
8980 // changing tile size requires update of graphic mapping for EM engine
8981 init_em_graphics = TRUE;
8992 SXSIZE = new_sxsize;
8993 SYSIZE = new_sysize;
8994 DXSIZE = new_dxsize;
8995 DYSIZE = new_dysize;
8996 VXSIZE = new_vxsize;
8997 VYSIZE = new_vysize;
8998 EXSIZE = new_exsize;
8999 EYSIZE = new_eysize;
9000 REAL_SX = new_real_sx;
9001 REAL_SY = new_real_sy;
9002 FULL_SXSIZE = new_full_sxsize;
9003 FULL_SYSIZE = new_full_sysize;
9004 TILESIZE_VAR = new_tilesize_var;
9006 init_gfx_buffers = TRUE;
9007 init_gadgets_and_anims = TRUE;
9009 // printf("::: viewports: init_gfx_buffers\n");
9010 // printf("::: viewports: init_gadgets_and_anims\n");
9013 if (init_gfx_buffers)
9015 // printf("::: init_gfx_buffers\n");
9017 SCR_FIELDX = new_scr_fieldx_buffers;
9018 SCR_FIELDY = new_scr_fieldy_buffers;
9022 SCR_FIELDX = new_scr_fieldx;
9023 SCR_FIELDY = new_scr_fieldy;
9025 SetDrawDeactivationMask(REDRAW_NONE);
9026 SetDrawBackgroundMask(REDRAW_FIELD);
9029 if (init_video_buffer)
9031 // printf("::: init_video_buffer\n");
9033 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9034 InitImageTextures();
9037 if (init_gadgets_and_anims)
9039 // printf("::: init_gadgets_and_anims\n");
9042 InitGlobalAnimations();
9045 if (init_em_graphics)
9047 InitGraphicInfo_EM();