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_draw_x, dst_draw_y,
2480 tilesize_draw, tilesize_draw);
2484 void DrawSizedElement(int x, int y, int element, int tilesize)
2486 if (IS_MM_WALL(element))
2488 DrawSizedWall_MM(SX + x * tilesize, SY + y * tilesize,
2489 element, tilesize, el2edimg);
2493 int graphic = el2edimg(element);
2495 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2499 void DrawMiniElement(int x, int y, int element)
2503 graphic = el2edimg(element);
2504 DrawMiniGraphic(x, y, graphic);
2507 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2510 int x = sx + scroll_x, y = sy + scroll_y;
2512 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2513 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2514 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2515 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2517 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2520 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2522 int x = sx + scroll_x, y = sy + scroll_y;
2524 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2525 DrawMiniElement(sx, sy, EL_EMPTY);
2526 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2527 DrawMiniElement(sx, sy, Feld[x][y]);
2529 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2532 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2533 int x, int y, int xsize, int ysize,
2534 int tile_width, int tile_height)
2538 int dst_x = startx + x * tile_width;
2539 int dst_y = starty + y * tile_height;
2540 int width = graphic_info[graphic].width;
2541 int height = graphic_info[graphic].height;
2542 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2543 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2544 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2545 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2546 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2547 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2548 boolean draw_masked = graphic_info[graphic].draw_masked;
2550 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2552 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2554 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2558 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2559 inner_sx + (x - 1) * tile_width % inner_width);
2560 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2561 inner_sy + (y - 1) * tile_height % inner_height);
2564 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2567 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2571 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2572 int x, int y, int xsize, int ysize, int font_nr)
2574 int font_width = getFontWidth(font_nr);
2575 int font_height = getFontHeight(font_nr);
2577 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2578 font_width, font_height);
2581 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2583 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2584 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2585 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2586 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2587 boolean no_delay = (tape.warp_forward);
2588 unsigned int anim_delay = 0;
2589 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2590 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2591 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2592 int font_width = getFontWidth(font_nr);
2593 int font_height = getFontHeight(font_nr);
2594 int max_xsize = level.envelope[envelope_nr].xsize;
2595 int max_ysize = level.envelope[envelope_nr].ysize;
2596 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2597 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2598 int xend = max_xsize;
2599 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2600 int xstep = (xstart < xend ? 1 : 0);
2601 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2603 int end = MAX(xend - xstart, yend - ystart);
2606 for (i = start; i <= end; i++)
2608 int last_frame = end; // last frame of this "for" loop
2609 int x = xstart + i * xstep;
2610 int y = ystart + i * ystep;
2611 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2612 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2613 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2614 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2617 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2619 BlitScreenToBitmap(backbuffer);
2621 SetDrawtoField(DRAW_TO_BACKBUFFER);
2623 for (yy = 0; yy < ysize; yy++)
2624 for (xx = 0; xx < xsize; xx++)
2625 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2627 DrawTextBuffer(sx + font_width, sy + font_height,
2628 level.envelope[envelope_nr].text, font_nr, max_xsize,
2629 xsize - 2, ysize - 2, 0, mask_mode,
2630 level.envelope[envelope_nr].autowrap,
2631 level.envelope[envelope_nr].centered, FALSE);
2633 redraw_mask |= REDRAW_FIELD;
2636 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2640 void ShowEnvelope(int envelope_nr)
2642 int element = EL_ENVELOPE_1 + envelope_nr;
2643 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2644 int sound_opening = element_info[element].sound[ACTION_OPENING];
2645 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2646 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2647 boolean no_delay = (tape.warp_forward);
2648 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2649 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2650 int anim_mode = graphic_info[graphic].anim_mode;
2651 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2652 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2654 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2656 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2658 if (anim_mode == ANIM_DEFAULT)
2659 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2661 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2664 Delay(wait_delay_value);
2666 WaitForEventToContinue();
2668 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2670 if (anim_mode != ANIM_NONE)
2671 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2673 if (anim_mode == ANIM_DEFAULT)
2674 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2676 game.envelope_active = FALSE;
2678 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2680 redraw_mask |= REDRAW_FIELD;
2684 static void setRequestBasePosition(int *x, int *y)
2686 int sx_base, sy_base;
2688 if (request.x != -1)
2689 sx_base = request.x;
2690 else if (request.align == ALIGN_LEFT)
2692 else if (request.align == ALIGN_RIGHT)
2693 sx_base = SX + SXSIZE;
2695 sx_base = SX + SXSIZE / 2;
2697 if (request.y != -1)
2698 sy_base = request.y;
2699 else if (request.valign == VALIGN_TOP)
2701 else if (request.valign == VALIGN_BOTTOM)
2702 sy_base = SY + SYSIZE;
2704 sy_base = SY + SYSIZE / 2;
2710 static void setRequestPositionExt(int *x, int *y, int width, int height,
2711 boolean add_border_size)
2713 int border_size = request.border_size;
2714 int sx_base, sy_base;
2717 setRequestBasePosition(&sx_base, &sy_base);
2719 if (request.align == ALIGN_LEFT)
2721 else if (request.align == ALIGN_RIGHT)
2722 sx = sx_base - width;
2724 sx = sx_base - width / 2;
2726 if (request.valign == VALIGN_TOP)
2728 else if (request.valign == VALIGN_BOTTOM)
2729 sy = sy_base - height;
2731 sy = sy_base - height / 2;
2733 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2734 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2736 if (add_border_size)
2746 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2748 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2751 void DrawEnvelopeRequest(char *text)
2753 char *text_final = text;
2754 char *text_door_style = NULL;
2755 int graphic = IMG_BACKGROUND_REQUEST;
2756 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2757 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2758 int font_nr = FONT_REQUEST;
2759 int font_width = getFontWidth(font_nr);
2760 int font_height = getFontHeight(font_nr);
2761 int border_size = request.border_size;
2762 int line_spacing = request.line_spacing;
2763 int line_height = font_height + line_spacing;
2764 int max_text_width = request.width - 2 * border_size;
2765 int max_text_height = request.height - 2 * border_size;
2766 int line_length = max_text_width / font_width;
2767 int max_lines = max_text_height / line_height;
2768 int text_width = line_length * font_width;
2769 int width = request.width;
2770 int height = request.height;
2771 int tile_size = MAX(request.step_offset, 1);
2772 int x_steps = width / tile_size;
2773 int y_steps = height / tile_size;
2774 int sx_offset = border_size;
2775 int sy_offset = border_size;
2779 if (request.centered)
2780 sx_offset = (request.width - text_width) / 2;
2782 if (request.wrap_single_words && !request.autowrap)
2784 char *src_text_ptr, *dst_text_ptr;
2786 text_door_style = checked_malloc(2 * strlen(text) + 1);
2788 src_text_ptr = text;
2789 dst_text_ptr = text_door_style;
2791 while (*src_text_ptr)
2793 if (*src_text_ptr == ' ' ||
2794 *src_text_ptr == '?' ||
2795 *src_text_ptr == '!')
2796 *dst_text_ptr++ = '\n';
2798 if (*src_text_ptr != ' ')
2799 *dst_text_ptr++ = *src_text_ptr;
2804 *dst_text_ptr = '\0';
2806 text_final = text_door_style;
2809 setRequestPosition(&sx, &sy, FALSE);
2811 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2813 for (y = 0; y < y_steps; y++)
2814 for (x = 0; x < x_steps; x++)
2815 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2816 x, y, x_steps, y_steps,
2817 tile_size, tile_size);
2819 /* force DOOR font inside door area */
2820 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2822 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2823 line_length, -1, max_lines, line_spacing, mask_mode,
2824 request.autowrap, request.centered, FALSE);
2828 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2829 RedrawGadget(tool_gadget[i]);
2831 // store readily prepared envelope request for later use when animating
2832 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2834 if (text_door_style)
2835 free(text_door_style);
2838 void AnimateEnvelopeRequest(int anim_mode, int action)
2840 int graphic = IMG_BACKGROUND_REQUEST;
2841 boolean draw_masked = graphic_info[graphic].draw_masked;
2842 int delay_value_normal = request.step_delay;
2843 int delay_value_fast = delay_value_normal / 2;
2844 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2845 boolean no_delay = (tape.warp_forward);
2846 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2847 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
2848 unsigned int anim_delay = 0;
2850 int tile_size = MAX(request.step_offset, 1);
2851 int max_xsize = request.width / tile_size;
2852 int max_ysize = request.height / tile_size;
2853 int max_xsize_inner = max_xsize - 2;
2854 int max_ysize_inner = max_ysize - 2;
2856 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2857 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2858 int xend = max_xsize_inner;
2859 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2860 int xstep = (xstart < xend ? 1 : 0);
2861 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2863 int end = MAX(xend - xstart, yend - ystart);
2866 if (setup.quick_doors)
2873 for (i = start; i <= end; i++)
2875 int last_frame = end; // last frame of this "for" loop
2876 int x = xstart + i * xstep;
2877 int y = ystart + i * ystep;
2878 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2879 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2880 int xsize_size_left = (xsize - 1) * tile_size;
2881 int ysize_size_top = (ysize - 1) * tile_size;
2882 int max_xsize_pos = (max_xsize - 1) * tile_size;
2883 int max_ysize_pos = (max_ysize - 1) * tile_size;
2884 int width = xsize * tile_size;
2885 int height = ysize * tile_size;
2890 setRequestPosition(&src_x, &src_y, FALSE);
2891 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2893 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2895 for (yy = 0; yy < 2; yy++)
2897 for (xx = 0; xx < 2; xx++)
2899 int src_xx = src_x + xx * max_xsize_pos;
2900 int src_yy = src_y + yy * max_ysize_pos;
2901 int dst_xx = dst_x + xx * xsize_size_left;
2902 int dst_yy = dst_y + yy * ysize_size_top;
2903 int xx_size = (xx ? tile_size : xsize_size_left);
2904 int yy_size = (yy ? tile_size : ysize_size_top);
2907 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
2908 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2910 BlitBitmap(bitmap_db_store_2, backbuffer,
2911 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2915 redraw_mask |= REDRAW_FIELD;
2919 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2923 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2925 int graphic = IMG_BACKGROUND_REQUEST;
2926 int sound_opening = SND_REQUEST_OPENING;
2927 int sound_closing = SND_REQUEST_CLOSING;
2928 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2929 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2930 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2931 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2932 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2934 if (game_status == GAME_MODE_PLAYING)
2935 BlitScreenToBitmap(backbuffer);
2937 SetDrawtoField(DRAW_TO_BACKBUFFER);
2939 // SetDrawBackgroundMask(REDRAW_NONE);
2941 if (action == ACTION_OPENING)
2943 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2945 if (req_state & REQ_ASK)
2947 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2948 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2950 else if (req_state & REQ_CONFIRM)
2952 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2954 else if (req_state & REQ_PLAYER)
2956 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2957 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2958 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2959 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2962 DrawEnvelopeRequest(text);
2965 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2967 if (action == ACTION_OPENING)
2969 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2971 if (anim_mode == ANIM_DEFAULT)
2972 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2974 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2978 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2980 if (anim_mode != ANIM_NONE)
2981 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2983 if (anim_mode == ANIM_DEFAULT)
2984 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2987 game.envelope_active = FALSE;
2989 if (action == ACTION_CLOSING)
2990 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2992 // SetDrawBackgroundMask(last_draw_background_mask);
2994 redraw_mask |= REDRAW_FIELD;
2998 if (action == ACTION_CLOSING &&
2999 game_status == GAME_MODE_PLAYING &&
3000 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3001 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3004 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3006 if (IS_MM_WALL(element))
3008 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3014 int graphic = el2preimg(element);
3016 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3017 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3022 void DrawLevel(int draw_background_mask)
3026 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3027 SetDrawBackgroundMask(draw_background_mask);
3031 for (x = BX1; x <= BX2; x++)
3032 for (y = BY1; y <= BY2; y++)
3033 DrawScreenField(x, y);
3035 redraw_mask |= REDRAW_FIELD;
3038 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3043 for (x = 0; x < size_x; x++)
3044 for (y = 0; y < size_y; y++)
3045 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3047 redraw_mask |= REDRAW_FIELD;
3050 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3054 for (x = 0; x < size_x; x++)
3055 for (y = 0; y < size_y; y++)
3056 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3058 redraw_mask |= REDRAW_FIELD;
3061 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3063 boolean show_level_border = (BorderElement != EL_EMPTY);
3064 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3065 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3066 int tile_size = preview.tile_size;
3067 int preview_width = preview.xsize * tile_size;
3068 int preview_height = preview.ysize * tile_size;
3069 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3070 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3071 int real_preview_width = real_preview_xsize * tile_size;
3072 int real_preview_height = real_preview_ysize * tile_size;
3073 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3074 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3077 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3080 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3082 dst_x += (preview_width - real_preview_width) / 2;
3083 dst_y += (preview_height - real_preview_height) / 2;
3085 for (x = 0; x < real_preview_xsize; x++)
3087 for (y = 0; y < real_preview_ysize; y++)
3089 int lx = from_x + x + (show_level_border ? -1 : 0);
3090 int ly = from_y + y + (show_level_border ? -1 : 0);
3091 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3092 getBorderElement(lx, ly));
3094 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3095 element, tile_size);
3099 redraw_mask |= REDRAW_FIELD;
3102 #define MICROLABEL_EMPTY 0
3103 #define MICROLABEL_LEVEL_NAME 1
3104 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3105 #define MICROLABEL_LEVEL_AUTHOR 3
3106 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3107 #define MICROLABEL_IMPORTED_FROM 5
3108 #define MICROLABEL_IMPORTED_BY_HEAD 6
3109 #define MICROLABEL_IMPORTED_BY 7
3111 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3113 int max_text_width = SXSIZE;
3114 int font_width = getFontWidth(font_nr);
3116 if (pos->align == ALIGN_CENTER)
3117 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3118 else if (pos->align == ALIGN_RIGHT)
3119 max_text_width = pos->x;
3121 max_text_width = SXSIZE - pos->x;
3123 return max_text_width / font_width;
3126 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3128 char label_text[MAX_OUTPUT_LINESIZE + 1];
3129 int max_len_label_text;
3130 int font_nr = pos->font;
3133 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3136 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3137 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3138 mode == MICROLABEL_IMPORTED_BY_HEAD)
3139 font_nr = pos->font_alt;
3141 max_len_label_text = getMaxTextLength(pos, font_nr);
3143 if (pos->size != -1)
3144 max_len_label_text = pos->size;
3146 for (i = 0; i < max_len_label_text; i++)
3147 label_text[i] = ' ';
3148 label_text[max_len_label_text] = '\0';
3150 if (strlen(label_text) > 0)
3151 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3154 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3155 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3156 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3157 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3158 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3159 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3160 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3161 max_len_label_text);
3162 label_text[max_len_label_text] = '\0';
3164 if (strlen(label_text) > 0)
3165 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3167 redraw_mask |= REDRAW_FIELD;
3170 static void DrawPreviewLevelLabel(int mode)
3172 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3175 static void DrawPreviewLevelInfo(int mode)
3177 if (mode == MICROLABEL_LEVEL_NAME)
3178 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3179 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3180 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3183 static void DrawPreviewLevelExt(boolean restart)
3185 static unsigned int scroll_delay = 0;
3186 static unsigned int label_delay = 0;
3187 static int from_x, from_y, scroll_direction;
3188 static int label_state, label_counter;
3189 unsigned int scroll_delay_value = preview.step_delay;
3190 boolean show_level_border = (BorderElement != EL_EMPTY);
3191 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3192 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3199 if (preview.anim_mode == ANIM_CENTERED)
3201 if (level_xsize > preview.xsize)
3202 from_x = (level_xsize - preview.xsize) / 2;
3203 if (level_ysize > preview.ysize)
3204 from_y = (level_ysize - preview.ysize) / 2;
3207 from_x += preview.xoffset;
3208 from_y += preview.yoffset;
3210 scroll_direction = MV_RIGHT;
3214 DrawPreviewLevelPlayfield(from_x, from_y);
3215 DrawPreviewLevelLabel(label_state);
3217 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3218 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3220 /* initialize delay counters */
3221 DelayReached(&scroll_delay, 0);
3222 DelayReached(&label_delay, 0);
3224 if (leveldir_current->name)
3226 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3227 char label_text[MAX_OUTPUT_LINESIZE + 1];
3228 int font_nr = pos->font;
3229 int max_len_label_text = getMaxTextLength(pos, font_nr);
3231 if (pos->size != -1)
3232 max_len_label_text = pos->size;
3234 strncpy(label_text, leveldir_current->name, max_len_label_text);
3235 label_text[max_len_label_text] = '\0';
3237 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3238 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3244 /* scroll preview level, if needed */
3245 if (preview.anim_mode != ANIM_NONE &&
3246 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3247 DelayReached(&scroll_delay, scroll_delay_value))
3249 switch (scroll_direction)
3254 from_x -= preview.step_offset;
3255 from_x = (from_x < 0 ? 0 : from_x);
3258 scroll_direction = MV_UP;
3262 if (from_x < level_xsize - preview.xsize)
3264 from_x += preview.step_offset;
3265 from_x = (from_x > level_xsize - preview.xsize ?
3266 level_xsize - preview.xsize : from_x);
3269 scroll_direction = MV_DOWN;
3275 from_y -= preview.step_offset;
3276 from_y = (from_y < 0 ? 0 : from_y);
3279 scroll_direction = MV_RIGHT;
3283 if (from_y < level_ysize - preview.ysize)
3285 from_y += preview.step_offset;
3286 from_y = (from_y > level_ysize - preview.ysize ?
3287 level_ysize - preview.ysize : from_y);
3290 scroll_direction = MV_LEFT;
3297 DrawPreviewLevelPlayfield(from_x, from_y);
3300 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
3301 /* redraw micro level label, if needed */
3302 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3303 !strEqual(level.author, ANONYMOUS_NAME) &&
3304 !strEqual(level.author, leveldir_current->name) &&
3305 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3307 int max_label_counter = 23;
3309 if (leveldir_current->imported_from != NULL &&
3310 strlen(leveldir_current->imported_from) > 0)
3311 max_label_counter += 14;
3312 if (leveldir_current->imported_by != NULL &&
3313 strlen(leveldir_current->imported_by) > 0)
3314 max_label_counter += 14;
3316 label_counter = (label_counter + 1) % max_label_counter;
3317 label_state = (label_counter >= 0 && label_counter <= 7 ?
3318 MICROLABEL_LEVEL_NAME :
3319 label_counter >= 9 && label_counter <= 12 ?
3320 MICROLABEL_LEVEL_AUTHOR_HEAD :
3321 label_counter >= 14 && label_counter <= 21 ?
3322 MICROLABEL_LEVEL_AUTHOR :
3323 label_counter >= 23 && label_counter <= 26 ?
3324 MICROLABEL_IMPORTED_FROM_HEAD :
3325 label_counter >= 28 && label_counter <= 35 ?
3326 MICROLABEL_IMPORTED_FROM :
3327 label_counter >= 37 && label_counter <= 40 ?
3328 MICROLABEL_IMPORTED_BY_HEAD :
3329 label_counter >= 42 && label_counter <= 49 ?
3330 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3332 if (leveldir_current->imported_from == NULL &&
3333 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3334 label_state == MICROLABEL_IMPORTED_FROM))
3335 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3336 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3338 DrawPreviewLevelLabel(label_state);
3342 void DrawPreviewLevelInitial()
3344 DrawPreviewLevelExt(TRUE);
3347 void DrawPreviewLevelAnimation()
3349 DrawPreviewLevelExt(FALSE);
3352 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3353 int graphic, int sync_frame,
3356 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3358 if (mask_mode == USE_MASKING)
3359 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3361 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3364 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3365 int graphic, int sync_frame, int mask_mode)
3367 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3369 if (mask_mode == USE_MASKING)
3370 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3372 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3375 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3377 int lx = LEVELX(x), ly = LEVELY(y);
3379 if (!IN_SCR_FIELD(x, y))
3382 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3383 graphic, GfxFrame[lx][ly], NO_MASKING);
3385 MarkTileDirty(x, y);
3388 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3390 int lx = LEVELX(x), ly = LEVELY(y);
3392 if (!IN_SCR_FIELD(x, y))
3395 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3396 graphic, GfxFrame[lx][ly], NO_MASKING);
3397 MarkTileDirty(x, y);
3400 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3402 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3405 void DrawLevelElementAnimation(int x, int y, int element)
3407 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3409 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3412 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3414 int sx = SCREENX(x), sy = SCREENY(y);
3416 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3419 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3422 DrawGraphicAnimation(sx, sy, graphic);
3425 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3426 DrawLevelFieldCrumbled(x, y);
3428 if (GFX_CRUMBLED(Feld[x][y]))
3429 DrawLevelFieldCrumbled(x, y);
3433 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3435 int sx = SCREENX(x), sy = SCREENY(y);
3438 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3441 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3443 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3446 DrawGraphicAnimation(sx, sy, graphic);
3448 if (GFX_CRUMBLED(element))
3449 DrawLevelFieldCrumbled(x, y);
3452 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3454 if (player->use_murphy)
3456 /* this works only because currently only one player can be "murphy" ... */
3457 static int last_horizontal_dir = MV_LEFT;
3458 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3460 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3461 last_horizontal_dir = move_dir;
3463 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3465 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3467 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3473 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3476 static boolean equalGraphics(int graphic1, int graphic2)
3478 struct GraphicInfo *g1 = &graphic_info[graphic1];
3479 struct GraphicInfo *g2 = &graphic_info[graphic2];
3481 return (g1->bitmap == g2->bitmap &&
3482 g1->src_x == g2->src_x &&
3483 g1->src_y == g2->src_y &&
3484 g1->anim_frames == g2->anim_frames &&
3485 g1->anim_delay == g2->anim_delay &&
3486 g1->anim_mode == g2->anim_mode);
3489 void DrawAllPlayers()
3493 for (i = 0; i < MAX_PLAYERS; i++)
3494 if (stored_player[i].active)
3495 DrawPlayer(&stored_player[i]);
3498 void DrawPlayerField(int x, int y)
3500 if (!IS_PLAYER(x, y))
3503 DrawPlayer(PLAYERINFO(x, y));
3506 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3508 void DrawPlayer(struct PlayerInfo *player)
3510 int jx = player->jx;
3511 int jy = player->jy;
3512 int move_dir = player->MovDir;
3513 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3514 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3515 int last_jx = (player->is_moving ? jx - dx : jx);
3516 int last_jy = (player->is_moving ? jy - dy : jy);
3517 int next_jx = jx + dx;
3518 int next_jy = jy + dy;
3519 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3520 boolean player_is_opaque = FALSE;
3521 int sx = SCREENX(jx), sy = SCREENY(jy);
3522 int sxx = 0, syy = 0;
3523 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3525 int action = ACTION_DEFAULT;
3526 int last_player_graphic = getPlayerGraphic(player, move_dir);
3527 int last_player_frame = player->Frame;
3530 /* GfxElement[][] is set to the element the player is digging or collecting;
3531 remove also for off-screen player if the player is not moving anymore */
3532 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3533 GfxElement[jx][jy] = EL_UNDEFINED;
3535 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3539 if (!IN_LEV_FIELD(jx, jy))
3541 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3542 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3543 printf("DrawPlayerField(): This should never happen!\n");
3548 if (element == EL_EXPLOSION)
3551 action = (player->is_pushing ? ACTION_PUSHING :
3552 player->is_digging ? ACTION_DIGGING :
3553 player->is_collecting ? ACTION_COLLECTING :
3554 player->is_moving ? ACTION_MOVING :
3555 player->is_snapping ? ACTION_SNAPPING :
3556 player->is_dropping ? ACTION_DROPPING :
3557 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3559 if (player->is_waiting)
3560 move_dir = player->dir_waiting;
3562 InitPlayerGfxAnimation(player, action, move_dir);
3564 /* ----------------------------------------------------------------------- */
3565 /* draw things in the field the player is leaving, if needed */
3566 /* ----------------------------------------------------------------------- */
3568 if (player->is_moving)
3570 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3572 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3574 if (last_element == EL_DYNAMITE_ACTIVE ||
3575 last_element == EL_EM_DYNAMITE_ACTIVE ||
3576 last_element == EL_SP_DISK_RED_ACTIVE)
3577 DrawDynamite(last_jx, last_jy);
3579 DrawLevelFieldThruMask(last_jx, last_jy);
3581 else if (last_element == EL_DYNAMITE_ACTIVE ||
3582 last_element == EL_EM_DYNAMITE_ACTIVE ||
3583 last_element == EL_SP_DISK_RED_ACTIVE)
3584 DrawDynamite(last_jx, last_jy);
3586 /* !!! this is not enough to prevent flickering of players which are
3587 moving next to each others without a free tile between them -- this
3588 can only be solved by drawing all players layer by layer (first the
3589 background, then the foreground etc.) !!! => TODO */
3590 else if (!IS_PLAYER(last_jx, last_jy))
3591 DrawLevelField(last_jx, last_jy);
3594 DrawLevelField(last_jx, last_jy);
3597 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3598 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3601 if (!IN_SCR_FIELD(sx, sy))
3604 /* ----------------------------------------------------------------------- */
3605 /* draw things behind the player, if needed */
3606 /* ----------------------------------------------------------------------- */
3609 DrawLevelElement(jx, jy, Back[jx][jy]);
3610 else if (IS_ACTIVE_BOMB(element))
3611 DrawLevelElement(jx, jy, EL_EMPTY);
3614 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3616 int old_element = GfxElement[jx][jy];
3617 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3618 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3620 if (GFX_CRUMBLED(old_element))
3621 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3623 DrawGraphic(sx, sy, old_graphic, frame);
3625 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3626 player_is_opaque = TRUE;
3630 GfxElement[jx][jy] = EL_UNDEFINED;
3632 /* make sure that pushed elements are drawn with correct frame rate */
3633 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3635 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3636 GfxFrame[jx][jy] = player->StepFrame;
3638 DrawLevelField(jx, jy);
3642 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3643 /* ----------------------------------------------------------------------- */
3644 /* draw player himself */
3645 /* ----------------------------------------------------------------------- */
3647 graphic = getPlayerGraphic(player, move_dir);
3649 /* in the case of changed player action or direction, prevent the current
3650 animation frame from being restarted for identical animations */
3651 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3652 player->Frame = last_player_frame;
3654 frame = getGraphicAnimationFrame(graphic, player->Frame);
3658 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3659 sxx = player->GfxPos;
3661 syy = player->GfxPos;
3664 if (player_is_opaque)
3665 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3667 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3669 if (SHIELD_ON(player))
3671 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3672 IMG_SHIELD_NORMAL_ACTIVE);
3673 int frame = getGraphicAnimationFrame(graphic, -1);
3675 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3679 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3682 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3683 sxx = player->GfxPos;
3685 syy = player->GfxPos;
3689 /* ----------------------------------------------------------------------- */
3690 /* draw things the player is pushing, if needed */
3691 /* ----------------------------------------------------------------------- */
3693 if (player->is_pushing && player->is_moving)
3695 int px = SCREENX(jx), py = SCREENY(jy);
3696 int pxx = (TILEX - ABS(sxx)) * dx;
3697 int pyy = (TILEY - ABS(syy)) * dy;
3698 int gfx_frame = GfxFrame[jx][jy];
3704 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3706 element = Feld[next_jx][next_jy];
3707 gfx_frame = GfxFrame[next_jx][next_jy];
3710 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3712 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3713 frame = getGraphicAnimationFrame(graphic, sync_frame);
3715 /* draw background element under pushed element (like the Sokoban field) */
3716 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3718 /* this allows transparent pushing animation over non-black background */
3721 DrawLevelElement(jx, jy, Back[jx][jy]);
3723 DrawLevelElement(jx, jy, EL_EMPTY);
3725 if (Back[next_jx][next_jy])
3726 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3728 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3730 else if (Back[next_jx][next_jy])
3731 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3734 /* do not draw (EM style) pushing animation when pushing is finished */
3735 /* (two-tile animations usually do not contain start and end frame) */
3736 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3737 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3739 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3741 /* masked drawing is needed for EMC style (double) movement graphics */
3742 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3743 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3747 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3748 /* ----------------------------------------------------------------------- */
3749 /* draw player himself */
3750 /* ----------------------------------------------------------------------- */
3752 graphic = getPlayerGraphic(player, move_dir);
3754 /* in the case of changed player action or direction, prevent the current
3755 animation frame from being restarted for identical animations */
3756 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3757 player->Frame = last_player_frame;
3759 frame = getGraphicAnimationFrame(graphic, player->Frame);
3763 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3764 sxx = player->GfxPos;
3766 syy = player->GfxPos;
3769 if (player_is_opaque)
3770 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3772 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3774 if (SHIELD_ON(player))
3776 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3777 IMG_SHIELD_NORMAL_ACTIVE);
3778 int frame = getGraphicAnimationFrame(graphic, -1);
3780 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3784 /* ----------------------------------------------------------------------- */
3785 /* draw things in front of player (active dynamite or dynabombs) */
3786 /* ----------------------------------------------------------------------- */
3788 if (IS_ACTIVE_BOMB(element))
3790 graphic = el2img(element);
3791 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3793 if (game.emulation == EMU_SUPAPLEX)
3794 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3796 DrawGraphicThruMask(sx, sy, graphic, frame);
3799 if (player_is_moving && last_element == EL_EXPLOSION)
3801 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3802 GfxElement[last_jx][last_jy] : EL_EMPTY);
3803 int graphic = el_act2img(element, ACTION_EXPLODING);
3804 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3805 int phase = ExplodePhase[last_jx][last_jy] - 1;
3806 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3809 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3812 /* ----------------------------------------------------------------------- */
3813 /* draw elements the player is just walking/passing through/under */
3814 /* ----------------------------------------------------------------------- */
3816 if (player_is_moving)
3818 /* handle the field the player is leaving ... */
3819 if (IS_ACCESSIBLE_INSIDE(last_element))
3820 DrawLevelField(last_jx, last_jy);
3821 else if (IS_ACCESSIBLE_UNDER(last_element))
3822 DrawLevelFieldThruMask(last_jx, last_jy);
3825 /* do not redraw accessible elements if the player is just pushing them */
3826 if (!player_is_moving || !player->is_pushing)
3828 /* ... and the field the player is entering */
3829 if (IS_ACCESSIBLE_INSIDE(element))
3830 DrawLevelField(jx, jy);
3831 else if (IS_ACCESSIBLE_UNDER(element))
3832 DrawLevelFieldThruMask(jx, jy);
3835 MarkTileDirty(sx, sy);
3838 /* ------------------------------------------------------------------------- */
3840 void WaitForEventToContinue()
3842 boolean still_wait = TRUE;
3844 if (program.headless)
3847 /* simulate releasing mouse button over last gadget, if still pressed */
3849 HandleGadgets(-1, -1, 0);
3851 button_status = MB_RELEASED;
3859 if (NextValidEvent(&event))
3863 case EVENT_BUTTONPRESS:
3864 case EVENT_KEYPRESS:
3865 #if defined(TARGET_SDL2)
3866 case SDL_CONTROLLERBUTTONDOWN:
3868 case SDL_JOYBUTTONDOWN:
3872 case EVENT_KEYRELEASE:
3873 ClearPlayerAction();
3877 HandleOtherEvents(&event);
3881 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3890 #define MAX_REQUEST_LINES 13
3891 #define MAX_REQUEST_LINE_FONT1_LEN 7
3892 #define MAX_REQUEST_LINE_FONT2_LEN 10
3894 static int RequestHandleEvents(unsigned int req_state)
3896 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3897 local_player->LevelSolved_GameEnd);
3898 int width = request.width;
3899 int height = request.height;
3903 setRequestPosition(&sx, &sy, FALSE);
3905 button_status = MB_RELEASED;
3907 request_gadget_id = -1;
3914 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3916 HandleGameActions();
3918 SetDrawtoField(DRAW_TO_BACKBUFFER);
3920 if (global.use_envelope_request)
3922 /* copy current state of request area to middle of playfield area */
3923 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
3931 while (NextValidEvent(&event))
3935 case EVENT_BUTTONPRESS:
3936 case EVENT_BUTTONRELEASE:
3937 case EVENT_MOTIONNOTIFY:
3941 if (event.type == EVENT_MOTIONNOTIFY)
3946 motion_status = TRUE;
3947 mx = ((MotionEvent *) &event)->x;
3948 my = ((MotionEvent *) &event)->y;
3952 motion_status = FALSE;
3953 mx = ((ButtonEvent *) &event)->x;
3954 my = ((ButtonEvent *) &event)->y;
3955 if (event.type == EVENT_BUTTONPRESS)
3956 button_status = ((ButtonEvent *) &event)->button;
3958 button_status = MB_RELEASED;
3961 /* this sets 'request_gadget_id' */
3962 HandleGadgets(mx, my, button_status);
3964 switch (request_gadget_id)
3966 case TOOL_CTRL_ID_YES:
3969 case TOOL_CTRL_ID_NO:
3972 case TOOL_CTRL_ID_CONFIRM:
3973 result = TRUE | FALSE;
3976 case TOOL_CTRL_ID_PLAYER_1:
3979 case TOOL_CTRL_ID_PLAYER_2:
3982 case TOOL_CTRL_ID_PLAYER_3:
3985 case TOOL_CTRL_ID_PLAYER_4:
3996 #if defined(TARGET_SDL2)
3997 case SDL_WINDOWEVENT:
3998 HandleWindowEvent((WindowEvent *) &event);
4001 case SDL_APP_WILLENTERBACKGROUND:
4002 case SDL_APP_DIDENTERBACKGROUND:
4003 case SDL_APP_WILLENTERFOREGROUND:
4004 case SDL_APP_DIDENTERFOREGROUND:
4005 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4009 case EVENT_KEYPRESS:
4011 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4016 if (req_state & REQ_CONFIRM)
4021 #if defined(TARGET_SDL2)
4024 #if defined(KSYM_Rewind)
4025 case KSYM_Rewind: /* for Amazon Fire TV remote */
4032 #if defined(TARGET_SDL2)
4034 #if defined(KSYM_FastForward)
4035 case KSYM_FastForward: /* for Amazon Fire TV remote */
4042 HandleKeysDebug(key);
4046 if (req_state & REQ_PLAYER)
4052 case EVENT_KEYRELEASE:
4053 ClearPlayerAction();
4056 #if defined(TARGET_SDL2)
4057 case SDL_CONTROLLERBUTTONDOWN:
4058 switch (event.cbutton.button)
4060 case SDL_CONTROLLER_BUTTON_A:
4061 case SDL_CONTROLLER_BUTTON_X:
4062 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4066 case SDL_CONTROLLER_BUTTON_B:
4067 case SDL_CONTROLLER_BUTTON_Y:
4068 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4069 case SDL_CONTROLLER_BUTTON_BACK:
4074 if (req_state & REQ_PLAYER)
4079 case SDL_CONTROLLERBUTTONUP:
4080 HandleJoystickEvent(&event);
4081 ClearPlayerAction();
4086 HandleOtherEvents(&event);
4091 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4093 int joy = AnyJoystick();
4095 if (joy & JOY_BUTTON_1)
4097 else if (joy & JOY_BUTTON_2)
4103 if (global.use_envelope_request)
4105 /* copy back current state of pressed buttons inside request area */
4106 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4116 static boolean RequestDoor(char *text, unsigned int req_state)
4118 unsigned int old_door_state;
4119 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4120 int font_nr = FONT_TEXT_2;
4125 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4127 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4128 font_nr = FONT_TEXT_1;
4131 if (game_status == GAME_MODE_PLAYING)
4132 BlitScreenToBitmap(backbuffer);
4134 /* disable deactivated drawing when quick-loading level tape recording */
4135 if (tape.playing && tape.deactivate_display)
4136 TapeDeactivateDisplayOff(TRUE);
4138 SetMouseCursor(CURSOR_DEFAULT);
4140 #if defined(NETWORK_AVALIABLE)
4141 /* pause network game while waiting for request to answer */
4142 if (options.network &&
4143 game_status == GAME_MODE_PLAYING &&
4144 req_state & REQUEST_WAIT_FOR_INPUT)
4145 SendToServer_PausePlaying();
4148 old_door_state = GetDoorState();
4150 /* simulate releasing mouse button over last gadget, if still pressed */
4152 HandleGadgets(-1, -1, 0);
4156 /* draw released gadget before proceeding */
4159 if (old_door_state & DOOR_OPEN_1)
4161 CloseDoor(DOOR_CLOSE_1);
4163 /* save old door content */
4164 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4165 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4168 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4169 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4171 /* clear door drawing field */
4172 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4174 /* force DOOR font inside door area */
4175 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4177 /* write text for request */
4178 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4180 char text_line[max_request_line_len + 1];
4186 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4188 tc = *(text_ptr + tx);
4189 // if (!tc || tc == ' ')
4190 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4194 if ((tc == '?' || tc == '!') && tl == 0)
4204 strncpy(text_line, text_ptr, tl);
4207 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4208 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4209 text_line, font_nr);
4211 text_ptr += tl + (tc == ' ' ? 1 : 0);
4212 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4217 if (req_state & REQ_ASK)
4219 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4220 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4222 else if (req_state & REQ_CONFIRM)
4224 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4226 else if (req_state & REQ_PLAYER)
4228 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4229 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4230 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4231 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4234 /* copy request gadgets to door backbuffer */
4235 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4237 OpenDoor(DOOR_OPEN_1);
4239 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4241 if (game_status == GAME_MODE_PLAYING)
4243 SetPanelBackground();
4244 SetDrawBackgroundMask(REDRAW_DOOR_1);
4248 SetDrawBackgroundMask(REDRAW_FIELD);
4254 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4256 // ---------- handle request buttons ----------
4257 result = RequestHandleEvents(req_state);
4261 if (!(req_state & REQ_STAY_OPEN))
4263 CloseDoor(DOOR_CLOSE_1);
4265 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4266 (req_state & REQ_REOPEN))
4267 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4272 if (game_status == GAME_MODE_PLAYING)
4274 SetPanelBackground();
4275 SetDrawBackgroundMask(REDRAW_DOOR_1);
4279 SetDrawBackgroundMask(REDRAW_FIELD);
4282 #if defined(NETWORK_AVALIABLE)
4283 /* continue network game after request */
4284 if (options.network &&
4285 game_status == GAME_MODE_PLAYING &&
4286 req_state & REQUEST_WAIT_FOR_INPUT)
4287 SendToServer_ContinuePlaying();
4290 /* restore deactivated drawing when quick-loading level tape recording */
4291 if (tape.playing && tape.deactivate_display)
4292 TapeDeactivateDisplayOn();
4297 static boolean RequestEnvelope(char *text, unsigned int req_state)
4301 if (game_status == GAME_MODE_PLAYING)
4302 BlitScreenToBitmap(backbuffer);
4304 /* disable deactivated drawing when quick-loading level tape recording */
4305 if (tape.playing && tape.deactivate_display)
4306 TapeDeactivateDisplayOff(TRUE);
4308 SetMouseCursor(CURSOR_DEFAULT);
4310 #if defined(NETWORK_AVALIABLE)
4311 /* pause network game while waiting for request to answer */
4312 if (options.network &&
4313 game_status == GAME_MODE_PLAYING &&
4314 req_state & REQUEST_WAIT_FOR_INPUT)
4315 SendToServer_PausePlaying();
4318 /* simulate releasing mouse button over last gadget, if still pressed */
4320 HandleGadgets(-1, -1, 0);
4324 // (replace with setting corresponding request background)
4325 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4326 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4328 /* clear door drawing field */
4329 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4331 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4333 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4335 if (game_status == GAME_MODE_PLAYING)
4337 SetPanelBackground();
4338 SetDrawBackgroundMask(REDRAW_DOOR_1);
4342 SetDrawBackgroundMask(REDRAW_FIELD);
4348 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4350 // ---------- handle request buttons ----------
4351 result = RequestHandleEvents(req_state);
4355 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4359 if (game_status == GAME_MODE_PLAYING)
4361 SetPanelBackground();
4362 SetDrawBackgroundMask(REDRAW_DOOR_1);
4366 SetDrawBackgroundMask(REDRAW_FIELD);
4369 #if defined(NETWORK_AVALIABLE)
4370 /* continue network game after request */
4371 if (options.network &&
4372 game_status == GAME_MODE_PLAYING &&
4373 req_state & REQUEST_WAIT_FOR_INPUT)
4374 SendToServer_ContinuePlaying();
4377 /* restore deactivated drawing when quick-loading level tape recording */
4378 if (tape.playing && tape.deactivate_display)
4379 TapeDeactivateDisplayOn();
4384 boolean Request(char *text, unsigned int req_state)
4386 boolean overlay_active = GetOverlayActive();
4389 SetOverlayActive(FALSE);
4391 if (global.use_envelope_request)
4392 result = RequestEnvelope(text, req_state);
4394 result = RequestDoor(text, req_state);
4396 SetOverlayActive(overlay_active);
4401 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4403 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4404 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4407 if (dpo1->sort_priority != dpo2->sort_priority)
4408 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4410 compare_result = dpo1->nr - dpo2->nr;
4412 return compare_result;
4415 void InitGraphicCompatibilityInfo_Doors()
4421 struct DoorInfo *door;
4425 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4426 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4428 { -1, -1, -1, NULL }
4430 struct Rect door_rect_list[] =
4432 { DX, DY, DXSIZE, DYSIZE },
4433 { VX, VY, VXSIZE, VYSIZE }
4437 for (i = 0; doors[i].door_token != -1; i++)
4439 int door_token = doors[i].door_token;
4440 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4441 int part_1 = doors[i].part_1;
4442 int part_8 = doors[i].part_8;
4443 int part_2 = part_1 + 1;
4444 int part_3 = part_1 + 2;
4445 struct DoorInfo *door = doors[i].door;
4446 struct Rect *door_rect = &door_rect_list[door_index];
4447 boolean door_gfx_redefined = FALSE;
4449 /* check if any door part graphic definitions have been redefined */
4451 for (j = 0; door_part_controls[j].door_token != -1; j++)
4453 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4454 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4456 if (dpc->door_token == door_token && fi->redefined)
4457 door_gfx_redefined = TRUE;
4460 /* check for old-style door graphic/animation modifications */
4462 if (!door_gfx_redefined)
4464 if (door->anim_mode & ANIM_STATIC_PANEL)
4466 door->panel.step_xoffset = 0;
4467 door->panel.step_yoffset = 0;
4470 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4472 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4473 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4474 int num_door_steps, num_panel_steps;
4476 /* remove door part graphics other than the two default wings */
4478 for (j = 0; door_part_controls[j].door_token != -1; j++)
4480 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4481 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4483 if (dpc->graphic >= part_3 &&
4484 dpc->graphic <= part_8)
4488 /* set graphics and screen positions of the default wings */
4490 g_part_1->width = door_rect->width;
4491 g_part_1->height = door_rect->height;
4492 g_part_2->width = door_rect->width;
4493 g_part_2->height = door_rect->height;
4494 g_part_2->src_x = door_rect->width;
4495 g_part_2->src_y = g_part_1->src_y;
4497 door->part_2.x = door->part_1.x;
4498 door->part_2.y = door->part_1.y;
4500 if (door->width != -1)
4502 g_part_1->width = door->width;
4503 g_part_2->width = door->width;
4505 // special treatment for graphics and screen position of right wing
4506 g_part_2->src_x += door_rect->width - door->width;
4507 door->part_2.x += door_rect->width - door->width;
4510 if (door->height != -1)
4512 g_part_1->height = door->height;
4513 g_part_2->height = door->height;
4515 // special treatment for graphics and screen position of bottom wing
4516 g_part_2->src_y += door_rect->height - door->height;
4517 door->part_2.y += door_rect->height - door->height;
4520 /* set animation delays for the default wings and panels */
4522 door->part_1.step_delay = door->step_delay;
4523 door->part_2.step_delay = door->step_delay;
4524 door->panel.step_delay = door->step_delay;
4526 /* set animation draw order for the default wings */
4528 door->part_1.sort_priority = 2; /* draw left wing over ... */
4529 door->part_2.sort_priority = 1; /* ... right wing */
4531 /* set animation draw offset for the default wings */
4533 if (door->anim_mode & ANIM_HORIZONTAL)
4535 door->part_1.step_xoffset = door->step_offset;
4536 door->part_1.step_yoffset = 0;
4537 door->part_2.step_xoffset = door->step_offset * -1;
4538 door->part_2.step_yoffset = 0;
4540 num_door_steps = g_part_1->width / door->step_offset;
4542 else // ANIM_VERTICAL
4544 door->part_1.step_xoffset = 0;
4545 door->part_1.step_yoffset = door->step_offset;
4546 door->part_2.step_xoffset = 0;
4547 door->part_2.step_yoffset = door->step_offset * -1;
4549 num_door_steps = g_part_1->height / door->step_offset;
4552 /* set animation draw offset for the default panels */
4554 if (door->step_offset > 1)
4556 num_panel_steps = 2 * door_rect->height / door->step_offset;
4557 door->panel.start_step = num_panel_steps - num_door_steps;
4558 door->panel.start_step_closing = door->panel.start_step;
4562 num_panel_steps = door_rect->height / door->step_offset;
4563 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4564 door->panel.start_step_closing = door->panel.start_step;
4565 door->panel.step_delay *= 2;
4576 for (i = 0; door_part_controls[i].door_token != -1; i++)
4578 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4579 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4581 /* initialize "start_step_opening" and "start_step_closing", if needed */
4582 if (dpc->pos->start_step_opening == 0 &&
4583 dpc->pos->start_step_closing == 0)
4585 // dpc->pos->start_step_opening = dpc->pos->start_step;
4586 dpc->pos->start_step_closing = dpc->pos->start_step;
4589 /* fill structure for door part draw order (sorted below) */
4591 dpo->sort_priority = dpc->pos->sort_priority;
4594 /* sort door part controls according to sort_priority and graphic number */
4595 qsort(door_part_order, MAX_DOOR_PARTS,
4596 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4599 unsigned int OpenDoor(unsigned int door_state)
4601 if (door_state & DOOR_COPY_BACK)
4603 if (door_state & DOOR_OPEN_1)
4604 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4605 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4607 if (door_state & DOOR_OPEN_2)
4608 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4609 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4611 door_state &= ~DOOR_COPY_BACK;
4614 return MoveDoor(door_state);
4617 unsigned int CloseDoor(unsigned int door_state)
4619 unsigned int old_door_state = GetDoorState();
4621 if (!(door_state & DOOR_NO_COPY_BACK))
4623 if (old_door_state & DOOR_OPEN_1)
4624 BlitBitmap(backbuffer, bitmap_db_door_1,
4625 DX, DY, DXSIZE, DYSIZE, 0, 0);
4627 if (old_door_state & DOOR_OPEN_2)
4628 BlitBitmap(backbuffer, bitmap_db_door_2,
4629 VX, VY, VXSIZE, VYSIZE, 0, 0);
4631 door_state &= ~DOOR_NO_COPY_BACK;
4634 return MoveDoor(door_state);
4637 unsigned int GetDoorState()
4639 return MoveDoor(DOOR_GET_STATE);
4642 unsigned int SetDoorState(unsigned int door_state)
4644 return MoveDoor(door_state | DOOR_SET_STATE);
4647 int euclid(int a, int b)
4649 return (b ? euclid(b, a % b) : a);
4652 unsigned int MoveDoor(unsigned int door_state)
4654 struct Rect door_rect_list[] =
4656 { DX, DY, DXSIZE, DYSIZE },
4657 { VX, VY, VXSIZE, VYSIZE }
4659 static int door1 = DOOR_CLOSE_1;
4660 static int door2 = DOOR_CLOSE_2;
4661 unsigned int door_delay = 0;
4662 unsigned int door_delay_value;
4665 if (door_state == DOOR_GET_STATE)
4666 return (door1 | door2);
4668 if (door_state & DOOR_SET_STATE)
4670 if (door_state & DOOR_ACTION_1)
4671 door1 = door_state & DOOR_ACTION_1;
4672 if (door_state & DOOR_ACTION_2)
4673 door2 = door_state & DOOR_ACTION_2;
4675 return (door1 | door2);
4678 if (!(door_state & DOOR_FORCE_REDRAW))
4680 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4681 door_state &= ~DOOR_OPEN_1;
4682 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4683 door_state &= ~DOOR_CLOSE_1;
4684 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4685 door_state &= ~DOOR_OPEN_2;
4686 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4687 door_state &= ~DOOR_CLOSE_2;
4690 if (global.autoplay_leveldir)
4692 door_state |= DOOR_NO_DELAY;
4693 door_state &= ~DOOR_CLOSE_ALL;
4696 if (game_status == GAME_MODE_EDITOR)
4697 door_state |= DOOR_NO_DELAY;
4699 if (door_state & DOOR_ACTION)
4701 boolean door_panel_drawn[NUM_DOORS];
4702 boolean panel_has_doors[NUM_DOORS];
4703 boolean door_part_skip[MAX_DOOR_PARTS];
4704 boolean door_part_done[MAX_DOOR_PARTS];
4705 boolean door_part_done_all;
4706 int num_steps[MAX_DOOR_PARTS];
4707 int max_move_delay = 0; // delay for complete animations of all doors
4708 int max_step_delay = 0; // delay (ms) between two animation frames
4709 int num_move_steps = 0; // number of animation steps for all doors
4710 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4711 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4712 int current_move_delay = 0;
4716 for (i = 0; i < NUM_DOORS; i++)
4717 panel_has_doors[i] = FALSE;
4719 for (i = 0; i < MAX_DOOR_PARTS; i++)
4721 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4722 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4723 int door_token = dpc->door_token;
4725 door_part_done[i] = FALSE;
4726 door_part_skip[i] = (!(door_state & door_token) ||
4730 for (i = 0; i < MAX_DOOR_PARTS; i++)
4732 int nr = door_part_order[i].nr;
4733 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4734 struct DoorPartPosInfo *pos = dpc->pos;
4735 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4736 int door_token = dpc->door_token;
4737 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4738 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4739 int step_xoffset = ABS(pos->step_xoffset);
4740 int step_yoffset = ABS(pos->step_yoffset);
4741 int step_delay = pos->step_delay;
4742 int current_door_state = door_state & door_token;
4743 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4744 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4745 boolean part_opening = (is_panel ? door_closing : door_opening);
4746 int start_step = (part_opening ? pos->start_step_opening :
4747 pos->start_step_closing);
4748 float move_xsize = (step_xoffset ? g->width : 0);
4749 float move_ysize = (step_yoffset ? g->height : 0);
4750 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4751 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4752 int move_steps = (move_xsteps && move_ysteps ?
4753 MIN(move_xsteps, move_ysteps) :
4754 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4755 int move_delay = move_steps * step_delay;
4757 if (door_part_skip[nr])
4760 max_move_delay = MAX(max_move_delay, move_delay);
4761 max_step_delay = (max_step_delay == 0 ? step_delay :
4762 euclid(max_step_delay, step_delay));
4763 num_steps[nr] = move_steps;
4767 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4769 panel_has_doors[door_index] = TRUE;
4773 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4775 num_move_steps = max_move_delay / max_step_delay;
4776 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4778 door_delay_value = max_step_delay;
4780 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4782 start = num_move_steps - 1;
4786 /* opening door sound has priority over simultaneously closing door */
4787 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4788 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4789 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4790 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4793 for (k = start; k < num_move_steps; k++)
4795 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4797 door_part_done_all = TRUE;
4799 for (i = 0; i < NUM_DOORS; i++)
4800 door_panel_drawn[i] = FALSE;
4802 for (i = 0; i < MAX_DOOR_PARTS; i++)
4804 int nr = door_part_order[i].nr;
4805 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4806 struct DoorPartPosInfo *pos = dpc->pos;
4807 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4808 int door_token = dpc->door_token;
4809 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4810 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4811 boolean is_panel_and_door_has_closed = FALSE;
4812 struct Rect *door_rect = &door_rect_list[door_index];
4813 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4815 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4816 int current_door_state = door_state & door_token;
4817 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4818 boolean door_closing = !door_opening;
4819 boolean part_opening = (is_panel ? door_closing : door_opening);
4820 boolean part_closing = !part_opening;
4821 int start_step = (part_opening ? pos->start_step_opening :
4822 pos->start_step_closing);
4823 int step_delay = pos->step_delay;
4824 int step_factor = step_delay / max_step_delay;
4825 int k1 = (step_factor ? k / step_factor + 1 : k);
4826 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4827 int kk = MAX(0, k2);
4830 int src_x, src_y, src_xx, src_yy;
4831 int dst_x, dst_y, dst_xx, dst_yy;
4834 if (door_part_skip[nr])
4837 if (!(door_state & door_token))
4845 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4846 int kk_door = MAX(0, k2_door);
4847 int sync_frame = kk_door * door_delay_value;
4848 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4850 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
4851 &g_src_x, &g_src_y);
4856 if (!door_panel_drawn[door_index])
4858 ClearRectangle(drawto, door_rect->x, door_rect->y,
4859 door_rect->width, door_rect->height);
4861 door_panel_drawn[door_index] = TRUE;
4864 // draw opening or closing door parts
4866 if (pos->step_xoffset < 0) // door part on right side
4869 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4872 if (dst_xx + width > door_rect->width)
4873 width = door_rect->width - dst_xx;
4875 else // door part on left side
4878 dst_xx = pos->x - kk * pos->step_xoffset;
4882 src_xx = ABS(dst_xx);
4886 width = g->width - src_xx;
4888 if (width > door_rect->width)
4889 width = door_rect->width;
4891 // printf("::: k == %d [%d] \n", k, start_step);
4894 if (pos->step_yoffset < 0) // door part on bottom side
4897 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4900 if (dst_yy + height > door_rect->height)
4901 height = door_rect->height - dst_yy;
4903 else // door part on top side
4906 dst_yy = pos->y - kk * pos->step_yoffset;
4910 src_yy = ABS(dst_yy);
4914 height = g->height - src_yy;
4917 src_x = g_src_x + src_xx;
4918 src_y = g_src_y + src_yy;
4920 dst_x = door_rect->x + dst_xx;
4921 dst_y = door_rect->y + dst_yy;
4923 is_panel_and_door_has_closed =
4926 panel_has_doors[door_index] &&
4927 k >= num_move_steps_doors_only - 1);
4929 if (width >= 0 && width <= g->width &&
4930 height >= 0 && height <= g->height &&
4931 !is_panel_and_door_has_closed)
4933 if (is_panel || !pos->draw_masked)
4934 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4937 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4941 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4943 if ((part_opening && (width < 0 || height < 0)) ||
4944 (part_closing && (width >= g->width && height >= g->height)))
4945 door_part_done[nr] = TRUE;
4947 // continue door part animations, but not panel after door has closed
4948 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4949 door_part_done_all = FALSE;
4952 if (!(door_state & DOOR_NO_DELAY))
4956 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4958 current_move_delay += max_step_delay;
4960 /* prevent OS (Windows) from complaining about program not responding */
4964 if (door_part_done_all)
4969 if (door_state & DOOR_ACTION_1)
4970 door1 = door_state & DOOR_ACTION_1;
4971 if (door_state & DOOR_ACTION_2)
4972 door2 = door_state & DOOR_ACTION_2;
4974 // draw masked border over door area
4975 DrawMaskedBorder(REDRAW_DOOR_1);
4976 DrawMaskedBorder(REDRAW_DOOR_2);
4978 return (door1 | door2);
4981 static boolean useSpecialEditorDoor()
4983 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4984 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4986 // do not draw special editor door if editor border defined or redefined
4987 if (graphic_info[graphic].bitmap != NULL || redefined)
4990 // do not draw special editor door if global border defined to be empty
4991 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4994 // do not draw special editor door if viewport definitions do not match
4998 EY + EYSIZE != VY + VYSIZE)
5004 void DrawSpecialEditorDoor()
5006 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5007 int top_border_width = gfx1->width;
5008 int top_border_height = gfx1->height;
5009 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5010 int ex = EX - outer_border;
5011 int ey = EY - outer_border;
5012 int vy = VY - outer_border;
5013 int exsize = EXSIZE + 2 * outer_border;
5015 if (!useSpecialEditorDoor())
5018 /* draw bigger level editor toolbox window */
5019 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5020 top_border_width, top_border_height, ex, ey - top_border_height);
5021 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5022 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5024 redraw_mask |= REDRAW_ALL;
5027 void UndrawSpecialEditorDoor()
5029 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5030 int top_border_width = gfx1->width;
5031 int top_border_height = gfx1->height;
5032 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5033 int ex = EX - outer_border;
5034 int ey = EY - outer_border;
5035 int ey_top = ey - top_border_height;
5036 int exsize = EXSIZE + 2 * outer_border;
5037 int eysize = EYSIZE + 2 * outer_border;
5039 if (!useSpecialEditorDoor())
5042 /* draw normal tape recorder window */
5043 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5045 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5046 ex, ey_top, top_border_width, top_border_height,
5048 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5049 ex, ey, exsize, eysize, ex, ey);
5053 // if screen background is set to "[NONE]", clear editor toolbox window
5054 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5055 ClearRectangle(drawto, ex, ey, exsize, eysize);
5058 redraw_mask |= REDRAW_ALL;
5062 /* ---------- new tool button stuff ---------------------------------------- */
5067 struct TextPosInfo *pos;
5070 } toolbutton_info[NUM_TOOL_BUTTONS] =
5073 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5074 TOOL_CTRL_ID_YES, "yes"
5077 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5078 TOOL_CTRL_ID_NO, "no"
5081 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5082 TOOL_CTRL_ID_CONFIRM, "confirm"
5085 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5086 TOOL_CTRL_ID_PLAYER_1, "player 1"
5089 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5090 TOOL_CTRL_ID_PLAYER_2, "player 2"
5093 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5094 TOOL_CTRL_ID_PLAYER_3, "player 3"
5097 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5098 TOOL_CTRL_ID_PLAYER_4, "player 4"
5102 void CreateToolButtons()
5106 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5108 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
5109 struct TextPosInfo *pos = toolbutton_info[i].pos;
5110 struct GadgetInfo *gi;
5111 Bitmap *deco_bitmap = None;
5112 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5113 unsigned int event_mask = GD_EVENT_RELEASED;
5116 int gd_x = gfx->src_x;
5117 int gd_y = gfx->src_y;
5118 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5119 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5122 if (global.use_envelope_request)
5123 setRequestPosition(&dx, &dy, TRUE);
5125 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5127 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5129 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5130 pos->size, &deco_bitmap, &deco_x, &deco_y);
5131 deco_xpos = (gfx->width - pos->size) / 2;
5132 deco_ypos = (gfx->height - pos->size) / 2;
5135 gi = CreateGadget(GDI_CUSTOM_ID, id,
5136 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5137 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
5138 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
5139 GDI_WIDTH, gfx->width,
5140 GDI_HEIGHT, gfx->height,
5141 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5142 GDI_STATE, GD_BUTTON_UNPRESSED,
5143 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5144 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5145 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5146 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5147 GDI_DECORATION_SIZE, pos->size, pos->size,
5148 GDI_DECORATION_SHIFTING, 1, 1,
5149 GDI_DIRECT_DRAW, FALSE,
5150 GDI_EVENT_MASK, event_mask,
5151 GDI_CALLBACK_ACTION, HandleToolButtons,
5155 Error(ERR_EXIT, "cannot create gadget");
5157 tool_gadget[id] = gi;
5161 void FreeToolButtons()
5165 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5166 FreeGadget(tool_gadget[i]);
5169 static void UnmapToolButtons()
5173 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5174 UnmapGadget(tool_gadget[i]);
5177 static void HandleToolButtons(struct GadgetInfo *gi)
5179 request_gadget_id = gi->custom_id;
5182 static struct Mapping_EM_to_RND_object
5185 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
5186 boolean is_backside; /* backside of moving element */
5192 em_object_mapping_list[] =
5195 Xblank, TRUE, FALSE,
5199 Yacid_splash_eB, FALSE, FALSE,
5200 EL_ACID_SPLASH_RIGHT, -1, -1
5203 Yacid_splash_wB, FALSE, FALSE,
5204 EL_ACID_SPLASH_LEFT, -1, -1
5207 #ifdef EM_ENGINE_BAD_ROLL
5209 Xstone_force_e, FALSE, FALSE,
5210 EL_ROCK, -1, MV_BIT_RIGHT
5213 Xstone_force_w, FALSE, FALSE,
5214 EL_ROCK, -1, MV_BIT_LEFT
5217 Xnut_force_e, FALSE, FALSE,
5218 EL_NUT, -1, MV_BIT_RIGHT
5221 Xnut_force_w, FALSE, FALSE,
5222 EL_NUT, -1, MV_BIT_LEFT
5225 Xspring_force_e, FALSE, FALSE,
5226 EL_SPRING, -1, MV_BIT_RIGHT
5229 Xspring_force_w, FALSE, FALSE,
5230 EL_SPRING, -1, MV_BIT_LEFT
5233 Xemerald_force_e, FALSE, FALSE,
5234 EL_EMERALD, -1, MV_BIT_RIGHT
5237 Xemerald_force_w, FALSE, FALSE,
5238 EL_EMERALD, -1, MV_BIT_LEFT
5241 Xdiamond_force_e, FALSE, FALSE,
5242 EL_DIAMOND, -1, MV_BIT_RIGHT
5245 Xdiamond_force_w, FALSE, FALSE,
5246 EL_DIAMOND, -1, MV_BIT_LEFT
5249 Xbomb_force_e, FALSE, FALSE,
5250 EL_BOMB, -1, MV_BIT_RIGHT
5253 Xbomb_force_w, FALSE, FALSE,
5254 EL_BOMB, -1, MV_BIT_LEFT
5256 #endif /* EM_ENGINE_BAD_ROLL */
5259 Xstone, TRUE, FALSE,
5263 Xstone_pause, FALSE, FALSE,
5267 Xstone_fall, FALSE, FALSE,
5271 Ystone_s, FALSE, FALSE,
5272 EL_ROCK, ACTION_FALLING, -1
5275 Ystone_sB, FALSE, TRUE,
5276 EL_ROCK, ACTION_FALLING, -1
5279 Ystone_e, FALSE, FALSE,
5280 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5283 Ystone_eB, FALSE, TRUE,
5284 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5287 Ystone_w, FALSE, FALSE,
5288 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5291 Ystone_wB, FALSE, TRUE,
5292 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5299 Xnut_pause, FALSE, FALSE,
5303 Xnut_fall, FALSE, FALSE,
5307 Ynut_s, FALSE, FALSE,
5308 EL_NUT, ACTION_FALLING, -1
5311 Ynut_sB, FALSE, TRUE,
5312 EL_NUT, ACTION_FALLING, -1
5315 Ynut_e, FALSE, FALSE,
5316 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5319 Ynut_eB, FALSE, TRUE,
5320 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5323 Ynut_w, FALSE, FALSE,
5324 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5327 Ynut_wB, FALSE, TRUE,
5328 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5331 Xbug_n, TRUE, FALSE,
5335 Xbug_e, TRUE, FALSE,
5336 EL_BUG_RIGHT, -1, -1
5339 Xbug_s, TRUE, FALSE,
5343 Xbug_w, TRUE, FALSE,
5347 Xbug_gon, FALSE, FALSE,
5351 Xbug_goe, FALSE, FALSE,
5352 EL_BUG_RIGHT, -1, -1
5355 Xbug_gos, FALSE, FALSE,
5359 Xbug_gow, FALSE, FALSE,
5363 Ybug_n, FALSE, FALSE,
5364 EL_BUG, ACTION_MOVING, MV_BIT_UP
5367 Ybug_nB, FALSE, TRUE,
5368 EL_BUG, ACTION_MOVING, MV_BIT_UP
5371 Ybug_e, FALSE, FALSE,
5372 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5375 Ybug_eB, FALSE, TRUE,
5376 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5379 Ybug_s, FALSE, FALSE,
5380 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5383 Ybug_sB, FALSE, TRUE,
5384 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5387 Ybug_w, FALSE, FALSE,
5388 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5391 Ybug_wB, FALSE, TRUE,
5392 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5395 Ybug_w_n, FALSE, FALSE,
5396 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5399 Ybug_n_e, FALSE, FALSE,
5400 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5403 Ybug_e_s, FALSE, FALSE,
5404 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5407 Ybug_s_w, FALSE, FALSE,
5408 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5411 Ybug_e_n, FALSE, FALSE,
5412 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5415 Ybug_s_e, FALSE, FALSE,
5416 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5419 Ybug_w_s, FALSE, FALSE,
5420 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5423 Ybug_n_w, FALSE, FALSE,
5424 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5427 Ybug_stone, FALSE, FALSE,
5428 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5431 Ybug_spring, FALSE, FALSE,
5432 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5435 Xtank_n, TRUE, FALSE,
5436 EL_SPACESHIP_UP, -1, -1
5439 Xtank_e, TRUE, FALSE,
5440 EL_SPACESHIP_RIGHT, -1, -1
5443 Xtank_s, TRUE, FALSE,
5444 EL_SPACESHIP_DOWN, -1, -1
5447 Xtank_w, TRUE, FALSE,
5448 EL_SPACESHIP_LEFT, -1, -1
5451 Xtank_gon, FALSE, FALSE,
5452 EL_SPACESHIP_UP, -1, -1
5455 Xtank_goe, FALSE, FALSE,
5456 EL_SPACESHIP_RIGHT, -1, -1
5459 Xtank_gos, FALSE, FALSE,
5460 EL_SPACESHIP_DOWN, -1, -1
5463 Xtank_gow, FALSE, FALSE,
5464 EL_SPACESHIP_LEFT, -1, -1
5467 Ytank_n, FALSE, FALSE,
5468 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5471 Ytank_nB, FALSE, TRUE,
5472 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5475 Ytank_e, FALSE, FALSE,
5476 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5479 Ytank_eB, FALSE, TRUE,
5480 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5483 Ytank_s, FALSE, FALSE,
5484 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5487 Ytank_sB, FALSE, TRUE,
5488 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5491 Ytank_w, FALSE, FALSE,
5492 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5495 Ytank_wB, FALSE, TRUE,
5496 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5499 Ytank_w_n, FALSE, FALSE,
5500 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5503 Ytank_n_e, FALSE, FALSE,
5504 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5507 Ytank_e_s, FALSE, FALSE,
5508 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5511 Ytank_s_w, FALSE, FALSE,
5512 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5515 Ytank_e_n, FALSE, FALSE,
5516 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5519 Ytank_s_e, FALSE, FALSE,
5520 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5523 Ytank_w_s, FALSE, FALSE,
5524 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5527 Ytank_n_w, FALSE, FALSE,
5528 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5531 Ytank_stone, FALSE, FALSE,
5532 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5535 Ytank_spring, FALSE, FALSE,
5536 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5539 Xandroid, TRUE, FALSE,
5540 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5543 Xandroid_1_n, FALSE, FALSE,
5544 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5547 Xandroid_2_n, FALSE, FALSE,
5548 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5551 Xandroid_1_e, FALSE, FALSE,
5552 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5555 Xandroid_2_e, FALSE, FALSE,
5556 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5559 Xandroid_1_w, FALSE, FALSE,
5560 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5563 Xandroid_2_w, FALSE, FALSE,
5564 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5567 Xandroid_1_s, FALSE, FALSE,
5568 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5571 Xandroid_2_s, FALSE, FALSE,
5572 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5575 Yandroid_n, FALSE, FALSE,
5576 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5579 Yandroid_nB, FALSE, TRUE,
5580 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5583 Yandroid_ne, FALSE, FALSE,
5584 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5587 Yandroid_neB, FALSE, TRUE,
5588 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5591 Yandroid_e, FALSE, FALSE,
5592 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5595 Yandroid_eB, FALSE, TRUE,
5596 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5599 Yandroid_se, FALSE, FALSE,
5600 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5603 Yandroid_seB, FALSE, TRUE,
5604 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5607 Yandroid_s, FALSE, FALSE,
5608 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5611 Yandroid_sB, FALSE, TRUE,
5612 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5615 Yandroid_sw, FALSE, FALSE,
5616 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5619 Yandroid_swB, FALSE, TRUE,
5620 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5623 Yandroid_w, FALSE, FALSE,
5624 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5627 Yandroid_wB, FALSE, TRUE,
5628 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5631 Yandroid_nw, FALSE, FALSE,
5632 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5635 Yandroid_nwB, FALSE, TRUE,
5636 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5639 Xspring, TRUE, FALSE,
5643 Xspring_pause, FALSE, FALSE,
5647 Xspring_e, FALSE, FALSE,
5651 Xspring_w, FALSE, FALSE,
5655 Xspring_fall, FALSE, FALSE,
5659 Yspring_s, FALSE, FALSE,
5660 EL_SPRING, ACTION_FALLING, -1
5663 Yspring_sB, FALSE, TRUE,
5664 EL_SPRING, ACTION_FALLING, -1
5667 Yspring_e, FALSE, FALSE,
5668 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5671 Yspring_eB, FALSE, TRUE,
5672 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5675 Yspring_w, FALSE, FALSE,
5676 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5679 Yspring_wB, FALSE, TRUE,
5680 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5683 Yspring_kill_e, FALSE, FALSE,
5684 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5687 Yspring_kill_eB, FALSE, TRUE,
5688 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5691 Yspring_kill_w, FALSE, FALSE,
5692 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5695 Yspring_kill_wB, FALSE, TRUE,
5696 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5699 Xeater_n, TRUE, FALSE,
5700 EL_YAMYAM_UP, -1, -1
5703 Xeater_e, TRUE, FALSE,
5704 EL_YAMYAM_RIGHT, -1, -1
5707 Xeater_w, TRUE, FALSE,
5708 EL_YAMYAM_LEFT, -1, -1
5711 Xeater_s, TRUE, FALSE,
5712 EL_YAMYAM_DOWN, -1, -1
5715 Yeater_n, FALSE, FALSE,
5716 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5719 Yeater_nB, FALSE, TRUE,
5720 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5723 Yeater_e, FALSE, FALSE,
5724 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5727 Yeater_eB, FALSE, TRUE,
5728 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5731 Yeater_s, FALSE, FALSE,
5732 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5735 Yeater_sB, FALSE, TRUE,
5736 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5739 Yeater_w, FALSE, FALSE,
5740 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5743 Yeater_wB, FALSE, TRUE,
5744 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5747 Yeater_stone, FALSE, FALSE,
5748 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5751 Yeater_spring, FALSE, FALSE,
5752 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5755 Xalien, TRUE, FALSE,
5759 Xalien_pause, FALSE, FALSE,
5763 Yalien_n, FALSE, FALSE,
5764 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5767 Yalien_nB, FALSE, TRUE,
5768 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5771 Yalien_e, FALSE, FALSE,
5772 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5775 Yalien_eB, FALSE, TRUE,
5776 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5779 Yalien_s, FALSE, FALSE,
5780 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5783 Yalien_sB, FALSE, TRUE,
5784 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5787 Yalien_w, FALSE, FALSE,
5788 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5791 Yalien_wB, FALSE, TRUE,
5792 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5795 Yalien_stone, FALSE, FALSE,
5796 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5799 Yalien_spring, FALSE, FALSE,
5800 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5803 Xemerald, TRUE, FALSE,
5807 Xemerald_pause, FALSE, FALSE,
5811 Xemerald_fall, FALSE, FALSE,
5815 Xemerald_shine, FALSE, FALSE,
5816 EL_EMERALD, ACTION_TWINKLING, -1
5819 Yemerald_s, FALSE, FALSE,
5820 EL_EMERALD, ACTION_FALLING, -1
5823 Yemerald_sB, FALSE, TRUE,
5824 EL_EMERALD, ACTION_FALLING, -1
5827 Yemerald_e, FALSE, FALSE,
5828 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5831 Yemerald_eB, FALSE, TRUE,
5832 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5835 Yemerald_w, FALSE, FALSE,
5836 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5839 Yemerald_wB, FALSE, TRUE,
5840 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5843 Yemerald_eat, FALSE, FALSE,
5844 EL_EMERALD, ACTION_COLLECTING, -1
5847 Yemerald_stone, FALSE, FALSE,
5848 EL_NUT, ACTION_BREAKING, -1
5851 Xdiamond, TRUE, FALSE,
5855 Xdiamond_pause, FALSE, FALSE,
5859 Xdiamond_fall, FALSE, FALSE,
5863 Xdiamond_shine, FALSE, FALSE,
5864 EL_DIAMOND, ACTION_TWINKLING, -1
5867 Ydiamond_s, FALSE, FALSE,
5868 EL_DIAMOND, ACTION_FALLING, -1
5871 Ydiamond_sB, FALSE, TRUE,
5872 EL_DIAMOND, ACTION_FALLING, -1
5875 Ydiamond_e, FALSE, FALSE,
5876 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5879 Ydiamond_eB, FALSE, TRUE,
5880 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5883 Ydiamond_w, FALSE, FALSE,
5884 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5887 Ydiamond_wB, FALSE, TRUE,
5888 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5891 Ydiamond_eat, FALSE, FALSE,
5892 EL_DIAMOND, ACTION_COLLECTING, -1
5895 Ydiamond_stone, FALSE, FALSE,
5896 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5899 Xdrip_fall, TRUE, FALSE,
5900 EL_AMOEBA_DROP, -1, -1
5903 Xdrip_stretch, FALSE, FALSE,
5904 EL_AMOEBA_DROP, ACTION_FALLING, -1
5907 Xdrip_stretchB, FALSE, TRUE,
5908 EL_AMOEBA_DROP, ACTION_FALLING, -1
5911 Xdrip_eat, FALSE, FALSE,
5912 EL_AMOEBA_DROP, ACTION_GROWING, -1
5915 Ydrip_s1, FALSE, FALSE,
5916 EL_AMOEBA_DROP, ACTION_FALLING, -1
5919 Ydrip_s1B, FALSE, TRUE,
5920 EL_AMOEBA_DROP, ACTION_FALLING, -1
5923 Ydrip_s2, FALSE, FALSE,
5924 EL_AMOEBA_DROP, ACTION_FALLING, -1
5927 Ydrip_s2B, FALSE, TRUE,
5928 EL_AMOEBA_DROP, ACTION_FALLING, -1
5935 Xbomb_pause, FALSE, FALSE,
5939 Xbomb_fall, FALSE, FALSE,
5943 Ybomb_s, FALSE, FALSE,
5944 EL_BOMB, ACTION_FALLING, -1
5947 Ybomb_sB, FALSE, TRUE,
5948 EL_BOMB, ACTION_FALLING, -1
5951 Ybomb_e, FALSE, FALSE,
5952 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5955 Ybomb_eB, FALSE, TRUE,
5956 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5959 Ybomb_w, FALSE, FALSE,
5960 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5963 Ybomb_wB, FALSE, TRUE,
5964 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5967 Ybomb_eat, FALSE, FALSE,
5968 EL_BOMB, ACTION_ACTIVATING, -1
5971 Xballoon, TRUE, FALSE,
5975 Yballoon_n, FALSE, FALSE,
5976 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5979 Yballoon_nB, FALSE, TRUE,
5980 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5983 Yballoon_e, FALSE, FALSE,
5984 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5987 Yballoon_eB, FALSE, TRUE,
5988 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5991 Yballoon_s, FALSE, FALSE,
5992 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5995 Yballoon_sB, FALSE, TRUE,
5996 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5999 Yballoon_w, FALSE, FALSE,
6000 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6003 Yballoon_wB, FALSE, TRUE,
6004 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6007 Xgrass, TRUE, FALSE,
6008 EL_EMC_GRASS, -1, -1
6011 Ygrass_nB, FALSE, FALSE,
6012 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6015 Ygrass_eB, FALSE, FALSE,
6016 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6019 Ygrass_sB, FALSE, FALSE,
6020 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6023 Ygrass_wB, FALSE, FALSE,
6024 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6031 Ydirt_nB, FALSE, FALSE,
6032 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6035 Ydirt_eB, FALSE, FALSE,
6036 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6039 Ydirt_sB, FALSE, FALSE,
6040 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6043 Ydirt_wB, FALSE, FALSE,
6044 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6047 Xacid_ne, TRUE, FALSE,
6048 EL_ACID_POOL_TOPRIGHT, -1, -1
6051 Xacid_se, TRUE, FALSE,
6052 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6055 Xacid_s, TRUE, FALSE,
6056 EL_ACID_POOL_BOTTOM, -1, -1
6059 Xacid_sw, TRUE, FALSE,
6060 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6063 Xacid_nw, TRUE, FALSE,
6064 EL_ACID_POOL_TOPLEFT, -1, -1
6067 Xacid_1, TRUE, FALSE,
6071 Xacid_2, FALSE, FALSE,
6075 Xacid_3, FALSE, FALSE,
6079 Xacid_4, FALSE, FALSE,
6083 Xacid_5, FALSE, FALSE,
6087 Xacid_6, FALSE, FALSE,
6091 Xacid_7, FALSE, FALSE,
6095 Xacid_8, FALSE, FALSE,
6099 Xball_1, TRUE, FALSE,
6100 EL_EMC_MAGIC_BALL, -1, -1
6103 Xball_1B, FALSE, FALSE,
6104 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6107 Xball_2, FALSE, FALSE,
6108 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6111 Xball_2B, FALSE, FALSE,
6112 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6115 Yball_eat, FALSE, FALSE,
6116 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6119 Ykey_1_eat, FALSE, FALSE,
6120 EL_EM_KEY_1, ACTION_COLLECTING, -1
6123 Ykey_2_eat, FALSE, FALSE,
6124 EL_EM_KEY_2, ACTION_COLLECTING, -1
6127 Ykey_3_eat, FALSE, FALSE,
6128 EL_EM_KEY_3, ACTION_COLLECTING, -1
6131 Ykey_4_eat, FALSE, FALSE,
6132 EL_EM_KEY_4, ACTION_COLLECTING, -1
6135 Ykey_5_eat, FALSE, FALSE,
6136 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6139 Ykey_6_eat, FALSE, FALSE,
6140 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6143 Ykey_7_eat, FALSE, FALSE,
6144 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6147 Ykey_8_eat, FALSE, FALSE,
6148 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6151 Ylenses_eat, FALSE, FALSE,
6152 EL_EMC_LENSES, ACTION_COLLECTING, -1
6155 Ymagnify_eat, FALSE, FALSE,
6156 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6159 Ygrass_eat, FALSE, FALSE,
6160 EL_EMC_GRASS, ACTION_SNAPPING, -1
6163 Ydirt_eat, FALSE, FALSE,
6164 EL_SAND, ACTION_SNAPPING, -1
6167 Xgrow_ns, TRUE, FALSE,
6168 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6171 Ygrow_ns_eat, FALSE, FALSE,
6172 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6175 Xgrow_ew, TRUE, FALSE,
6176 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6179 Ygrow_ew_eat, FALSE, FALSE,
6180 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6183 Xwonderwall, TRUE, FALSE,
6184 EL_MAGIC_WALL, -1, -1
6187 XwonderwallB, FALSE, FALSE,
6188 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6191 Xamoeba_1, TRUE, FALSE,
6192 EL_AMOEBA_DRY, ACTION_OTHER, -1
6195 Xamoeba_2, FALSE, FALSE,
6196 EL_AMOEBA_DRY, ACTION_OTHER, -1
6199 Xamoeba_3, FALSE, FALSE,
6200 EL_AMOEBA_DRY, ACTION_OTHER, -1
6203 Xamoeba_4, FALSE, FALSE,
6204 EL_AMOEBA_DRY, ACTION_OTHER, -1
6207 Xamoeba_5, TRUE, FALSE,
6208 EL_AMOEBA_WET, ACTION_OTHER, -1
6211 Xamoeba_6, FALSE, FALSE,
6212 EL_AMOEBA_WET, ACTION_OTHER, -1
6215 Xamoeba_7, FALSE, FALSE,
6216 EL_AMOEBA_WET, ACTION_OTHER, -1
6219 Xamoeba_8, FALSE, FALSE,
6220 EL_AMOEBA_WET, ACTION_OTHER, -1
6223 Xdoor_1, TRUE, FALSE,
6224 EL_EM_GATE_1, -1, -1
6227 Xdoor_2, TRUE, FALSE,
6228 EL_EM_GATE_2, -1, -1
6231 Xdoor_3, TRUE, FALSE,
6232 EL_EM_GATE_3, -1, -1
6235 Xdoor_4, TRUE, FALSE,
6236 EL_EM_GATE_4, -1, -1
6239 Xdoor_5, TRUE, FALSE,
6240 EL_EMC_GATE_5, -1, -1
6243 Xdoor_6, TRUE, FALSE,
6244 EL_EMC_GATE_6, -1, -1
6247 Xdoor_7, TRUE, FALSE,
6248 EL_EMC_GATE_7, -1, -1
6251 Xdoor_8, TRUE, FALSE,
6252 EL_EMC_GATE_8, -1, -1
6255 Xkey_1, TRUE, FALSE,
6259 Xkey_2, TRUE, FALSE,
6263 Xkey_3, TRUE, FALSE,
6267 Xkey_4, TRUE, FALSE,
6271 Xkey_5, TRUE, FALSE,
6272 EL_EMC_KEY_5, -1, -1
6275 Xkey_6, TRUE, FALSE,
6276 EL_EMC_KEY_6, -1, -1
6279 Xkey_7, TRUE, FALSE,
6280 EL_EMC_KEY_7, -1, -1
6283 Xkey_8, TRUE, FALSE,
6284 EL_EMC_KEY_8, -1, -1
6287 Xwind_n, TRUE, FALSE,
6288 EL_BALLOON_SWITCH_UP, -1, -1
6291 Xwind_e, TRUE, FALSE,
6292 EL_BALLOON_SWITCH_RIGHT, -1, -1
6295 Xwind_s, TRUE, FALSE,
6296 EL_BALLOON_SWITCH_DOWN, -1, -1
6299 Xwind_w, TRUE, FALSE,
6300 EL_BALLOON_SWITCH_LEFT, -1, -1
6303 Xwind_nesw, TRUE, FALSE,
6304 EL_BALLOON_SWITCH_ANY, -1, -1
6307 Xwind_stop, TRUE, FALSE,
6308 EL_BALLOON_SWITCH_NONE, -1, -1
6312 EL_EM_EXIT_CLOSED, -1, -1
6315 Xexit_1, TRUE, FALSE,
6316 EL_EM_EXIT_OPEN, -1, -1
6319 Xexit_2, FALSE, FALSE,
6320 EL_EM_EXIT_OPEN, -1, -1
6323 Xexit_3, FALSE, FALSE,
6324 EL_EM_EXIT_OPEN, -1, -1
6327 Xdynamite, TRUE, FALSE,
6328 EL_EM_DYNAMITE, -1, -1
6331 Ydynamite_eat, FALSE, FALSE,
6332 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6335 Xdynamite_1, TRUE, FALSE,
6336 EL_EM_DYNAMITE_ACTIVE, -1, -1
6339 Xdynamite_2, FALSE, FALSE,
6340 EL_EM_DYNAMITE_ACTIVE, -1, -1
6343 Xdynamite_3, FALSE, FALSE,
6344 EL_EM_DYNAMITE_ACTIVE, -1, -1
6347 Xdynamite_4, FALSE, FALSE,
6348 EL_EM_DYNAMITE_ACTIVE, -1, -1
6351 Xbumper, TRUE, FALSE,
6352 EL_EMC_SPRING_BUMPER, -1, -1
6355 XbumperB, FALSE, FALSE,
6356 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6359 Xwheel, TRUE, FALSE,
6360 EL_ROBOT_WHEEL, -1, -1
6363 XwheelB, FALSE, FALSE,
6364 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6367 Xswitch, TRUE, FALSE,
6368 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6371 XswitchB, FALSE, FALSE,
6372 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6376 EL_QUICKSAND_EMPTY, -1, -1
6379 Xsand_stone, TRUE, FALSE,
6380 EL_QUICKSAND_FULL, -1, -1
6383 Xsand_stonein_1, FALSE, TRUE,
6384 EL_ROCK, ACTION_FILLING, -1
6387 Xsand_stonein_2, FALSE, TRUE,
6388 EL_ROCK, ACTION_FILLING, -1
6391 Xsand_stonein_3, FALSE, TRUE,
6392 EL_ROCK, ACTION_FILLING, -1
6395 Xsand_stonein_4, FALSE, TRUE,
6396 EL_ROCK, ACTION_FILLING, -1
6399 Xsand_stonesand_1, FALSE, FALSE,
6400 EL_QUICKSAND_EMPTYING, -1, -1
6403 Xsand_stonesand_2, FALSE, FALSE,
6404 EL_QUICKSAND_EMPTYING, -1, -1
6407 Xsand_stonesand_3, FALSE, FALSE,
6408 EL_QUICKSAND_EMPTYING, -1, -1
6411 Xsand_stonesand_4, FALSE, FALSE,
6412 EL_QUICKSAND_EMPTYING, -1, -1
6415 Xsand_stonesand_quickout_1, FALSE, FALSE,
6416 EL_QUICKSAND_EMPTYING, -1, -1
6419 Xsand_stonesand_quickout_2, FALSE, FALSE,
6420 EL_QUICKSAND_EMPTYING, -1, -1
6423 Xsand_stoneout_1, FALSE, FALSE,
6424 EL_ROCK, ACTION_EMPTYING, -1
6427 Xsand_stoneout_2, FALSE, FALSE,
6428 EL_ROCK, ACTION_EMPTYING, -1
6431 Xsand_sandstone_1, FALSE, FALSE,
6432 EL_QUICKSAND_FILLING, -1, -1
6435 Xsand_sandstone_2, FALSE, FALSE,
6436 EL_QUICKSAND_FILLING, -1, -1
6439 Xsand_sandstone_3, FALSE, FALSE,
6440 EL_QUICKSAND_FILLING, -1, -1
6443 Xsand_sandstone_4, FALSE, FALSE,
6444 EL_QUICKSAND_FILLING, -1, -1
6447 Xplant, TRUE, FALSE,
6448 EL_EMC_PLANT, -1, -1
6451 Yplant, FALSE, FALSE,
6452 EL_EMC_PLANT, -1, -1
6455 Xlenses, TRUE, FALSE,
6456 EL_EMC_LENSES, -1, -1
6459 Xmagnify, TRUE, FALSE,
6460 EL_EMC_MAGNIFIER, -1, -1
6463 Xdripper, TRUE, FALSE,
6464 EL_EMC_DRIPPER, -1, -1
6467 XdripperB, FALSE, FALSE,
6468 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6471 Xfake_blank, TRUE, FALSE,
6472 EL_INVISIBLE_WALL, -1, -1
6475 Xfake_blankB, FALSE, FALSE,
6476 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6479 Xfake_grass, TRUE, FALSE,
6480 EL_EMC_FAKE_GRASS, -1, -1
6483 Xfake_grassB, FALSE, FALSE,
6484 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6487 Xfake_door_1, TRUE, FALSE,
6488 EL_EM_GATE_1_GRAY, -1, -1
6491 Xfake_door_2, TRUE, FALSE,
6492 EL_EM_GATE_2_GRAY, -1, -1
6495 Xfake_door_3, TRUE, FALSE,
6496 EL_EM_GATE_3_GRAY, -1, -1
6499 Xfake_door_4, TRUE, FALSE,
6500 EL_EM_GATE_4_GRAY, -1, -1
6503 Xfake_door_5, TRUE, FALSE,
6504 EL_EMC_GATE_5_GRAY, -1, -1
6507 Xfake_door_6, TRUE, FALSE,
6508 EL_EMC_GATE_6_GRAY, -1, -1
6511 Xfake_door_7, TRUE, FALSE,
6512 EL_EMC_GATE_7_GRAY, -1, -1
6515 Xfake_door_8, TRUE, FALSE,
6516 EL_EMC_GATE_8_GRAY, -1, -1
6519 Xfake_acid_1, TRUE, FALSE,
6520 EL_EMC_FAKE_ACID, -1, -1
6523 Xfake_acid_2, FALSE, FALSE,
6524 EL_EMC_FAKE_ACID, -1, -1
6527 Xfake_acid_3, FALSE, FALSE,
6528 EL_EMC_FAKE_ACID, -1, -1
6531 Xfake_acid_4, FALSE, FALSE,
6532 EL_EMC_FAKE_ACID, -1, -1
6535 Xfake_acid_5, FALSE, FALSE,
6536 EL_EMC_FAKE_ACID, -1, -1
6539 Xfake_acid_6, FALSE, FALSE,
6540 EL_EMC_FAKE_ACID, -1, -1
6543 Xfake_acid_7, FALSE, FALSE,
6544 EL_EMC_FAKE_ACID, -1, -1
6547 Xfake_acid_8, FALSE, FALSE,
6548 EL_EMC_FAKE_ACID, -1, -1
6551 Xsteel_1, TRUE, FALSE,
6552 EL_STEELWALL, -1, -1
6555 Xsteel_2, TRUE, FALSE,
6556 EL_EMC_STEELWALL_2, -1, -1
6559 Xsteel_3, TRUE, FALSE,
6560 EL_EMC_STEELWALL_3, -1, -1
6563 Xsteel_4, TRUE, FALSE,
6564 EL_EMC_STEELWALL_4, -1, -1
6567 Xwall_1, TRUE, FALSE,
6571 Xwall_2, TRUE, FALSE,
6572 EL_EMC_WALL_14, -1, -1
6575 Xwall_3, TRUE, FALSE,
6576 EL_EMC_WALL_15, -1, -1
6579 Xwall_4, TRUE, FALSE,
6580 EL_EMC_WALL_16, -1, -1
6583 Xround_wall_1, TRUE, FALSE,
6584 EL_WALL_SLIPPERY, -1, -1
6587 Xround_wall_2, TRUE, FALSE,
6588 EL_EMC_WALL_SLIPPERY_2, -1, -1
6591 Xround_wall_3, TRUE, FALSE,
6592 EL_EMC_WALL_SLIPPERY_3, -1, -1
6595 Xround_wall_4, TRUE, FALSE,
6596 EL_EMC_WALL_SLIPPERY_4, -1, -1
6599 Xdecor_1, TRUE, FALSE,
6600 EL_EMC_WALL_8, -1, -1
6603 Xdecor_2, TRUE, FALSE,
6604 EL_EMC_WALL_6, -1, -1
6607 Xdecor_3, TRUE, FALSE,
6608 EL_EMC_WALL_4, -1, -1
6611 Xdecor_4, TRUE, FALSE,
6612 EL_EMC_WALL_7, -1, -1
6615 Xdecor_5, TRUE, FALSE,
6616 EL_EMC_WALL_5, -1, -1
6619 Xdecor_6, TRUE, FALSE,
6620 EL_EMC_WALL_9, -1, -1
6623 Xdecor_7, TRUE, FALSE,
6624 EL_EMC_WALL_10, -1, -1
6627 Xdecor_8, TRUE, FALSE,
6628 EL_EMC_WALL_1, -1, -1
6631 Xdecor_9, TRUE, FALSE,
6632 EL_EMC_WALL_2, -1, -1
6635 Xdecor_10, TRUE, FALSE,
6636 EL_EMC_WALL_3, -1, -1
6639 Xdecor_11, TRUE, FALSE,
6640 EL_EMC_WALL_11, -1, -1
6643 Xdecor_12, TRUE, FALSE,
6644 EL_EMC_WALL_12, -1, -1
6647 Xalpha_0, TRUE, FALSE,
6648 EL_CHAR('0'), -1, -1
6651 Xalpha_1, TRUE, FALSE,
6652 EL_CHAR('1'), -1, -1
6655 Xalpha_2, TRUE, FALSE,
6656 EL_CHAR('2'), -1, -1
6659 Xalpha_3, TRUE, FALSE,
6660 EL_CHAR('3'), -1, -1
6663 Xalpha_4, TRUE, FALSE,
6664 EL_CHAR('4'), -1, -1
6667 Xalpha_5, TRUE, FALSE,
6668 EL_CHAR('5'), -1, -1
6671 Xalpha_6, TRUE, FALSE,
6672 EL_CHAR('6'), -1, -1
6675 Xalpha_7, TRUE, FALSE,
6676 EL_CHAR('7'), -1, -1
6679 Xalpha_8, TRUE, FALSE,
6680 EL_CHAR('8'), -1, -1
6683 Xalpha_9, TRUE, FALSE,
6684 EL_CHAR('9'), -1, -1
6687 Xalpha_excla, TRUE, FALSE,
6688 EL_CHAR('!'), -1, -1
6691 Xalpha_quote, TRUE, FALSE,
6692 EL_CHAR('"'), -1, -1
6695 Xalpha_comma, TRUE, FALSE,
6696 EL_CHAR(','), -1, -1
6699 Xalpha_minus, TRUE, FALSE,
6700 EL_CHAR('-'), -1, -1
6703 Xalpha_perio, TRUE, FALSE,
6704 EL_CHAR('.'), -1, -1
6707 Xalpha_colon, TRUE, FALSE,
6708 EL_CHAR(':'), -1, -1
6711 Xalpha_quest, TRUE, FALSE,
6712 EL_CHAR('?'), -1, -1
6715 Xalpha_a, TRUE, FALSE,
6716 EL_CHAR('A'), -1, -1
6719 Xalpha_b, TRUE, FALSE,
6720 EL_CHAR('B'), -1, -1
6723 Xalpha_c, TRUE, FALSE,
6724 EL_CHAR('C'), -1, -1
6727 Xalpha_d, TRUE, FALSE,
6728 EL_CHAR('D'), -1, -1
6731 Xalpha_e, TRUE, FALSE,
6732 EL_CHAR('E'), -1, -1
6735 Xalpha_f, TRUE, FALSE,
6736 EL_CHAR('F'), -1, -1
6739 Xalpha_g, TRUE, FALSE,
6740 EL_CHAR('G'), -1, -1
6743 Xalpha_h, TRUE, FALSE,
6744 EL_CHAR('H'), -1, -1
6747 Xalpha_i, TRUE, FALSE,
6748 EL_CHAR('I'), -1, -1
6751 Xalpha_j, TRUE, FALSE,
6752 EL_CHAR('J'), -1, -1
6755 Xalpha_k, TRUE, FALSE,
6756 EL_CHAR('K'), -1, -1
6759 Xalpha_l, TRUE, FALSE,
6760 EL_CHAR('L'), -1, -1
6763 Xalpha_m, TRUE, FALSE,
6764 EL_CHAR('M'), -1, -1
6767 Xalpha_n, TRUE, FALSE,
6768 EL_CHAR('N'), -1, -1
6771 Xalpha_o, TRUE, FALSE,
6772 EL_CHAR('O'), -1, -1
6775 Xalpha_p, TRUE, FALSE,
6776 EL_CHAR('P'), -1, -1
6779 Xalpha_q, TRUE, FALSE,
6780 EL_CHAR('Q'), -1, -1
6783 Xalpha_r, TRUE, FALSE,
6784 EL_CHAR('R'), -1, -1
6787 Xalpha_s, TRUE, FALSE,
6788 EL_CHAR('S'), -1, -1
6791 Xalpha_t, TRUE, FALSE,
6792 EL_CHAR('T'), -1, -1
6795 Xalpha_u, TRUE, FALSE,
6796 EL_CHAR('U'), -1, -1
6799 Xalpha_v, TRUE, FALSE,
6800 EL_CHAR('V'), -1, -1
6803 Xalpha_w, TRUE, FALSE,
6804 EL_CHAR('W'), -1, -1
6807 Xalpha_x, TRUE, FALSE,
6808 EL_CHAR('X'), -1, -1
6811 Xalpha_y, TRUE, FALSE,
6812 EL_CHAR('Y'), -1, -1
6815 Xalpha_z, TRUE, FALSE,
6816 EL_CHAR('Z'), -1, -1
6819 Xalpha_arrow_e, TRUE, FALSE,
6820 EL_CHAR('>'), -1, -1
6823 Xalpha_arrow_w, TRUE, FALSE,
6824 EL_CHAR('<'), -1, -1
6827 Xalpha_copyr, TRUE, FALSE,
6828 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6832 Xboom_bug, FALSE, FALSE,
6833 EL_BUG, ACTION_EXPLODING, -1
6836 Xboom_bomb, FALSE, FALSE,
6837 EL_BOMB, ACTION_EXPLODING, -1
6840 Xboom_android, FALSE, FALSE,
6841 EL_EMC_ANDROID, ACTION_OTHER, -1
6844 Xboom_1, FALSE, FALSE,
6845 EL_DEFAULT, ACTION_EXPLODING, -1
6848 Xboom_2, FALSE, FALSE,
6849 EL_DEFAULT, ACTION_EXPLODING, -1
6852 Znormal, FALSE, FALSE,
6856 Zdynamite, FALSE, FALSE,
6860 Zplayer, FALSE, FALSE,
6864 ZBORDER, FALSE, FALSE,
6874 static struct Mapping_EM_to_RND_player
6883 em_player_mapping_list[] =
6887 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6891 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6895 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6899 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6903 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6907 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6911 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6915 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6919 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6923 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6927 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6931 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6935 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6939 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6943 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6947 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6951 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6955 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6959 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6963 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6967 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6971 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6975 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6979 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6983 EL_PLAYER_1, ACTION_DEFAULT, -1,
6987 EL_PLAYER_2, ACTION_DEFAULT, -1,
6991 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6995 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6999 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7003 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7007 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7011 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7015 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7019 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7023 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7027 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7031 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7035 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7039 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7043 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7047 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7051 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7055 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7059 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7063 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7067 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7071 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7075 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7079 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7083 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7087 EL_PLAYER_3, ACTION_DEFAULT, -1,
7091 EL_PLAYER_4, ACTION_DEFAULT, -1,
7100 int map_element_RND_to_EM(int element_rnd)
7102 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7103 static boolean mapping_initialized = FALSE;
7105 if (!mapping_initialized)
7109 /* return "Xalpha_quest" for all undefined elements in mapping array */
7110 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7111 mapping_RND_to_EM[i] = Xalpha_quest;
7113 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7114 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7115 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7116 em_object_mapping_list[i].element_em;
7118 mapping_initialized = TRUE;
7121 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7122 return mapping_RND_to_EM[element_rnd];
7124 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7129 int map_element_EM_to_RND(int element_em)
7131 static unsigned short mapping_EM_to_RND[TILE_MAX];
7132 static boolean mapping_initialized = FALSE;
7134 if (!mapping_initialized)
7138 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
7139 for (i = 0; i < TILE_MAX; i++)
7140 mapping_EM_to_RND[i] = EL_UNKNOWN;
7142 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7143 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7144 em_object_mapping_list[i].element_rnd;
7146 mapping_initialized = TRUE;
7149 if (element_em >= 0 && element_em < TILE_MAX)
7150 return mapping_EM_to_RND[element_em];
7152 Error(ERR_WARN, "invalid EM level element %d", element_em);
7157 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7159 struct LevelInfo_EM *level_em = level->native_em_level;
7160 struct LEVEL *lev = level_em->lev;
7163 for (i = 0; i < TILE_MAX; i++)
7164 lev->android_array[i] = Xblank;
7166 for (i = 0; i < level->num_android_clone_elements; i++)
7168 int element_rnd = level->android_clone_element[i];
7169 int element_em = map_element_RND_to_EM(element_rnd);
7171 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7172 if (em_object_mapping_list[j].element_rnd == element_rnd)
7173 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7177 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7179 struct LevelInfo_EM *level_em = level->native_em_level;
7180 struct LEVEL *lev = level_em->lev;
7183 level->num_android_clone_elements = 0;
7185 for (i = 0; i < TILE_MAX; i++)
7187 int element_em = lev->android_array[i];
7189 boolean element_found = FALSE;
7191 if (element_em == Xblank)
7194 element_rnd = map_element_EM_to_RND(element_em);
7196 for (j = 0; j < level->num_android_clone_elements; j++)
7197 if (level->android_clone_element[j] == element_rnd)
7198 element_found = TRUE;
7202 level->android_clone_element[level->num_android_clone_elements++] =
7205 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7210 if (level->num_android_clone_elements == 0)
7212 level->num_android_clone_elements = 1;
7213 level->android_clone_element[0] = EL_EMPTY;
7217 int map_direction_RND_to_EM(int direction)
7219 return (direction == MV_UP ? 0 :
7220 direction == MV_RIGHT ? 1 :
7221 direction == MV_DOWN ? 2 :
7222 direction == MV_LEFT ? 3 :
7226 int map_direction_EM_to_RND(int direction)
7228 return (direction == 0 ? MV_UP :
7229 direction == 1 ? MV_RIGHT :
7230 direction == 2 ? MV_DOWN :
7231 direction == 3 ? MV_LEFT :
7235 int map_element_RND_to_SP(int element_rnd)
7237 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
7239 if (element_rnd >= EL_SP_START &&
7240 element_rnd <= EL_SP_END)
7241 element_sp = element_rnd - EL_SP_START;
7242 else if (element_rnd == EL_EMPTY_SPACE)
7244 else if (element_rnd == EL_INVISIBLE_WALL)
7250 int map_element_SP_to_RND(int element_sp)
7252 int element_rnd = EL_UNKNOWN;
7254 if (element_sp >= 0x00 &&
7256 element_rnd = EL_SP_START + element_sp;
7257 else if (element_sp == 0x28)
7258 element_rnd = EL_INVISIBLE_WALL;
7263 int map_action_SP_to_RND(int action_sp)
7267 case actActive: return ACTION_ACTIVE;
7268 case actImpact: return ACTION_IMPACT;
7269 case actExploding: return ACTION_EXPLODING;
7270 case actDigging: return ACTION_DIGGING;
7271 case actSnapping: return ACTION_SNAPPING;
7272 case actCollecting: return ACTION_COLLECTING;
7273 case actPassing: return ACTION_PASSING;
7274 case actPushing: return ACTION_PUSHING;
7275 case actDropping: return ACTION_DROPPING;
7277 default: return ACTION_DEFAULT;
7281 int map_element_RND_to_MM(int element_rnd)
7283 return (element_rnd >= EL_MM_START_1 &&
7284 element_rnd <= EL_MM_END_1 ?
7285 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7287 element_rnd >= EL_MM_START_2 &&
7288 element_rnd <= EL_MM_END_2 ?
7289 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7291 element_rnd >= EL_CHAR_START &&
7292 element_rnd <= EL_CHAR_END ?
7293 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7295 element_rnd >= EL_MM_RUNTIME_START &&
7296 element_rnd <= EL_MM_RUNTIME_END ?
7297 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7299 element_rnd >= EL_MM_DUMMY_START &&
7300 element_rnd <= EL_MM_DUMMY_END ?
7301 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7303 EL_MM_EMPTY_NATIVE);
7306 int map_element_MM_to_RND(int element_mm)
7308 return (element_mm == EL_MM_EMPTY_NATIVE ||
7309 element_mm == EL_DF_EMPTY_NATIVE ?
7312 element_mm >= EL_MM_START_1_NATIVE &&
7313 element_mm <= EL_MM_END_1_NATIVE ?
7314 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7316 element_mm >= EL_MM_START_2_NATIVE &&
7317 element_mm <= EL_MM_END_2_NATIVE ?
7318 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7320 element_mm >= EL_MM_CHAR_START_NATIVE &&
7321 element_mm <= EL_MM_CHAR_END_NATIVE ?
7322 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7324 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7325 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7326 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7328 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7329 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7330 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7335 int map_mm_wall_element(int element)
7337 return (element >= EL_MM_STEEL_WALL_START &&
7338 element <= EL_MM_STEEL_WALL_END ?
7341 element >= EL_MM_WOODEN_WALL_START &&
7342 element <= EL_MM_WOODEN_WALL_END ?
7345 element >= EL_MM_ICE_WALL_START &&
7346 element <= EL_MM_ICE_WALL_END ?
7349 element >= EL_MM_AMOEBA_WALL_START &&
7350 element <= EL_MM_AMOEBA_WALL_END ?
7353 element >= EL_DF_STEEL_WALL_START &&
7354 element <= EL_DF_STEEL_WALL_END ?
7357 element >= EL_DF_WOODEN_WALL_START &&
7358 element <= EL_DF_WOODEN_WALL_END ?
7364 int get_next_element(int element)
7368 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7369 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7370 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7371 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7372 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7373 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7374 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7375 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7376 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7377 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7378 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7380 default: return element;
7384 int el2img_mm(int element_mm)
7386 return el2img(map_element_MM_to_RND(element_mm));
7389 int el_act_dir2img(int element, int action, int direction)
7391 element = GFX_ELEMENT(element);
7392 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7394 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7395 return element_info[element].direction_graphic[action][direction];
7398 static int el_act_dir2crm(int element, int action, int direction)
7400 element = GFX_ELEMENT(element);
7401 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7403 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7404 return element_info[element].direction_crumbled[action][direction];
7407 int el_act2img(int element, int action)
7409 element = GFX_ELEMENT(element);
7411 return element_info[element].graphic[action];
7414 int el_act2crm(int element, int action)
7416 element = GFX_ELEMENT(element);
7418 return element_info[element].crumbled[action];
7421 int el_dir2img(int element, int direction)
7423 element = GFX_ELEMENT(element);
7425 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7428 int el2baseimg(int element)
7430 return element_info[element].graphic[ACTION_DEFAULT];
7433 int el2img(int element)
7435 element = GFX_ELEMENT(element);
7437 return element_info[element].graphic[ACTION_DEFAULT];
7440 int el2edimg(int element)
7442 element = GFX_ELEMENT(element);
7444 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7447 int el2preimg(int element)
7449 element = GFX_ELEMENT(element);
7451 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7454 int el2panelimg(int element)
7456 element = GFX_ELEMENT(element);
7458 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7461 int font2baseimg(int font_nr)
7463 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7466 int getBeltNrFromBeltElement(int element)
7468 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7469 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7470 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
7473 int getBeltNrFromBeltActiveElement(int element)
7475 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
7476 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
7477 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
7480 int getBeltNrFromBeltSwitchElement(int element)
7482 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
7483 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
7484 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
7487 int getBeltDirNrFromBeltElement(int element)
7489 static int belt_base_element[4] =
7491 EL_CONVEYOR_BELT_1_LEFT,
7492 EL_CONVEYOR_BELT_2_LEFT,
7493 EL_CONVEYOR_BELT_3_LEFT,
7494 EL_CONVEYOR_BELT_4_LEFT
7497 int belt_nr = getBeltNrFromBeltElement(element);
7498 int belt_dir_nr = element - belt_base_element[belt_nr];
7500 return (belt_dir_nr % 3);
7503 int getBeltDirNrFromBeltSwitchElement(int element)
7505 static int belt_base_element[4] =
7507 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7508 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7509 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7510 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7513 int belt_nr = getBeltNrFromBeltSwitchElement(element);
7514 int belt_dir_nr = element - belt_base_element[belt_nr];
7516 return (belt_dir_nr % 3);
7519 int getBeltDirFromBeltElement(int element)
7521 static int belt_move_dir[3] =
7528 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7530 return belt_move_dir[belt_dir_nr];
7533 int getBeltDirFromBeltSwitchElement(int element)
7535 static int belt_move_dir[3] =
7542 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7544 return belt_move_dir[belt_dir_nr];
7547 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7549 static int belt_base_element[4] =
7551 EL_CONVEYOR_BELT_1_LEFT,
7552 EL_CONVEYOR_BELT_2_LEFT,
7553 EL_CONVEYOR_BELT_3_LEFT,
7554 EL_CONVEYOR_BELT_4_LEFT
7557 return belt_base_element[belt_nr] + belt_dir_nr;
7560 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7562 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7564 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7567 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7569 static int belt_base_element[4] =
7571 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7572 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7573 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7574 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7577 return belt_base_element[belt_nr] + belt_dir_nr;
7580 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7582 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7584 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7587 boolean getTeamMode_EM()
7589 return game.team_mode;
7592 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7594 int game_frame_delay_value;
7596 game_frame_delay_value =
7597 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7598 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7601 if (tape.playing && tape.warp_forward && !tape.pausing)
7602 game_frame_delay_value = 0;
7604 return game_frame_delay_value;
7607 unsigned int InitRND(int seed)
7609 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7610 return InitEngineRandom_EM(seed);
7611 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7612 return InitEngineRandom_SP(seed);
7613 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
7614 return InitEngineRandom_MM(seed);
7616 return InitEngineRandom_RND(seed);
7619 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7620 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7622 inline static int get_effective_element_EM(int tile, int frame_em)
7624 int element = object_mapping[tile].element_rnd;
7625 int action = object_mapping[tile].action;
7626 boolean is_backside = object_mapping[tile].is_backside;
7627 boolean action_removing = (action == ACTION_DIGGING ||
7628 action == ACTION_SNAPPING ||
7629 action == ACTION_COLLECTING);
7635 case Yacid_splash_eB:
7636 case Yacid_splash_wB:
7637 return (frame_em > 5 ? EL_EMPTY : element);
7643 else /* frame_em == 7 */
7647 case Yacid_splash_eB:
7648 case Yacid_splash_wB:
7651 case Yemerald_stone:
7654 case Ydiamond_stone:
7658 case Xdrip_stretchB:
7677 case Xsand_stonein_1:
7678 case Xsand_stonein_2:
7679 case Xsand_stonein_3:
7680 case Xsand_stonein_4:
7684 return (is_backside || action_removing ? EL_EMPTY : element);
7689 inline static boolean check_linear_animation_EM(int tile)
7693 case Xsand_stonesand_1:
7694 case Xsand_stonesand_quickout_1:
7695 case Xsand_sandstone_1:
7696 case Xsand_stonein_1:
7697 case Xsand_stoneout_1:
7716 case Yacid_splash_eB:
7717 case Yacid_splash_wB:
7718 case Yemerald_stone:
7725 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7726 boolean has_crumbled_graphics,
7727 int crumbled, int sync_frame)
7729 /* if element can be crumbled, but certain action graphics are just empty
7730 space (like instantly snapping sand to empty space in 1 frame), do not
7731 treat these empty space graphics as crumbled graphics in EMC engine */
7732 if (crumbled == IMG_EMPTY_SPACE)
7733 has_crumbled_graphics = FALSE;
7735 if (has_crumbled_graphics)
7737 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7738 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7739 g_crumbled->anim_delay,
7740 g_crumbled->anim_mode,
7741 g_crumbled->anim_start_frame,
7744 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7745 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7747 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7748 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
7750 g_em->has_crumbled_graphics = TRUE;
7754 g_em->crumbled_bitmap = NULL;
7755 g_em->crumbled_src_x = 0;
7756 g_em->crumbled_src_y = 0;
7757 g_em->crumbled_border_size = 0;
7758 g_em->crumbled_tile_size = 0;
7760 g_em->has_crumbled_graphics = FALSE;
7764 void ResetGfxAnimation_EM(int x, int y, int tile)
7769 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7770 int tile, int frame_em, int x, int y)
7772 int action = object_mapping[tile].action;
7773 int direction = object_mapping[tile].direction;
7774 int effective_element = get_effective_element_EM(tile, frame_em);
7775 int graphic = (direction == MV_NONE ?
7776 el_act2img(effective_element, action) :
7777 el_act_dir2img(effective_element, action, direction));
7778 struct GraphicInfo *g = &graphic_info[graphic];
7780 boolean action_removing = (action == ACTION_DIGGING ||
7781 action == ACTION_SNAPPING ||
7782 action == ACTION_COLLECTING);
7783 boolean action_moving = (action == ACTION_FALLING ||
7784 action == ACTION_MOVING ||
7785 action == ACTION_PUSHING ||
7786 action == ACTION_EATING ||
7787 action == ACTION_FILLING ||
7788 action == ACTION_EMPTYING);
7789 boolean action_falling = (action == ACTION_FALLING ||
7790 action == ACTION_FILLING ||
7791 action == ACTION_EMPTYING);
7793 /* special case: graphic uses "2nd movement tile" and has defined
7794 7 frames for movement animation (or less) => use default graphic
7795 for last (8th) frame which ends the movement animation */
7796 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7798 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7799 graphic = (direction == MV_NONE ?
7800 el_act2img(effective_element, action) :
7801 el_act_dir2img(effective_element, action, direction));
7803 g = &graphic_info[graphic];
7806 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7810 else if (action_moving)
7812 boolean is_backside = object_mapping[tile].is_backside;
7816 int direction = object_mapping[tile].direction;
7817 int move_dir = (action_falling ? MV_DOWN : direction);
7822 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7823 if (g->double_movement && frame_em == 0)
7827 if (move_dir == MV_LEFT)
7828 GfxFrame[x - 1][y] = GfxFrame[x][y];
7829 else if (move_dir == MV_RIGHT)
7830 GfxFrame[x + 1][y] = GfxFrame[x][y];
7831 else if (move_dir == MV_UP)
7832 GfxFrame[x][y - 1] = GfxFrame[x][y];
7833 else if (move_dir == MV_DOWN)
7834 GfxFrame[x][y + 1] = GfxFrame[x][y];
7841 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7842 if (tile == Xsand_stonesand_quickout_1 ||
7843 tile == Xsand_stonesand_quickout_2)
7847 if (graphic_info[graphic].anim_global_sync)
7848 sync_frame = FrameCounter;
7849 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7850 sync_frame = GfxFrame[x][y];
7852 sync_frame = 0; /* playfield border (pseudo steel) */
7854 SetRandomAnimationValue(x, y);
7856 int frame = getAnimationFrame(g->anim_frames,
7859 g->anim_start_frame,
7862 g_em->unique_identifier =
7863 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7866 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7867 int tile, int frame_em, int x, int y)
7869 int action = object_mapping[tile].action;
7870 int direction = object_mapping[tile].direction;
7871 boolean is_backside = object_mapping[tile].is_backside;
7872 int effective_element = get_effective_element_EM(tile, frame_em);
7873 int effective_action = action;
7874 int graphic = (direction == MV_NONE ?
7875 el_act2img(effective_element, effective_action) :
7876 el_act_dir2img(effective_element, effective_action,
7878 int crumbled = (direction == MV_NONE ?
7879 el_act2crm(effective_element, effective_action) :
7880 el_act_dir2crm(effective_element, effective_action,
7882 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7883 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7884 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7885 struct GraphicInfo *g = &graphic_info[graphic];
7888 /* special case: graphic uses "2nd movement tile" and has defined
7889 7 frames for movement animation (or less) => use default graphic
7890 for last (8th) frame which ends the movement animation */
7891 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7893 effective_action = ACTION_DEFAULT;
7894 graphic = (direction == MV_NONE ?
7895 el_act2img(effective_element, effective_action) :
7896 el_act_dir2img(effective_element, effective_action,
7898 crumbled = (direction == MV_NONE ?
7899 el_act2crm(effective_element, effective_action) :
7900 el_act_dir2crm(effective_element, effective_action,
7903 g = &graphic_info[graphic];
7906 if (graphic_info[graphic].anim_global_sync)
7907 sync_frame = FrameCounter;
7908 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7909 sync_frame = GfxFrame[x][y];
7911 sync_frame = 0; /* playfield border (pseudo steel) */
7913 SetRandomAnimationValue(x, y);
7915 int frame = getAnimationFrame(g->anim_frames,
7918 g->anim_start_frame,
7921 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7922 g->double_movement && is_backside);
7924 /* (updating the "crumbled" graphic definitions is probably not really needed,
7925 as animations for crumbled graphics can't be longer than one EMC cycle) */
7926 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7930 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7931 int player_nr, int anim, int frame_em)
7933 int element = player_mapping[player_nr][anim].element_rnd;
7934 int action = player_mapping[player_nr][anim].action;
7935 int direction = player_mapping[player_nr][anim].direction;
7936 int graphic = (direction == MV_NONE ?
7937 el_act2img(element, action) :
7938 el_act_dir2img(element, action, direction));
7939 struct GraphicInfo *g = &graphic_info[graphic];
7942 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7944 stored_player[player_nr].StepFrame = frame_em;
7946 sync_frame = stored_player[player_nr].Frame;
7948 int frame = getAnimationFrame(g->anim_frames,
7951 g->anim_start_frame,
7954 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7955 &g_em->src_x, &g_em->src_y, FALSE);
7958 void InitGraphicInfo_EM(void)
7963 int num_em_gfx_errors = 0;
7965 if (graphic_info_em_object[0][0].bitmap == NULL)
7967 /* EM graphics not yet initialized in em_open_all() */
7972 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7975 /* always start with reliable default values */
7976 for (i = 0; i < TILE_MAX; i++)
7978 object_mapping[i].element_rnd = EL_UNKNOWN;
7979 object_mapping[i].is_backside = FALSE;
7980 object_mapping[i].action = ACTION_DEFAULT;
7981 object_mapping[i].direction = MV_NONE;
7984 /* always start with reliable default values */
7985 for (p = 0; p < MAX_PLAYERS; p++)
7987 for (i = 0; i < SPR_MAX; i++)
7989 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7990 player_mapping[p][i].action = ACTION_DEFAULT;
7991 player_mapping[p][i].direction = MV_NONE;
7995 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7997 int e = em_object_mapping_list[i].element_em;
7999 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8000 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8002 if (em_object_mapping_list[i].action != -1)
8003 object_mapping[e].action = em_object_mapping_list[i].action;
8005 if (em_object_mapping_list[i].direction != -1)
8006 object_mapping[e].direction =
8007 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8010 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8012 int a = em_player_mapping_list[i].action_em;
8013 int p = em_player_mapping_list[i].player_nr;
8015 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8017 if (em_player_mapping_list[i].action != -1)
8018 player_mapping[p][a].action = em_player_mapping_list[i].action;
8020 if (em_player_mapping_list[i].direction != -1)
8021 player_mapping[p][a].direction =
8022 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8025 for (i = 0; i < TILE_MAX; i++)
8027 int element = object_mapping[i].element_rnd;
8028 int action = object_mapping[i].action;
8029 int direction = object_mapping[i].direction;
8030 boolean is_backside = object_mapping[i].is_backside;
8031 boolean action_exploding = ((action == ACTION_EXPLODING ||
8032 action == ACTION_SMASHED_BY_ROCK ||
8033 action == ACTION_SMASHED_BY_SPRING) &&
8034 element != EL_DIAMOND);
8035 boolean action_active = (action == ACTION_ACTIVE);
8036 boolean action_other = (action == ACTION_OTHER);
8038 for (j = 0; j < 8; j++)
8040 int effective_element = get_effective_element_EM(i, j);
8041 int effective_action = (j < 7 ? action :
8042 i == Xdrip_stretch ? action :
8043 i == Xdrip_stretchB ? action :
8044 i == Ydrip_s1 ? action :
8045 i == Ydrip_s1B ? action :
8046 i == Xball_1B ? action :
8047 i == Xball_2 ? action :
8048 i == Xball_2B ? action :
8049 i == Yball_eat ? action :
8050 i == Ykey_1_eat ? action :
8051 i == Ykey_2_eat ? action :
8052 i == Ykey_3_eat ? action :
8053 i == Ykey_4_eat ? action :
8054 i == Ykey_5_eat ? action :
8055 i == Ykey_6_eat ? action :
8056 i == Ykey_7_eat ? action :
8057 i == Ykey_8_eat ? action :
8058 i == Ylenses_eat ? action :
8059 i == Ymagnify_eat ? action :
8060 i == Ygrass_eat ? action :
8061 i == Ydirt_eat ? action :
8062 i == Xsand_stonein_1 ? action :
8063 i == Xsand_stonein_2 ? action :
8064 i == Xsand_stonein_3 ? action :
8065 i == Xsand_stonein_4 ? action :
8066 i == Xsand_stoneout_1 ? action :
8067 i == Xsand_stoneout_2 ? action :
8068 i == Xboom_android ? ACTION_EXPLODING :
8069 action_exploding ? ACTION_EXPLODING :
8070 action_active ? action :
8071 action_other ? action :
8073 int graphic = (el_act_dir2img(effective_element, effective_action,
8075 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8077 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8078 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8079 boolean has_action_graphics = (graphic != base_graphic);
8080 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8081 struct GraphicInfo *g = &graphic_info[graphic];
8082 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8085 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
8086 boolean special_animation = (action != ACTION_DEFAULT &&
8087 g->anim_frames == 3 &&
8088 g->anim_delay == 2 &&
8089 g->anim_mode & ANIM_LINEAR);
8090 int sync_frame = (i == Xdrip_stretch ? 7 :
8091 i == Xdrip_stretchB ? 7 :
8092 i == Ydrip_s2 ? j + 8 :
8093 i == Ydrip_s2B ? j + 8 :
8102 i == Xfake_acid_1 ? 0 :
8103 i == Xfake_acid_2 ? 10 :
8104 i == Xfake_acid_3 ? 20 :
8105 i == Xfake_acid_4 ? 30 :
8106 i == Xfake_acid_5 ? 40 :
8107 i == Xfake_acid_6 ? 50 :
8108 i == Xfake_acid_7 ? 60 :
8109 i == Xfake_acid_8 ? 70 :
8111 i == Xball_2B ? j + 8 :
8112 i == Yball_eat ? j + 1 :
8113 i == Ykey_1_eat ? j + 1 :
8114 i == Ykey_2_eat ? j + 1 :
8115 i == Ykey_3_eat ? j + 1 :
8116 i == Ykey_4_eat ? j + 1 :
8117 i == Ykey_5_eat ? j + 1 :
8118 i == Ykey_6_eat ? j + 1 :
8119 i == Ykey_7_eat ? j + 1 :
8120 i == Ykey_8_eat ? j + 1 :
8121 i == Ylenses_eat ? j + 1 :
8122 i == Ymagnify_eat ? j + 1 :
8123 i == Ygrass_eat ? j + 1 :
8124 i == Ydirt_eat ? j + 1 :
8125 i == Xamoeba_1 ? 0 :
8126 i == Xamoeba_2 ? 1 :
8127 i == Xamoeba_3 ? 2 :
8128 i == Xamoeba_4 ? 3 :
8129 i == Xamoeba_5 ? 0 :
8130 i == Xamoeba_6 ? 1 :
8131 i == Xamoeba_7 ? 2 :
8132 i == Xamoeba_8 ? 3 :
8133 i == Xexit_2 ? j + 8 :
8134 i == Xexit_3 ? j + 16 :
8135 i == Xdynamite_1 ? 0 :
8136 i == Xdynamite_2 ? 8 :
8137 i == Xdynamite_3 ? 16 :
8138 i == Xdynamite_4 ? 24 :
8139 i == Xsand_stonein_1 ? j + 1 :
8140 i == Xsand_stonein_2 ? j + 9 :
8141 i == Xsand_stonein_3 ? j + 17 :
8142 i == Xsand_stonein_4 ? j + 25 :
8143 i == Xsand_stoneout_1 && j == 0 ? 0 :
8144 i == Xsand_stoneout_1 && j == 1 ? 0 :
8145 i == Xsand_stoneout_1 && j == 2 ? 1 :
8146 i == Xsand_stoneout_1 && j == 3 ? 2 :
8147 i == Xsand_stoneout_1 && j == 4 ? 2 :
8148 i == Xsand_stoneout_1 && j == 5 ? 3 :
8149 i == Xsand_stoneout_1 && j == 6 ? 4 :
8150 i == Xsand_stoneout_1 && j == 7 ? 4 :
8151 i == Xsand_stoneout_2 && j == 0 ? 5 :
8152 i == Xsand_stoneout_2 && j == 1 ? 6 :
8153 i == Xsand_stoneout_2 && j == 2 ? 7 :
8154 i == Xsand_stoneout_2 && j == 3 ? 8 :
8155 i == Xsand_stoneout_2 && j == 4 ? 9 :
8156 i == Xsand_stoneout_2 && j == 5 ? 11 :
8157 i == Xsand_stoneout_2 && j == 6 ? 13 :
8158 i == Xsand_stoneout_2 && j == 7 ? 15 :
8159 i == Xboom_bug && j == 1 ? 2 :
8160 i == Xboom_bug && j == 2 ? 2 :
8161 i == Xboom_bug && j == 3 ? 4 :
8162 i == Xboom_bug && j == 4 ? 4 :
8163 i == Xboom_bug && j == 5 ? 2 :
8164 i == Xboom_bug && j == 6 ? 2 :
8165 i == Xboom_bug && j == 7 ? 0 :
8166 i == Xboom_bomb && j == 1 ? 2 :
8167 i == Xboom_bomb && j == 2 ? 2 :
8168 i == Xboom_bomb && j == 3 ? 4 :
8169 i == Xboom_bomb && j == 4 ? 4 :
8170 i == Xboom_bomb && j == 5 ? 2 :
8171 i == Xboom_bomb && j == 6 ? 2 :
8172 i == Xboom_bomb && j == 7 ? 0 :
8173 i == Xboom_android && j == 7 ? 6 :
8174 i == Xboom_1 && j == 1 ? 2 :
8175 i == Xboom_1 && j == 2 ? 2 :
8176 i == Xboom_1 && j == 3 ? 4 :
8177 i == Xboom_1 && j == 4 ? 4 :
8178 i == Xboom_1 && j == 5 ? 6 :
8179 i == Xboom_1 && j == 6 ? 6 :
8180 i == Xboom_1 && j == 7 ? 8 :
8181 i == Xboom_2 && j == 0 ? 8 :
8182 i == Xboom_2 && j == 1 ? 8 :
8183 i == Xboom_2 && j == 2 ? 10 :
8184 i == Xboom_2 && j == 3 ? 10 :
8185 i == Xboom_2 && j == 4 ? 10 :
8186 i == Xboom_2 && j == 5 ? 12 :
8187 i == Xboom_2 && j == 6 ? 12 :
8188 i == Xboom_2 && j == 7 ? 12 :
8189 special_animation && j == 4 ? 3 :
8190 effective_action != action ? 0 :
8194 Bitmap *debug_bitmap = g_em->bitmap;
8195 int debug_src_x = g_em->src_x;
8196 int debug_src_y = g_em->src_y;
8199 int frame = getAnimationFrame(g->anim_frames,
8202 g->anim_start_frame,
8205 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8206 g->double_movement && is_backside);
8208 g_em->bitmap = src_bitmap;
8209 g_em->src_x = src_x;
8210 g_em->src_y = src_y;
8211 g_em->src_offset_x = 0;
8212 g_em->src_offset_y = 0;
8213 g_em->dst_offset_x = 0;
8214 g_em->dst_offset_y = 0;
8215 g_em->width = TILEX;
8216 g_em->height = TILEY;
8218 g_em->preserve_background = FALSE;
8220 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8223 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8224 effective_action == ACTION_MOVING ||
8225 effective_action == ACTION_PUSHING ||
8226 effective_action == ACTION_EATING)) ||
8227 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8228 effective_action == ACTION_EMPTYING)))
8231 (effective_action == ACTION_FALLING ||
8232 effective_action == ACTION_FILLING ||
8233 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8234 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8235 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8236 int num_steps = (i == Ydrip_s1 ? 16 :
8237 i == Ydrip_s1B ? 16 :
8238 i == Ydrip_s2 ? 16 :
8239 i == Ydrip_s2B ? 16 :
8240 i == Xsand_stonein_1 ? 32 :
8241 i == Xsand_stonein_2 ? 32 :
8242 i == Xsand_stonein_3 ? 32 :
8243 i == Xsand_stonein_4 ? 32 :
8244 i == Xsand_stoneout_1 ? 16 :
8245 i == Xsand_stoneout_2 ? 16 : 8);
8246 int cx = ABS(dx) * (TILEX / num_steps);
8247 int cy = ABS(dy) * (TILEY / num_steps);
8248 int step_frame = (i == Ydrip_s2 ? j + 8 :
8249 i == Ydrip_s2B ? j + 8 :
8250 i == Xsand_stonein_2 ? j + 8 :
8251 i == Xsand_stonein_3 ? j + 16 :
8252 i == Xsand_stonein_4 ? j + 24 :
8253 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8254 int step = (is_backside ? step_frame : num_steps - step_frame);
8256 if (is_backside) /* tile where movement starts */
8258 if (dx < 0 || dy < 0)
8260 g_em->src_offset_x = cx * step;
8261 g_em->src_offset_y = cy * step;
8265 g_em->dst_offset_x = cx * step;
8266 g_em->dst_offset_y = cy * step;
8269 else /* tile where movement ends */
8271 if (dx < 0 || dy < 0)
8273 g_em->dst_offset_x = cx * step;
8274 g_em->dst_offset_y = cy * step;
8278 g_em->src_offset_x = cx * step;
8279 g_em->src_offset_y = cy * step;
8283 g_em->width = TILEX - cx * step;
8284 g_em->height = TILEY - cy * step;
8287 /* create unique graphic identifier to decide if tile must be redrawn */
8288 /* bit 31 - 16 (16 bit): EM style graphic
8289 bit 15 - 12 ( 4 bit): EM style frame
8290 bit 11 - 6 ( 6 bit): graphic width
8291 bit 5 - 0 ( 6 bit): graphic height */
8292 g_em->unique_identifier =
8293 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8297 /* skip check for EMC elements not contained in original EMC artwork */
8298 if (element == EL_EMC_FAKE_ACID)
8301 if (g_em->bitmap != debug_bitmap ||
8302 g_em->src_x != debug_src_x ||
8303 g_em->src_y != debug_src_y ||
8304 g_em->src_offset_x != 0 ||
8305 g_em->src_offset_y != 0 ||
8306 g_em->dst_offset_x != 0 ||
8307 g_em->dst_offset_y != 0 ||
8308 g_em->width != TILEX ||
8309 g_em->height != TILEY)
8311 static int last_i = -1;
8319 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8320 i, element, element_info[element].token_name,
8321 element_action_info[effective_action].suffix, direction);
8323 if (element != effective_element)
8324 printf(" [%d ('%s')]",
8326 element_info[effective_element].token_name);
8330 if (g_em->bitmap != debug_bitmap)
8331 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8332 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8334 if (g_em->src_x != debug_src_x ||
8335 g_em->src_y != debug_src_y)
8336 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8337 j, (is_backside ? 'B' : 'F'),
8338 g_em->src_x, g_em->src_y,
8339 g_em->src_x / 32, g_em->src_y / 32,
8340 debug_src_x, debug_src_y,
8341 debug_src_x / 32, debug_src_y / 32);
8343 if (g_em->src_offset_x != 0 ||
8344 g_em->src_offset_y != 0 ||
8345 g_em->dst_offset_x != 0 ||
8346 g_em->dst_offset_y != 0)
8347 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8349 g_em->src_offset_x, g_em->src_offset_y,
8350 g_em->dst_offset_x, g_em->dst_offset_y);
8352 if (g_em->width != TILEX ||
8353 g_em->height != TILEY)
8354 printf(" %d (%d): size %d,%d should be %d,%d\n",
8356 g_em->width, g_em->height, TILEX, TILEY);
8358 num_em_gfx_errors++;
8365 for (i = 0; i < TILE_MAX; i++)
8367 for (j = 0; j < 8; j++)
8369 int element = object_mapping[i].element_rnd;
8370 int action = object_mapping[i].action;
8371 int direction = object_mapping[i].direction;
8372 boolean is_backside = object_mapping[i].is_backside;
8373 int graphic_action = el_act_dir2img(element, action, direction);
8374 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8376 if ((action == ACTION_SMASHED_BY_ROCK ||
8377 action == ACTION_SMASHED_BY_SPRING ||
8378 action == ACTION_EATING) &&
8379 graphic_action == graphic_default)
8381 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8382 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8383 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8384 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8387 /* no separate animation for "smashed by rock" -- use rock instead */
8388 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8389 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8391 g_em->bitmap = g_xx->bitmap;
8392 g_em->src_x = g_xx->src_x;
8393 g_em->src_y = g_xx->src_y;
8394 g_em->src_offset_x = g_xx->src_offset_x;
8395 g_em->src_offset_y = g_xx->src_offset_y;
8396 g_em->dst_offset_x = g_xx->dst_offset_x;
8397 g_em->dst_offset_y = g_xx->dst_offset_y;
8398 g_em->width = g_xx->width;
8399 g_em->height = g_xx->height;
8400 g_em->unique_identifier = g_xx->unique_identifier;
8403 g_em->preserve_background = TRUE;
8408 for (p = 0; p < MAX_PLAYERS; p++)
8410 for (i = 0; i < SPR_MAX; i++)
8412 int element = player_mapping[p][i].element_rnd;
8413 int action = player_mapping[p][i].action;
8414 int direction = player_mapping[p][i].direction;
8416 for (j = 0; j < 8; j++)
8418 int effective_element = element;
8419 int effective_action = action;
8420 int graphic = (direction == MV_NONE ?
8421 el_act2img(effective_element, effective_action) :
8422 el_act_dir2img(effective_element, effective_action,
8424 struct GraphicInfo *g = &graphic_info[graphic];
8425 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8431 Bitmap *debug_bitmap = g_em->bitmap;
8432 int debug_src_x = g_em->src_x;
8433 int debug_src_y = g_em->src_y;
8436 int frame = getAnimationFrame(g->anim_frames,
8439 g->anim_start_frame,
8442 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8444 g_em->bitmap = src_bitmap;
8445 g_em->src_x = src_x;
8446 g_em->src_y = src_y;
8447 g_em->src_offset_x = 0;
8448 g_em->src_offset_y = 0;
8449 g_em->dst_offset_x = 0;
8450 g_em->dst_offset_y = 0;
8451 g_em->width = TILEX;
8452 g_em->height = TILEY;
8456 /* skip check for EMC elements not contained in original EMC artwork */
8457 if (element == EL_PLAYER_3 ||
8458 element == EL_PLAYER_4)
8461 if (g_em->bitmap != debug_bitmap ||
8462 g_em->src_x != debug_src_x ||
8463 g_em->src_y != debug_src_y)
8465 static int last_i = -1;
8473 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
8474 p, i, element, element_info[element].token_name,
8475 element_action_info[effective_action].suffix, direction);
8477 if (element != effective_element)
8478 printf(" [%d ('%s')]",
8480 element_info[effective_element].token_name);
8484 if (g_em->bitmap != debug_bitmap)
8485 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
8486 j, (int)(g_em->bitmap), (int)(debug_bitmap));
8488 if (g_em->src_x != debug_src_x ||
8489 g_em->src_y != debug_src_y)
8490 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8492 g_em->src_x, g_em->src_y,
8493 g_em->src_x / 32, g_em->src_y / 32,
8494 debug_src_x, debug_src_y,
8495 debug_src_x / 32, debug_src_y / 32);
8497 num_em_gfx_errors++;
8507 printf("::: [%d errors found]\n", num_em_gfx_errors);
8513 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
8514 boolean any_player_moving,
8515 boolean any_player_snapping,
8516 boolean any_player_dropping)
8518 if (frame == 0 && !any_player_dropping)
8520 if (!local_player->was_waiting)
8522 if (!CheckSaveEngineSnapshotToList())
8525 local_player->was_waiting = TRUE;
8528 else if (any_player_moving || any_player_snapping || any_player_dropping)
8530 local_player->was_waiting = FALSE;
8534 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8535 boolean murphy_is_dropping)
8537 if (murphy_is_waiting)
8539 if (!local_player->was_waiting)
8541 if (!CheckSaveEngineSnapshotToList())
8544 local_player->was_waiting = TRUE;
8549 local_player->was_waiting = FALSE;
8553 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8554 boolean any_player_moving,
8555 boolean any_player_snapping,
8556 boolean any_player_dropping)
8558 if (tape.single_step && tape.recording && !tape.pausing)
8559 if (frame == 0 && !any_player_dropping)
8560 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8562 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8563 any_player_snapping, any_player_dropping);
8566 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8567 boolean murphy_is_dropping)
8569 boolean murphy_starts_dropping = FALSE;
8572 for (i = 0; i < MAX_PLAYERS; i++)
8573 if (stored_player[i].force_dropping)
8574 murphy_starts_dropping = TRUE;
8576 if (tape.single_step && tape.recording && !tape.pausing)
8577 if (murphy_is_waiting && !murphy_starts_dropping)
8578 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8580 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8583 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8584 int graphic, int sync_frame, int x, int y)
8586 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8588 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8591 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8593 return (IS_NEXT_FRAME(sync_frame, graphic));
8596 int getGraphicInfo_Delay(int graphic)
8598 return graphic_info[graphic].anim_delay;
8601 void PlayMenuSoundExt(int sound)
8603 if (sound == SND_UNDEFINED)
8606 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8607 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8610 if (IS_LOOP_SOUND(sound))
8611 PlaySoundLoop(sound);
8616 void PlayMenuSound()
8618 PlayMenuSoundExt(menu.sound[game_status]);
8621 void PlayMenuSoundStereo(int sound, int stereo_position)
8623 if (sound == SND_UNDEFINED)
8626 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8627 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8630 if (IS_LOOP_SOUND(sound))
8631 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8633 PlaySoundStereo(sound, stereo_position);
8636 void PlayMenuSoundIfLoopExt(int sound)
8638 if (sound == SND_UNDEFINED)
8641 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8642 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8645 if (IS_LOOP_SOUND(sound))
8646 PlaySoundLoop(sound);
8649 void PlayMenuSoundIfLoop()
8651 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8654 void PlayMenuMusicExt(int music)
8656 if (music == MUS_UNDEFINED)
8659 if (!setup.sound_music)
8665 void PlayMenuMusic()
8667 char *curr_music = getCurrentlyPlayingMusicFilename();
8668 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8670 if (!strEqual(curr_music, next_music))
8671 PlayMenuMusicExt(menu.music[game_status]);
8674 void PlayMenuSoundsAndMusic()
8680 static void FadeMenuSounds()
8685 static void FadeMenuMusic()
8687 char *curr_music = getCurrentlyPlayingMusicFilename();
8688 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8690 if (!strEqual(curr_music, next_music))
8694 void FadeMenuSoundsAndMusic()
8700 void PlaySoundActivating()
8703 PlaySound(SND_MENU_ITEM_ACTIVATING);
8707 void PlaySoundSelecting()
8710 PlaySound(SND_MENU_ITEM_SELECTING);
8714 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8716 boolean change_fullscreen = (setup.fullscreen !=
8717 video.fullscreen_enabled);
8718 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8719 setup.window_scaling_percent !=
8720 video.window_scaling_percent);
8722 if (change_window_scaling_percent && video.fullscreen_enabled)
8725 if (!change_window_scaling_percent && !video.fullscreen_available)
8728 #if defined(TARGET_SDL2)
8729 if (change_window_scaling_percent)
8731 SDLSetWindowScaling(setup.window_scaling_percent);
8735 else if (change_fullscreen)
8737 SDLSetWindowFullscreen(setup.fullscreen);
8739 /* set setup value according to successfully changed fullscreen mode */
8740 setup.fullscreen = video.fullscreen_enabled;
8746 if (change_fullscreen ||
8747 change_window_scaling_percent)
8749 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8751 /* save backbuffer content which gets lost when toggling fullscreen mode */
8752 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8754 if (change_window_scaling_percent)
8756 /* keep window mode, but change window scaling */
8757 video.fullscreen_enabled = TRUE; /* force new window scaling */
8760 /* toggle fullscreen */
8761 ChangeVideoModeIfNeeded(setup.fullscreen);
8763 /* set setup value according to successfully changed fullscreen mode */
8764 setup.fullscreen = video.fullscreen_enabled;
8766 /* restore backbuffer content from temporary backbuffer backup bitmap */
8767 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8769 FreeBitmap(tmp_backbuffer);
8771 /* update visible window/screen */
8772 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8776 void JoinRectangles(int *x, int *y, int *width, int *height,
8777 int x2, int y2, int width2, int height2)
8779 // do not join with "off-screen" rectangle
8780 if (x2 == -1 || y2 == -1)
8785 *width = MAX(*width, width2);
8786 *height = MAX(*height, height2);
8789 void SetAnimStatus(int anim_status_new)
8791 if (anim_status_new == GAME_MODE_MAIN)
8792 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
8793 else if (anim_status_new == GAME_MODE_SCORES)
8794 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
8796 global.anim_status_next = anim_status_new;
8798 // directly set screen modes that are entered without fading
8799 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
8800 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
8801 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
8802 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
8803 global.anim_status = global.anim_status_next;
8806 void SetGameStatus(int game_status_new)
8808 if (game_status_new != game_status)
8809 game_status_last_screen = game_status;
8811 game_status = game_status_new;
8813 SetAnimStatus(game_status_new);
8816 void SetFontStatus(int game_status_new)
8818 static int last_game_status = -1;
8820 if (game_status_new != -1)
8822 // set game status for font use after storing last game status
8823 last_game_status = game_status;
8824 game_status = game_status_new;
8828 // reset game status after font use from last stored game status
8829 game_status = last_game_status;
8833 void ResetFontStatus()
8838 void ChangeViewportPropertiesIfNeeded()
8840 int gfx_game_mode = game_status;
8841 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8843 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
8844 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8845 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8846 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8847 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8848 int new_win_xsize = vp_window->width;
8849 int new_win_ysize = vp_window->height;
8850 int border_size = vp_playfield->border_size;
8851 int new_sx = vp_playfield->x + border_size;
8852 int new_sy = vp_playfield->y + border_size;
8853 int new_sxsize = vp_playfield->width - 2 * border_size;
8854 int new_sysize = vp_playfield->height - 2 * border_size;
8855 int new_real_sx = vp_playfield->x;
8856 int new_real_sy = vp_playfield->y;
8857 int new_full_sxsize = vp_playfield->width;
8858 int new_full_sysize = vp_playfield->height;
8859 int new_dx = vp_door_1->x;
8860 int new_dy = vp_door_1->y;
8861 int new_dxsize = vp_door_1->width;
8862 int new_dysize = vp_door_1->height;
8863 int new_vx = vp_door_2->x;
8864 int new_vy = vp_door_2->y;
8865 int new_vxsize = vp_door_2->width;
8866 int new_vysize = vp_door_2->height;
8867 int new_ex = vp_door_3->x;
8868 int new_ey = vp_door_3->y;
8869 int new_exsize = vp_door_3->width;
8870 int new_eysize = vp_door_3->height;
8871 int new_tilesize_var =
8872 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8874 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8875 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8876 int new_scr_fieldx = new_sxsize / tilesize;
8877 int new_scr_fieldy = new_sysize / tilesize;
8878 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8879 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8880 boolean init_gfx_buffers = FALSE;
8881 boolean init_video_buffer = FALSE;
8882 boolean init_gadgets_and_anims = FALSE;
8883 boolean init_em_graphics = FALSE;
8885 if (new_win_xsize != WIN_XSIZE ||
8886 new_win_ysize != WIN_YSIZE)
8888 WIN_XSIZE = new_win_xsize;
8889 WIN_YSIZE = new_win_ysize;
8891 init_video_buffer = TRUE;
8892 init_gfx_buffers = TRUE;
8893 init_gadgets_and_anims = TRUE;
8895 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8898 if (new_scr_fieldx != SCR_FIELDX ||
8899 new_scr_fieldy != SCR_FIELDY)
8901 /* this always toggles between MAIN and GAME when using small tile size */
8903 SCR_FIELDX = new_scr_fieldx;
8904 SCR_FIELDY = new_scr_fieldy;
8906 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8917 new_sxsize != SXSIZE ||
8918 new_sysize != SYSIZE ||
8919 new_dxsize != DXSIZE ||
8920 new_dysize != DYSIZE ||
8921 new_vxsize != VXSIZE ||
8922 new_vysize != VYSIZE ||
8923 new_exsize != EXSIZE ||
8924 new_eysize != EYSIZE ||
8925 new_real_sx != REAL_SX ||
8926 new_real_sy != REAL_SY ||
8927 new_full_sxsize != FULL_SXSIZE ||
8928 new_full_sysize != FULL_SYSIZE ||
8929 new_tilesize_var != TILESIZE_VAR
8932 // ------------------------------------------------------------------------
8933 // determine next fading area for changed viewport definitions
8934 // ------------------------------------------------------------------------
8936 // start with current playfield area (default fading area)
8939 FADE_SXSIZE = FULL_SXSIZE;
8940 FADE_SYSIZE = FULL_SYSIZE;
8942 // add new playfield area if position or size has changed
8943 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8944 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8946 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8947 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8950 // add current and new door 1 area if position or size has changed
8951 if (new_dx != DX || new_dy != DY ||
8952 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8954 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8955 DX, DY, DXSIZE, DYSIZE);
8956 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8957 new_dx, new_dy, new_dxsize, new_dysize);
8960 // add current and new door 2 area if position or size has changed
8961 if (new_dx != VX || new_dy != VY ||
8962 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8964 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8965 VX, VY, VXSIZE, VYSIZE);
8966 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8967 new_vx, new_vy, new_vxsize, new_vysize);
8970 // ------------------------------------------------------------------------
8971 // handle changed tile size
8972 // ------------------------------------------------------------------------
8974 if (new_tilesize_var != TILESIZE_VAR)
8976 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8978 // changing tile size invalidates scroll values of engine snapshots
8979 FreeEngineSnapshotSingle();
8981 // changing tile size requires update of graphic mapping for EM engine
8982 init_em_graphics = TRUE;
8993 SXSIZE = new_sxsize;
8994 SYSIZE = new_sysize;
8995 DXSIZE = new_dxsize;
8996 DYSIZE = new_dysize;
8997 VXSIZE = new_vxsize;
8998 VYSIZE = new_vysize;
8999 EXSIZE = new_exsize;
9000 EYSIZE = new_eysize;
9001 REAL_SX = new_real_sx;
9002 REAL_SY = new_real_sy;
9003 FULL_SXSIZE = new_full_sxsize;
9004 FULL_SYSIZE = new_full_sysize;
9005 TILESIZE_VAR = new_tilesize_var;
9007 init_gfx_buffers = TRUE;
9008 init_gadgets_and_anims = TRUE;
9010 // printf("::: viewports: init_gfx_buffers\n");
9011 // printf("::: viewports: init_gadgets_and_anims\n");
9014 if (init_gfx_buffers)
9016 // printf("::: init_gfx_buffers\n");
9018 SCR_FIELDX = new_scr_fieldx_buffers;
9019 SCR_FIELDY = new_scr_fieldy_buffers;
9023 SCR_FIELDX = new_scr_fieldx;
9024 SCR_FIELDY = new_scr_fieldy;
9026 SetDrawDeactivationMask(REDRAW_NONE);
9027 SetDrawBackgroundMask(REDRAW_FIELD);
9030 if (init_video_buffer)
9032 // printf("::: init_video_buffer\n");
9034 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9035 InitImageTextures();
9038 if (init_gadgets_and_anims)
9040 // printf("::: init_gadgets_and_anims\n");
9043 InitGlobalAnimations();
9046 if (init_em_graphics)
9048 InitGraphicInfo_EM();