1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 /* select level set with EMC X11 graphics before activating EM GFX debugging */
27 #define DEBUG_EM_GFX FALSE
28 #define DEBUG_FRAME_TIME FALSE
30 /* tool button identifiers */
31 #define TOOL_CTRL_ID_YES 0
32 #define TOOL_CTRL_ID_NO 1
33 #define TOOL_CTRL_ID_CONFIRM 2
34 #define TOOL_CTRL_ID_PLAYER_1 3
35 #define TOOL_CTRL_ID_PLAYER_2 4
36 #define TOOL_CTRL_ID_PLAYER_3 5
37 #define TOOL_CTRL_ID_PLAYER_4 6
39 #define NUM_TOOL_BUTTONS 7
41 /* constants for number of doors and door parts */
43 #define NUM_PANELS NUM_DOORS
44 // #define NUM_PANELS 0
45 #define MAX_PARTS_PER_DOOR 8
46 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
47 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
50 struct DoorPartOrderInfo
56 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
58 struct DoorPartControlInfo
62 struct DoorPartPosInfo *pos;
65 static struct DoorPartControlInfo door_part_controls[] =
69 IMG_GFX_DOOR_1_PART_1,
74 IMG_GFX_DOOR_1_PART_2,
79 IMG_GFX_DOOR_1_PART_3,
84 IMG_GFX_DOOR_1_PART_4,
89 IMG_GFX_DOOR_1_PART_5,
94 IMG_GFX_DOOR_1_PART_6,
99 IMG_GFX_DOOR_1_PART_7,
104 IMG_GFX_DOOR_1_PART_8,
110 IMG_GFX_DOOR_2_PART_1,
115 IMG_GFX_DOOR_2_PART_2,
120 IMG_GFX_DOOR_2_PART_3,
125 IMG_GFX_DOOR_2_PART_4,
130 IMG_GFX_DOOR_2_PART_5,
135 IMG_GFX_DOOR_2_PART_6,
140 IMG_GFX_DOOR_2_PART_7,
145 IMG_GFX_DOOR_2_PART_8,
151 IMG_BACKGROUND_PANEL,
168 /* forward declaration for internal use */
169 static void UnmapToolButtons();
170 static void HandleToolButtons(struct GadgetInfo *);
171 static int el_act_dir2crm(int, int, int);
172 static int el_act2crm(int, int);
174 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
175 static int request_gadget_id = -1;
177 static char *print_if_not_empty(int element)
179 static char *s = NULL;
180 char *token_name = element_info[element].token_name;
185 s = checked_malloc(strlen(token_name) + 10 + 1);
187 if (element != EL_EMPTY)
188 sprintf(s, "%d\t['%s']", element, token_name);
190 sprintf(s, "%d", element);
195 int correctLevelPosX_EM(int lx)
198 lx -= (BorderElement != EL_EMPTY ? 1 : 0);
203 int correctLevelPosY_EM(int ly)
206 ly -= (BorderElement != EL_EMPTY ? 1 : 0);
211 static int getFieldbufferOffsetX_RND()
213 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
214 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
215 int dx_var = dx * TILESIZE_VAR / TILESIZE;
218 if (EVEN(SCR_FIELDX))
220 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
222 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
223 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
225 fx += (dx_var > 0 ? TILEX_VAR : 0);
232 if (full_lev_fieldx <= SCR_FIELDX)
234 if (EVEN(SCR_FIELDX))
235 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
237 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
243 static int getFieldbufferOffsetY_RND()
245 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
246 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
247 int dy_var = dy * TILESIZE_VAR / TILESIZE;
250 if (EVEN(SCR_FIELDY))
252 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
254 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
255 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
257 fy += (dy_var > 0 ? TILEY_VAR : 0);
264 if (full_lev_fieldy <= SCR_FIELDY)
266 if (EVEN(SCR_FIELDY))
267 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
269 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
275 static int getLevelFromScreenX_RND(int sx)
277 int fx = getFieldbufferOffsetX_RND();
280 int lx = LEVELX((px + dx) / TILESIZE_VAR);
285 static int getLevelFromScreenY_RND(int sy)
287 int fy = getFieldbufferOffsetY_RND();
290 int ly = LEVELY((py + dy) / TILESIZE_VAR);
295 static int getLevelFromScreenX_EM(int sx)
297 int level_xsize = level.native_em_level->lev->width;
298 int full_xsize = level_xsize * TILESIZE_VAR;
300 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
302 int fx = getFieldbufferOffsetX_EM();
305 int lx = LEVELX((px + dx) / TILESIZE_VAR);
307 lx = correctLevelPosX_EM(lx);
312 static int getLevelFromScreenY_EM(int sy)
314 int level_ysize = level.native_em_level->lev->height;
315 int full_ysize = level_ysize * TILESIZE_VAR;
317 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
319 int fy = getFieldbufferOffsetY_EM();
322 int ly = LEVELY((py + dy) / TILESIZE_VAR);
324 ly = correctLevelPosY_EM(ly);
329 static int getLevelFromScreenX_SP(int sx)
331 int menBorder = setup.sp_show_border_elements;
332 int level_xsize = level.native_sp_level->width;
333 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
335 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
337 int fx = getFieldbufferOffsetX_SP();
340 int lx = LEVELX((px + dx) / TILESIZE_VAR);
345 static int getLevelFromScreenY_SP(int sy)
347 int menBorder = setup.sp_show_border_elements;
348 int level_ysize = level.native_sp_level->height;
349 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
351 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
353 int fy = getFieldbufferOffsetY_SP();
356 int ly = LEVELY((py + dy) / TILESIZE_VAR);
361 static int getLevelFromScreenX_MM(int sx)
364 int level_xsize = level.native_mm_level->fieldx;
365 int full_xsize = level_xsize * TILESIZE_VAR;
367 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
371 int lx = px / TILESIZE_VAR;
376 static int getLevelFromScreenY_MM(int sy)
379 int level_ysize = level.native_mm_level->fieldy;
380 int full_ysize = level_ysize * TILESIZE_VAR;
382 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
386 int ly = py / TILESIZE_VAR;
391 int getLevelFromScreenX(int x)
393 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
394 return getLevelFromScreenX_EM(x);
395 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
396 return getLevelFromScreenX_SP(x);
397 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
398 return getLevelFromScreenX_MM(x);
400 return getLevelFromScreenX_RND(x);
403 int getLevelFromScreenY(int y)
405 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
406 return getLevelFromScreenY_EM(y);
407 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
408 return getLevelFromScreenY_SP(y);
409 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
410 return getLevelFromScreenY_MM(y);
412 return getLevelFromScreenY_RND(y);
415 void DumpTile(int x, int y)
421 printf_line("-", 79);
422 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
423 printf_line("-", 79);
425 if (!IN_LEV_FIELD(x, y))
427 printf("(not in level field)\n");
433 token_name = element_info[Feld[x][y]].token_name;
435 printf(" Feld: %d\t['%s']\n", Feld[x][y], token_name);
436 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
437 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
438 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
439 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
440 printf(" MovPos: %d\n", MovPos[x][y]);
441 printf(" MovDir: %d\n", MovDir[x][y]);
442 printf(" MovDelay: %d\n", MovDelay[x][y]);
443 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
444 printf(" CustomValue: %d\n", CustomValue[x][y]);
445 printf(" GfxElement: %d\n", GfxElement[x][y]);
446 printf(" GfxAction: %d\n", GfxAction[x][y]);
447 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
448 printf(" Player x/y: %d, %d\n", local_player->jx, local_player->jy);
452 void DumpTileFromScreen(int sx, int sy)
454 int lx = getLevelFromScreenX(sx);
455 int ly = getLevelFromScreenY(sy);
460 void SetDrawtoField(int mode)
462 if (mode == DRAW_TO_FIELDBUFFER)
468 BX2 = SCR_FIELDX + 1;
469 BY2 = SCR_FIELDY + 1;
471 drawto_field = fieldbuffer;
473 else /* DRAW_TO_BACKBUFFER */
479 BX2 = SCR_FIELDX - 1;
480 BY2 = SCR_FIELDY - 1;
482 drawto_field = backbuffer;
486 static void RedrawPlayfield_RND()
488 if (game.envelope_active)
491 DrawLevel(REDRAW_ALL);
495 void RedrawPlayfield()
497 if (game_status != GAME_MODE_PLAYING)
500 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
501 RedrawPlayfield_EM(TRUE);
502 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
503 RedrawPlayfield_SP(TRUE);
504 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
505 RedrawPlayfield_MM();
506 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
507 RedrawPlayfield_RND();
509 BlitScreenToBitmap(backbuffer);
511 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
515 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
518 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
519 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
521 if (x == -1 && y == -1)
524 if (draw_target == DRAW_TO_SCREEN)
525 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
527 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
530 static void DrawMaskedBorderExt_FIELD(int draw_target)
532 if (global.border_status >= GAME_MODE_MAIN &&
533 global.border_status <= GAME_MODE_PLAYING &&
534 border.draw_masked[global.border_status])
535 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
539 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
541 // when drawing to backbuffer, never draw border over open doors
542 if (draw_target == DRAW_TO_BACKBUFFER &&
543 (GetDoorState() & DOOR_OPEN_1))
546 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
547 (global.border_status != GAME_MODE_EDITOR ||
548 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
549 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
552 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
554 // when drawing to backbuffer, never draw border over open doors
555 if (draw_target == DRAW_TO_BACKBUFFER &&
556 (GetDoorState() & DOOR_OPEN_2))
559 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
560 global.border_status != GAME_MODE_EDITOR)
561 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
564 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
566 /* currently not available */
569 static void DrawMaskedBorderExt_ALL(int draw_target)
571 DrawMaskedBorderExt_FIELD(draw_target);
572 DrawMaskedBorderExt_DOOR_1(draw_target);
573 DrawMaskedBorderExt_DOOR_2(draw_target);
574 DrawMaskedBorderExt_DOOR_3(draw_target);
577 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
579 /* never draw masked screen borders on borderless screens */
580 if (global.border_status == GAME_MODE_LOADING ||
581 global.border_status == GAME_MODE_TITLE)
584 if (redraw_mask & REDRAW_ALL)
585 DrawMaskedBorderExt_ALL(draw_target);
588 if (redraw_mask & REDRAW_FIELD)
589 DrawMaskedBorderExt_FIELD(draw_target);
590 if (redraw_mask & REDRAW_DOOR_1)
591 DrawMaskedBorderExt_DOOR_1(draw_target);
592 if (redraw_mask & REDRAW_DOOR_2)
593 DrawMaskedBorderExt_DOOR_2(draw_target);
594 if (redraw_mask & REDRAW_DOOR_3)
595 DrawMaskedBorderExt_DOOR_3(draw_target);
599 void DrawMaskedBorder_FIELD()
601 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
604 void DrawMaskedBorder(int redraw_mask)
606 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
609 void DrawMaskedBorderToTarget(int draw_target)
611 if (draw_target == DRAW_TO_BACKBUFFER ||
612 draw_target == DRAW_TO_SCREEN)
614 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
618 int last_border_status = global.border_status;
620 if (draw_target == DRAW_TO_FADE_SOURCE)
622 global.border_status = gfx.fade_border_source_status;
623 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
625 else if (draw_target == DRAW_TO_FADE_TARGET)
627 global.border_status = gfx.fade_border_target_status;
628 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
631 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
633 global.border_status = last_border_status;
634 gfx.masked_border_bitmap_ptr = backbuffer;
638 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
640 int fx = getFieldbufferOffsetX_RND();
641 int fy = getFieldbufferOffsetY_RND();
643 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
646 void BlitScreenToBitmap(Bitmap *target_bitmap)
648 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
649 BlitScreenToBitmap_EM(target_bitmap);
650 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
651 BlitScreenToBitmap_SP(target_bitmap);
652 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
653 BlitScreenToBitmap_MM(target_bitmap);
654 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
655 BlitScreenToBitmap_RND(target_bitmap);
657 redraw_mask |= REDRAW_FIELD;
660 void DrawFramesPerSecond()
663 int font_nr = FONT_TEXT_2;
664 int font_width = getFontWidth(font_nr);
665 int draw_deactivation_mask = GetDrawDeactivationMask();
666 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
668 /* draw FPS with leading space (needed if field buffer deactivated) */
669 sprintf(text, " %04.1f fps", global.frames_per_second);
671 /* override draw deactivation mask (required for invisible warp mode) */
672 SetDrawDeactivationMask(REDRAW_NONE);
674 /* draw opaque FPS if field buffer deactivated, else draw masked FPS */
675 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
676 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
678 /* set draw deactivation mask to previous value */
679 SetDrawDeactivationMask(draw_deactivation_mask);
681 /* force full-screen redraw in this frame */
682 redraw_mask = REDRAW_ALL;
686 static void PrintFrameTimeDebugging()
688 static unsigned int last_counter = 0;
689 unsigned int counter = Counter();
690 int diff_1 = counter - last_counter;
691 int diff_2 = diff_1 - GAME_FRAME_DELAY;
693 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
694 char diff_bar[2 * diff_2_max + 5];
698 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
700 for (i = 0; i < diff_2_max; i++)
701 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
702 i >= diff_2_max - diff_2_cut ? '-' : ' ');
704 diff_bar[pos++] = '|';
706 for (i = 0; i < diff_2_max; i++)
707 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
709 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
711 diff_bar[pos++] = '\0';
713 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
716 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
719 last_counter = counter;
723 static int unifiedRedrawMask(int mask)
725 if (mask & REDRAW_ALL)
728 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
734 static boolean equalRedrawMasks(int mask_1, int mask_2)
736 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
741 static int last_redraw_mask = REDRAW_NONE;
743 // force screen redraw in every frame to continue drawing global animations
744 // (but always use the last redraw mask to prevent unwanted side effects)
745 if (redraw_mask == REDRAW_NONE)
746 redraw_mask = last_redraw_mask;
748 last_redraw_mask = redraw_mask;
751 // masked border now drawn immediately when blitting backbuffer to window
753 // draw masked border to all viewports, if defined
754 DrawMaskedBorder(redraw_mask);
757 // draw frames per second (only if debug mode is enabled)
758 if (redraw_mask & REDRAW_FPS)
759 DrawFramesPerSecond();
761 // remove playfield redraw before potentially merging with doors redraw
762 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
763 redraw_mask &= ~REDRAW_FIELD;
765 // redraw complete window if both playfield and (some) doors need redraw
766 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
767 redraw_mask = REDRAW_ALL;
769 /* although redrawing the whole window would be fine for normal gameplay,
770 being able to only redraw the playfield is required for deactivating
771 certain drawing areas (mainly playfield) to work, which is needed for
772 warp-forward to be fast enough (by skipping redraw of most frames) */
774 if (redraw_mask & REDRAW_ALL)
776 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
778 else if (redraw_mask & REDRAW_FIELD)
780 BlitBitmap(backbuffer, window,
781 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
783 else if (redraw_mask & REDRAW_DOORS)
785 // merge door areas to prevent calling screen redraw more than once
791 if (redraw_mask & REDRAW_DOOR_1)
795 x2 = MAX(x2, DX + DXSIZE);
796 y2 = MAX(y2, DY + DYSIZE);
799 if (redraw_mask & REDRAW_DOOR_2)
803 x2 = MAX(x2, VX + VXSIZE);
804 y2 = MAX(y2, VY + VYSIZE);
807 if (redraw_mask & REDRAW_DOOR_3)
811 x2 = MAX(x2, EX + EXSIZE);
812 y2 = MAX(y2, EY + EYSIZE);
815 // make sure that at least one pixel is blitted, and inside the screen
816 // (else nothing is blitted, causing the animations not to be updated)
817 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
818 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
819 x2 = MIN(MAX(1, x2), WIN_XSIZE);
820 y2 = MIN(MAX(1, y2), WIN_YSIZE);
822 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
825 redraw_mask = REDRAW_NONE;
828 PrintFrameTimeDebugging();
832 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
834 unsigned int frame_delay_value_old = GetVideoFrameDelay();
836 SetVideoFrameDelay(frame_delay_value);
840 SetVideoFrameDelay(frame_delay_value_old);
843 static int fade_type_skip = FADE_TYPE_NONE;
845 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
847 void (*draw_border_function)(void) = NULL;
848 int x, y, width, height;
849 int fade_delay, post_delay;
851 if (fade_type == FADE_TYPE_FADE_OUT)
853 if (fade_type_skip != FADE_TYPE_NONE)
855 /* skip all fade operations until specified fade operation */
856 if (fade_type & fade_type_skip)
857 fade_type_skip = FADE_TYPE_NONE;
862 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
866 redraw_mask |= fade_mask;
868 if (fade_type == FADE_TYPE_SKIP)
870 fade_type_skip = fade_mode;
875 fade_delay = fading.fade_delay;
876 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
878 if (fade_type_skip != FADE_TYPE_NONE)
880 /* skip all fade operations until specified fade operation */
881 if (fade_type & fade_type_skip)
882 fade_type_skip = FADE_TYPE_NONE;
887 if (global.autoplay_leveldir)
892 if (fade_mask == REDRAW_FIELD)
897 height = FADE_SYSIZE;
899 if (border.draw_masked_when_fading)
900 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
902 DrawMaskedBorder_FIELD(); /* draw once */
904 else /* REDRAW_ALL */
912 if (!setup.fade_screens ||
914 fading.fade_mode == FADE_MODE_NONE)
916 if (fade_mode == FADE_MODE_FADE_OUT)
919 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
921 redraw_mask &= ~fade_mask;
926 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
927 draw_border_function);
929 redraw_mask &= ~fade_mask;
932 static void SetScreenStates_BeforeFadingIn()
934 // temporarily set screen mode for animations to screen after fading in
935 global.anim_status = global.anim_status_next;
937 // store backbuffer with all animations that will be started after fading in
938 if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
939 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
941 // set screen mode for animations back to fading
942 global.anim_status = GAME_MODE_PSEUDO_FADING;
945 static void SetScreenStates_AfterFadingIn()
947 // store new source screen (to use correct masked border for fading)
948 gfx.fade_border_source_status = global.border_status;
950 global.anim_status = global.anim_status_next;
953 static void SetScreenStates_BeforeFadingOut()
955 // store new target screen (to use correct masked border for fading)
956 gfx.fade_border_target_status = game_status;
958 // set screen mode for animations to fading
959 global.anim_status = GAME_MODE_PSEUDO_FADING;
961 // store backbuffer with all animations that will be stopped for fading out
962 if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
963 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
966 static void SetScreenStates_AfterFadingOut()
968 global.border_status = game_status;
971 void FadeIn(int fade_mask)
973 SetScreenStates_BeforeFadingIn();
976 DrawMaskedBorder(REDRAW_ALL);
979 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
980 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
982 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
986 FADE_SXSIZE = FULL_SXSIZE;
987 FADE_SYSIZE = FULL_SYSIZE;
989 if (game_status == GAME_MODE_PLAYING &&
990 strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
991 SetOverlayActive(TRUE);
993 SetScreenStates_AfterFadingIn();
995 // force update of global animation status in case of rapid screen changes
996 redraw_mask = REDRAW_ALL;
1000 void FadeOut(int fade_mask)
1002 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
1003 if (!equalRedrawMasks(fade_mask, redraw_mask))
1006 SetScreenStates_BeforeFadingOut();
1008 SetOverlayActive(FALSE);
1011 DrawMaskedBorder(REDRAW_ALL);
1014 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1015 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1017 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1019 SetScreenStates_AfterFadingOut();
1022 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1024 static struct TitleFadingInfo fading_leave_stored;
1027 fading_leave_stored = fading_leave;
1029 fading = fading_leave_stored;
1032 void FadeSetEnterMenu()
1034 fading = menu.enter_menu;
1036 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1039 void FadeSetLeaveMenu()
1041 fading = menu.leave_menu;
1043 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1046 void FadeSetEnterScreen()
1048 fading = menu.enter_screen[game_status];
1050 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
1053 void FadeSetNextScreen()
1055 fading = menu.next_screen[game_status];
1057 // (do not overwrite fade mode set by FadeSetEnterScreen)
1058 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1061 void FadeSetLeaveScreen()
1063 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
1066 void FadeSetFromType(int type)
1068 if (type & TYPE_ENTER_SCREEN)
1069 FadeSetEnterScreen();
1070 else if (type & TYPE_ENTER)
1072 else if (type & TYPE_LEAVE)
1076 void FadeSetDisabled()
1078 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1080 fading = fading_none;
1083 void FadeSkipNextFadeIn()
1085 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1088 void FadeSkipNextFadeOut()
1090 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1093 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1095 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1097 return (graphic == IMG_UNDEFINED ? NULL :
1098 graphic_info[graphic].bitmap != NULL || redefined ?
1099 graphic_info[graphic].bitmap :
1100 graphic_info[default_graphic].bitmap);
1103 Bitmap *getBackgroundBitmap(int graphic)
1105 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1108 Bitmap *getGlobalBorderBitmap(int graphic)
1110 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1113 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1116 (status == GAME_MODE_MAIN ||
1117 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1118 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1119 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1120 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1123 return getGlobalBorderBitmap(graphic);
1126 void SetWindowBackgroundImageIfDefined(int graphic)
1128 if (graphic_info[graphic].bitmap)
1129 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1132 void SetMainBackgroundImageIfDefined(int graphic)
1134 if (graphic_info[graphic].bitmap)
1135 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1138 void SetDoorBackgroundImageIfDefined(int graphic)
1140 if (graphic_info[graphic].bitmap)
1141 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1144 void SetWindowBackgroundImage(int graphic)
1146 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1149 void SetMainBackgroundImage(int graphic)
1151 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1154 void SetDoorBackgroundImage(int graphic)
1156 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1159 void SetPanelBackground()
1161 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1163 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1164 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1166 SetDoorBackgroundBitmap(bitmap_db_panel);
1169 void DrawBackground(int x, int y, int width, int height)
1171 /* "drawto" might still point to playfield buffer here (hall of fame) */
1172 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1174 if (IN_GFX_FIELD_FULL(x, y))
1175 redraw_mask |= REDRAW_FIELD;
1176 else if (IN_GFX_DOOR_1(x, y))
1177 redraw_mask |= REDRAW_DOOR_1;
1178 else if (IN_GFX_DOOR_2(x, y))
1179 redraw_mask |= REDRAW_DOOR_2;
1180 else if (IN_GFX_DOOR_3(x, y))
1181 redraw_mask |= REDRAW_DOOR_3;
1184 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1186 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1188 if (font->bitmap == NULL)
1191 DrawBackground(x, y, width, height);
1194 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1196 struct GraphicInfo *g = &graphic_info[graphic];
1198 if (g->bitmap == NULL)
1201 DrawBackground(x, y, width, height);
1204 static int game_status_last = -1;
1205 static Bitmap *global_border_bitmap_last = NULL;
1206 static Bitmap *global_border_bitmap = NULL;
1207 static int real_sx_last = -1, real_sy_last = -1;
1208 static int full_sxsize_last = -1, full_sysize_last = -1;
1209 static int dx_last = -1, dy_last = -1;
1210 static int dxsize_last = -1, dysize_last = -1;
1211 static int vx_last = -1, vy_last = -1;
1212 static int vxsize_last = -1, vysize_last = -1;
1214 boolean CheckIfGlobalBorderHasChanged()
1216 // if game status has not changed, global border has not changed either
1217 if (game_status == game_status_last)
1220 // determine and store new global border bitmap for current game status
1221 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1223 return (global_border_bitmap_last != global_border_bitmap);
1226 boolean CheckIfGlobalBorderRedrawIsNeeded()
1228 // if game status has not changed, nothing has to be redrawn
1229 if (game_status == game_status_last)
1232 // redraw if last screen was title screen
1233 if (game_status_last == GAME_MODE_TITLE)
1236 // redraw if global screen border has changed
1237 if (CheckIfGlobalBorderHasChanged())
1240 // redraw if position or size of playfield area has changed
1241 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1242 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1245 // redraw if position or size of door area has changed
1246 if (dx_last != DX || dy_last != DY ||
1247 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1250 // redraw if position or size of tape area has changed
1251 if (vx_last != VX || vy_last != VY ||
1252 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1258 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1261 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1263 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1266 void RedrawGlobalBorder()
1268 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1270 RedrawGlobalBorderFromBitmap(bitmap);
1272 redraw_mask = REDRAW_ALL;
1275 static void RedrawGlobalBorderIfNeeded()
1277 if (game_status == game_status_last)
1280 // copy current draw buffer to later copy back areas that have not changed
1281 if (game_status_last != GAME_MODE_TITLE)
1282 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1284 if (CheckIfGlobalBorderRedrawIsNeeded())
1286 // redraw global screen border (or clear, if defined to be empty)
1287 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1289 // copy previous playfield and door areas, if they are defined on both
1290 // previous and current screen and if they still have the same size
1292 if (real_sx_last != -1 && real_sy_last != -1 &&
1293 REAL_SX != -1 && REAL_SY != -1 &&
1294 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1295 BlitBitmap(bitmap_db_store_1, backbuffer,
1296 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1299 if (dx_last != -1 && dy_last != -1 &&
1300 DX != -1 && DY != -1 &&
1301 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1302 BlitBitmap(bitmap_db_store_1, backbuffer,
1303 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1305 if (vx_last != -1 && vy_last != -1 &&
1306 VX != -1 && VY != -1 &&
1307 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1308 BlitBitmap(bitmap_db_store_1, backbuffer,
1309 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1311 redraw_mask = REDRAW_ALL;
1314 game_status_last = game_status;
1316 global_border_bitmap_last = global_border_bitmap;
1318 real_sx_last = REAL_SX;
1319 real_sy_last = REAL_SY;
1320 full_sxsize_last = FULL_SXSIZE;
1321 full_sysize_last = FULL_SYSIZE;
1324 dxsize_last = DXSIZE;
1325 dysize_last = DYSIZE;
1328 vxsize_last = VXSIZE;
1329 vysize_last = VYSIZE;
1334 RedrawGlobalBorderIfNeeded();
1336 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1337 /* (when entering hall of fame after playing) */
1338 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1340 /* !!! maybe this should be done before clearing the background !!! */
1341 if (game_status == GAME_MODE_PLAYING)
1343 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1344 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1348 SetDrawtoField(DRAW_TO_BACKBUFFER);
1352 void MarkTileDirty(int x, int y)
1354 redraw_mask |= REDRAW_FIELD;
1357 void SetBorderElement()
1361 BorderElement = EL_EMPTY;
1363 /* the MM game engine does not use a visible border element */
1364 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
1367 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1369 for (x = 0; x < lev_fieldx; x++)
1371 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1372 BorderElement = EL_STEELWALL;
1374 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1380 void FloodFillLevel(int from_x, int from_y, int fill_element,
1381 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1382 int max_fieldx, int max_fieldy)
1386 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1387 static int safety = 0;
1389 /* check if starting field still has the desired content */
1390 if (field[from_x][from_y] == fill_element)
1395 if (safety > max_fieldx * max_fieldy)
1396 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1398 old_element = field[from_x][from_y];
1399 field[from_x][from_y] = fill_element;
1401 for (i = 0; i < 4; i++)
1403 x = from_x + check[i][0];
1404 y = from_y + check[i][1];
1406 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1407 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1413 void SetRandomAnimationValue(int x, int y)
1415 gfx.anim_random_frame = GfxRandom[x][y];
1418 int getGraphicAnimationFrame(int graphic, int sync_frame)
1420 /* animation synchronized with global frame counter, not move position */
1421 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1422 sync_frame = FrameCounter;
1424 return getAnimationFrame(graphic_info[graphic].anim_frames,
1425 graphic_info[graphic].anim_delay,
1426 graphic_info[graphic].anim_mode,
1427 graphic_info[graphic].anim_start_frame,
1431 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1433 struct GraphicInfo *g = &graphic_info[graphic];
1434 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1436 if (tilesize == gfx.standard_tile_size)
1437 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1438 else if (tilesize == game.tile_size)
1439 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1441 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1444 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1445 boolean get_backside)
1447 struct GraphicInfo *g = &graphic_info[graphic];
1448 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1449 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1451 if (g->offset_y == 0) /* frames are ordered horizontally */
1453 int max_width = g->anim_frames_per_line * g->width;
1454 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1456 *x = pos % max_width;
1457 *y = src_y % g->height + pos / max_width * g->height;
1459 else if (g->offset_x == 0) /* frames are ordered vertically */
1461 int max_height = g->anim_frames_per_line * g->height;
1462 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1464 *x = src_x % g->width + pos / max_height * g->width;
1465 *y = pos % max_height;
1467 else /* frames are ordered diagonally */
1469 *x = src_x + frame * g->offset_x;
1470 *y = src_y + frame * g->offset_y;
1474 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1475 Bitmap **bitmap, int *x, int *y,
1476 boolean get_backside)
1478 struct GraphicInfo *g = &graphic_info[graphic];
1480 // if no in-game graphics defined, always use standard graphic size
1481 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1482 tilesize = TILESIZE;
1484 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1485 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1487 *x = *x * tilesize / g->tile_size;
1488 *y = *y * tilesize / g->tile_size;
1491 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1492 Bitmap **bitmap, int *x, int *y)
1494 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1497 void getFixedGraphicSource(int graphic, int frame,
1498 Bitmap **bitmap, int *x, int *y)
1500 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1503 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1505 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1508 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1509 int *x, int *y, boolean get_backside)
1511 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1515 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1517 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1520 void DrawGraphic(int x, int y, int graphic, int frame)
1523 if (!IN_SCR_FIELD(x, y))
1525 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1526 printf("DrawGraphic(): This should never happen!\n");
1531 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1534 MarkTileDirty(x, y);
1537 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1540 if (!IN_SCR_FIELD(x, y))
1542 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1543 printf("DrawGraphic(): This should never happen!\n");
1548 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1550 MarkTileDirty(x, y);
1553 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1559 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1561 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1564 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1570 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1571 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1574 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1577 if (!IN_SCR_FIELD(x, y))
1579 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1580 printf("DrawGraphicThruMask(): This should never happen!\n");
1585 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1588 MarkTileDirty(x, y);
1591 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1594 if (!IN_SCR_FIELD(x, y))
1596 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1597 printf("DrawGraphicThruMask(): This should never happen!\n");
1602 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1604 MarkTileDirty(x, y);
1607 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1613 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1615 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1619 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1620 int graphic, int frame)
1625 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1627 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1631 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1633 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1635 MarkTileDirty(x / tilesize, y / tilesize);
1638 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1644 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1645 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1648 void DrawMiniGraphic(int x, int y, int graphic)
1650 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1651 MarkTileDirty(x / 2, y / 2);
1654 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1659 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1660 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1663 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1664 int graphic, int frame,
1665 int cut_mode, int mask_mode)
1670 int width = TILEX, height = TILEY;
1673 if (dx || dy) /* shifted graphic */
1675 if (x < BX1) /* object enters playfield from the left */
1682 else if (x > BX2) /* object enters playfield from the right */
1688 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1694 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1696 else if (dx) /* general horizontal movement */
1697 MarkTileDirty(x + SIGN(dx), y);
1699 if (y < BY1) /* object enters playfield from the top */
1701 if (cut_mode == CUT_BELOW) /* object completely above top border */
1709 else if (y > BY2) /* object enters playfield from the bottom */
1715 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1721 else if (dy > 0 && cut_mode == CUT_ABOVE)
1723 if (y == BY2) /* object completely above bottom border */
1729 MarkTileDirty(x, y + 1);
1730 } /* object leaves playfield to the bottom */
1731 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1733 else if (dy) /* general vertical movement */
1734 MarkTileDirty(x, y + SIGN(dy));
1738 if (!IN_SCR_FIELD(x, y))
1740 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1741 printf("DrawGraphicShifted(): This should never happen!\n");
1746 width = width * TILESIZE_VAR / TILESIZE;
1747 height = height * TILESIZE_VAR / TILESIZE;
1748 cx = cx * TILESIZE_VAR / TILESIZE;
1749 cy = cy * TILESIZE_VAR / TILESIZE;
1750 dx = dx * TILESIZE_VAR / TILESIZE;
1751 dy = dy * TILESIZE_VAR / TILESIZE;
1753 if (width > 0 && height > 0)
1755 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1760 dst_x = FX + x * TILEX_VAR + dx;
1761 dst_y = FY + y * TILEY_VAR + dy;
1763 if (mask_mode == USE_MASKING)
1764 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1767 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1770 MarkTileDirty(x, y);
1774 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1775 int graphic, int frame,
1776 int cut_mode, int mask_mode)
1781 int width = TILEX_VAR, height = TILEY_VAR;
1784 int x2 = x + SIGN(dx);
1785 int y2 = y + SIGN(dy);
1787 /* movement with two-tile animations must be sync'ed with movement position,
1788 not with current GfxFrame (which can be higher when using slow movement) */
1789 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1790 int anim_frames = graphic_info[graphic].anim_frames;
1792 /* (we also need anim_delay here for movement animations with less frames) */
1793 int anim_delay = graphic_info[graphic].anim_delay;
1794 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1796 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1797 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1799 /* re-calculate animation frame for two-tile movement animation */
1800 frame = getGraphicAnimationFrame(graphic, sync_frame);
1802 /* check if movement start graphic inside screen area and should be drawn */
1803 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1805 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1807 dst_x = FX + x1 * TILEX_VAR;
1808 dst_y = FY + y1 * TILEY_VAR;
1810 if (mask_mode == USE_MASKING)
1811 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1814 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1817 MarkTileDirty(x1, y1);
1820 /* check if movement end graphic inside screen area and should be drawn */
1821 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1823 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1825 dst_x = FX + x2 * TILEX_VAR;
1826 dst_y = FY + y2 * TILEY_VAR;
1828 if (mask_mode == USE_MASKING)
1829 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1832 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1835 MarkTileDirty(x2, y2);
1839 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1840 int graphic, int frame,
1841 int cut_mode, int mask_mode)
1845 DrawGraphic(x, y, graphic, frame);
1850 if (graphic_info[graphic].double_movement) /* EM style movement images */
1851 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1853 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1856 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1857 int frame, int cut_mode)
1859 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1862 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1863 int cut_mode, int mask_mode)
1865 int lx = LEVELX(x), ly = LEVELY(y);
1869 if (IN_LEV_FIELD(lx, ly))
1871 SetRandomAnimationValue(lx, ly);
1873 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1874 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1876 /* do not use double (EM style) movement graphic when not moving */
1877 if (graphic_info[graphic].double_movement && !dx && !dy)
1879 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1880 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1883 else /* border element */
1885 graphic = el2img(element);
1886 frame = getGraphicAnimationFrame(graphic, -1);
1889 if (element == EL_EXPANDABLE_WALL)
1891 boolean left_stopped = FALSE, right_stopped = FALSE;
1893 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1894 left_stopped = TRUE;
1895 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1896 right_stopped = TRUE;
1898 if (left_stopped && right_stopped)
1900 else if (left_stopped)
1902 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1903 frame = graphic_info[graphic].anim_frames - 1;
1905 else if (right_stopped)
1907 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1908 frame = graphic_info[graphic].anim_frames - 1;
1913 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1914 else if (mask_mode == USE_MASKING)
1915 DrawGraphicThruMask(x, y, graphic, frame);
1917 DrawGraphic(x, y, graphic, frame);
1920 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1921 int cut_mode, int mask_mode)
1923 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1924 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1925 cut_mode, mask_mode);
1928 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1931 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1934 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1937 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1940 void DrawLevelElementThruMask(int x, int y, int element)
1942 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1945 void DrawLevelFieldThruMask(int x, int y)
1947 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1950 /* !!! implementation of quicksand is totally broken !!! */
1951 #define IS_CRUMBLED_TILE(x, y, e) \
1952 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1953 !IS_MOVING(x, y) || \
1954 (e) == EL_QUICKSAND_EMPTYING || \
1955 (e) == EL_QUICKSAND_FAST_EMPTYING))
1957 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1962 int width, height, cx, cy;
1963 int sx = SCREENX(x), sy = SCREENY(y);
1964 int crumbled_border_size = graphic_info[graphic].border_size;
1965 int crumbled_tile_size = graphic_info[graphic].tile_size;
1966 int crumbled_border_size_var =
1967 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
1970 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1972 for (i = 1; i < 4; i++)
1974 int dxx = (i & 1 ? dx : 0);
1975 int dyy = (i & 2 ? dy : 0);
1978 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1981 /* check if neighbour field is of same crumble type */
1982 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1983 graphic_info[graphic].class ==
1984 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1986 /* return if check prevents inner corner */
1987 if (same == (dxx == dx && dyy == dy))
1991 /* if we reach this point, we have an inner corner */
1993 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1995 width = crumbled_border_size_var;
1996 height = crumbled_border_size_var;
1997 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1998 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
2000 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
2001 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
2004 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
2009 int width, height, bx, by, cx, cy;
2010 int sx = SCREENX(x), sy = SCREENY(y);
2011 int crumbled_border_size = graphic_info[graphic].border_size;
2012 int crumbled_tile_size = graphic_info[graphic].tile_size;
2013 int crumbled_border_size_var =
2014 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2015 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2018 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2020 /* draw simple, sloppy, non-corner-accurate crumbled border */
2022 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2023 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2024 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2025 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2027 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2028 FX + sx * TILEX_VAR + cx,
2029 FY + sy * TILEY_VAR + cy);
2031 /* (remaining middle border part must be at least as big as corner part) */
2032 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2033 crumbled_border_size_var >= TILESIZE_VAR / 3)
2036 /* correct corners of crumbled border, if needed */
2038 for (i = -1; i <= 1; i += 2)
2040 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2041 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2042 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2045 /* check if neighbour field is of same crumble type */
2046 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2047 graphic_info[graphic].class ==
2048 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2050 /* no crumbled corner, but continued crumbled border */
2052 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2053 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2054 int b1 = (i == 1 ? crumbled_border_size_var :
2055 TILESIZE_VAR - 2 * crumbled_border_size_var);
2057 width = crumbled_border_size_var;
2058 height = crumbled_border_size_var;
2060 if (dir == 1 || dir == 2)
2075 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2077 FX + sx * TILEX_VAR + cx,
2078 FY + sy * TILEY_VAR + cy);
2083 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2085 int sx = SCREENX(x), sy = SCREENY(y);
2088 static int xy[4][2] =
2096 if (!IN_LEV_FIELD(x, y))
2099 element = TILE_GFX_ELEMENT(x, y);
2101 if (IS_CRUMBLED_TILE(x, y, element)) /* crumble field itself */
2103 if (!IN_SCR_FIELD(sx, sy))
2106 /* crumble field borders towards direct neighbour fields */
2107 for (i = 0; i < 4; i++)
2109 int xx = x + xy[i][0];
2110 int yy = y + xy[i][1];
2112 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2115 /* check if neighbour field is of same crumble type */
2116 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2117 graphic_info[graphic].class ==
2118 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2121 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2124 /* crumble inner field corners towards corner neighbour fields */
2125 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2126 graphic_info[graphic].anim_frames == 2)
2128 for (i = 0; i < 4; i++)
2130 int dx = (i & 1 ? +1 : -1);
2131 int dy = (i & 2 ? +1 : -1);
2133 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2137 MarkTileDirty(sx, sy);
2139 else /* center field is not crumbled -- crumble neighbour fields */
2141 /* crumble field borders of direct neighbour fields */
2142 for (i = 0; i < 4; i++)
2144 int xx = x + xy[i][0];
2145 int yy = y + xy[i][1];
2146 int sxx = sx + xy[i][0];
2147 int syy = sy + xy[i][1];
2149 if (!IN_LEV_FIELD(xx, yy) ||
2150 !IN_SCR_FIELD(sxx, syy))
2153 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2156 element = TILE_GFX_ELEMENT(xx, yy);
2158 if (!IS_CRUMBLED_TILE(xx, yy, element))
2161 graphic = el_act2crm(element, ACTION_DEFAULT);
2163 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2165 MarkTileDirty(sxx, syy);
2168 /* crumble inner field corners of corner neighbour fields */
2169 for (i = 0; i < 4; i++)
2171 int dx = (i & 1 ? +1 : -1);
2172 int dy = (i & 2 ? +1 : -1);
2178 if (!IN_LEV_FIELD(xx, yy) ||
2179 !IN_SCR_FIELD(sxx, syy))
2182 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2185 element = TILE_GFX_ELEMENT(xx, yy);
2187 if (!IS_CRUMBLED_TILE(xx, yy, element))
2190 graphic = el_act2crm(element, ACTION_DEFAULT);
2192 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2193 graphic_info[graphic].anim_frames == 2)
2194 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2196 MarkTileDirty(sxx, syy);
2201 void DrawLevelFieldCrumbled(int x, int y)
2205 if (!IN_LEV_FIELD(x, y))
2208 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2209 GfxElement[x][y] != EL_UNDEFINED &&
2210 GFX_CRUMBLED(GfxElement[x][y]))
2212 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2217 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2219 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2222 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2225 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2226 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2227 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2228 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2229 int sx = SCREENX(x), sy = SCREENY(y);
2231 DrawGraphic(sx, sy, graphic1, frame1);
2232 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2235 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2237 int sx = SCREENX(x), sy = SCREENY(y);
2238 static int xy[4][2] =
2247 /* crumble direct neighbour fields (required for field borders) */
2248 for (i = 0; i < 4; i++)
2250 int xx = x + xy[i][0];
2251 int yy = y + xy[i][1];
2252 int sxx = sx + xy[i][0];
2253 int syy = sy + xy[i][1];
2255 if (!IN_LEV_FIELD(xx, yy) ||
2256 !IN_SCR_FIELD(sxx, syy) ||
2257 !GFX_CRUMBLED(Feld[xx][yy]) ||
2261 DrawLevelField(xx, yy);
2264 /* crumble corner neighbour fields (required for inner field corners) */
2265 for (i = 0; i < 4; i++)
2267 int dx = (i & 1 ? +1 : -1);
2268 int dy = (i & 2 ? +1 : -1);
2274 if (!IN_LEV_FIELD(xx, yy) ||
2275 !IN_SCR_FIELD(sxx, syy) ||
2276 !GFX_CRUMBLED(Feld[xx][yy]) ||
2280 int element = TILE_GFX_ELEMENT(xx, yy);
2281 int graphic = el_act2crm(element, ACTION_DEFAULT);
2283 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2284 graphic_info[graphic].anim_frames == 2)
2285 DrawLevelField(xx, yy);
2289 static int getBorderElement(int x, int y)
2293 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2294 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2295 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2296 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2297 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2298 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2299 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2301 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2302 int steel_position = (x == -1 && y == -1 ? 0 :
2303 x == lev_fieldx && y == -1 ? 1 :
2304 x == -1 && y == lev_fieldy ? 2 :
2305 x == lev_fieldx && y == lev_fieldy ? 3 :
2306 x == -1 || x == lev_fieldx ? 4 :
2307 y == -1 || y == lev_fieldy ? 5 : 6);
2309 return border[steel_position][steel_type];
2312 void DrawScreenElement(int x, int y, int element)
2314 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2315 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2318 void DrawLevelElement(int x, int y, int element)
2320 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2321 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2324 void DrawScreenField(int x, int y)
2326 int lx = LEVELX(x), ly = LEVELY(y);
2327 int element, content;
2329 if (!IN_LEV_FIELD(lx, ly))
2331 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2334 element = getBorderElement(lx, ly);
2336 DrawScreenElement(x, y, element);
2341 element = Feld[lx][ly];
2342 content = Store[lx][ly];
2344 if (IS_MOVING(lx, ly))
2346 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2347 boolean cut_mode = NO_CUTTING;
2349 if (element == EL_QUICKSAND_EMPTYING ||
2350 element == EL_QUICKSAND_FAST_EMPTYING ||
2351 element == EL_MAGIC_WALL_EMPTYING ||
2352 element == EL_BD_MAGIC_WALL_EMPTYING ||
2353 element == EL_DC_MAGIC_WALL_EMPTYING ||
2354 element == EL_AMOEBA_DROPPING)
2355 cut_mode = CUT_ABOVE;
2356 else if (element == EL_QUICKSAND_FILLING ||
2357 element == EL_QUICKSAND_FAST_FILLING ||
2358 element == EL_MAGIC_WALL_FILLING ||
2359 element == EL_BD_MAGIC_WALL_FILLING ||
2360 element == EL_DC_MAGIC_WALL_FILLING)
2361 cut_mode = CUT_BELOW;
2363 if (cut_mode == CUT_ABOVE)
2364 DrawScreenElement(x, y, element);
2366 DrawScreenElement(x, y, EL_EMPTY);
2369 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2370 else if (cut_mode == NO_CUTTING)
2371 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2374 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2376 if (cut_mode == CUT_BELOW &&
2377 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2378 DrawLevelElement(lx, ly + 1, element);
2381 if (content == EL_ACID)
2383 int dir = MovDir[lx][ly];
2384 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2385 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2387 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2389 // prevent target field from being drawn again (but without masking)
2390 // (this would happen if target field is scanned after moving element)
2391 Stop[newlx][newly] = TRUE;
2394 else if (IS_BLOCKED(lx, ly))
2399 boolean cut_mode = NO_CUTTING;
2400 int element_old, content_old;
2402 Blocked2Moving(lx, ly, &oldx, &oldy);
2405 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2406 MovDir[oldx][oldy] == MV_RIGHT);
2408 element_old = Feld[oldx][oldy];
2409 content_old = Store[oldx][oldy];
2411 if (element_old == EL_QUICKSAND_EMPTYING ||
2412 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2413 element_old == EL_MAGIC_WALL_EMPTYING ||
2414 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2415 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2416 element_old == EL_AMOEBA_DROPPING)
2417 cut_mode = CUT_ABOVE;
2419 DrawScreenElement(x, y, EL_EMPTY);
2422 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2424 else if (cut_mode == NO_CUTTING)
2425 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2428 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2431 else if (IS_DRAWABLE(element))
2432 DrawScreenElement(x, y, element);
2434 DrawScreenElement(x, y, EL_EMPTY);
2437 void DrawLevelField(int x, int y)
2439 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2440 DrawScreenField(SCREENX(x), SCREENY(y));
2441 else if (IS_MOVING(x, y))
2445 Moving2Blocked(x, y, &newx, &newy);
2446 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2447 DrawScreenField(SCREENX(newx), SCREENY(newy));
2449 else if (IS_BLOCKED(x, y))
2453 Blocked2Moving(x, y, &oldx, &oldy);
2454 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2455 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2459 void DrawSizedWall_MM(int dst_x, int dst_y, int element, int tilesize,
2460 int (*el2img_function)(int))
2462 int element_base = map_mm_wall_element(element);
2463 int element_bits = (IS_DF_WALL(element) ?
2464 element - EL_DF_WALL_START :
2465 element - EL_MM_WALL_START) & 0x000f;
2466 int graphic = el2img_function(element_base);
2467 int tilesize_draw = tilesize / 2;
2472 getSizedGraphicSource(graphic, 0, tilesize_draw, &src_bitmap, &src_x, &src_y);
2474 for (i = 0; i < 4; i++)
2476 int dst_draw_x = dst_x + (i % 2) * tilesize_draw;
2477 int dst_draw_y = dst_y + (i / 2) * tilesize_draw;
2479 if (element_bits & (1 << i))
2480 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize_draw, tilesize_draw,
2481 dst_draw_x, dst_draw_y);
2483 ClearRectangle(drawto, dst_draw_x, dst_draw_y,
2484 tilesize_draw, tilesize_draw);
2488 void DrawSizedElement(int x, int y, int element, int tilesize)
2490 if (IS_MM_WALL(element))
2492 DrawSizedWall_MM(SX + x * tilesize, SY + y * tilesize,
2493 element, tilesize, el2edimg);
2497 int graphic = el2edimg(element);
2499 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2503 void DrawMiniElement(int x, int y, int element)
2507 graphic = el2edimg(element);
2508 DrawMiniGraphic(x, y, graphic);
2511 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2514 int x = sx + scroll_x, y = sy + scroll_y;
2516 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2517 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2518 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2519 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2521 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2524 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2526 int x = sx + scroll_x, y = sy + scroll_y;
2528 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2529 DrawMiniElement(sx, sy, EL_EMPTY);
2530 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2531 DrawMiniElement(sx, sy, Feld[x][y]);
2533 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2536 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2537 int x, int y, int xsize, int ysize,
2538 int tile_width, int tile_height)
2542 int dst_x = startx + x * tile_width;
2543 int dst_y = starty + y * tile_height;
2544 int width = graphic_info[graphic].width;
2545 int height = graphic_info[graphic].height;
2546 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2547 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2548 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2549 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2550 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2551 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2552 boolean draw_masked = graphic_info[graphic].draw_masked;
2554 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2556 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2558 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2562 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2563 inner_sx + (x - 1) * tile_width % inner_width);
2564 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2565 inner_sy + (y - 1) * tile_height % inner_height);
2568 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2571 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2575 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2576 int x, int y, int xsize, int ysize, int font_nr)
2578 int font_width = getFontWidth(font_nr);
2579 int font_height = getFontHeight(font_nr);
2581 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2582 font_width, font_height);
2585 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2587 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2588 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2589 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2590 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2591 boolean no_delay = (tape.warp_forward);
2592 unsigned int anim_delay = 0;
2593 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2594 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2595 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2596 int font_width = getFontWidth(font_nr);
2597 int font_height = getFontHeight(font_nr);
2598 int max_xsize = level.envelope[envelope_nr].xsize;
2599 int max_ysize = level.envelope[envelope_nr].ysize;
2600 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2601 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2602 int xend = max_xsize;
2603 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2604 int xstep = (xstart < xend ? 1 : 0);
2605 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2607 int end = MAX(xend - xstart, yend - ystart);
2610 for (i = start; i <= end; i++)
2612 int last_frame = end; // last frame of this "for" loop
2613 int x = xstart + i * xstep;
2614 int y = ystart + i * ystep;
2615 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2616 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2617 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2618 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2621 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2623 BlitScreenToBitmap(backbuffer);
2625 SetDrawtoField(DRAW_TO_BACKBUFFER);
2627 for (yy = 0; yy < ysize; yy++)
2628 for (xx = 0; xx < xsize; xx++)
2629 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2631 DrawTextBuffer(sx + font_width, sy + font_height,
2632 level.envelope[envelope_nr].text, font_nr, max_xsize,
2633 xsize - 2, ysize - 2, 0, mask_mode,
2634 level.envelope[envelope_nr].autowrap,
2635 level.envelope[envelope_nr].centered, FALSE);
2637 redraw_mask |= REDRAW_FIELD;
2640 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2644 void ShowEnvelope(int envelope_nr)
2646 int element = EL_ENVELOPE_1 + envelope_nr;
2647 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2648 int sound_opening = element_info[element].sound[ACTION_OPENING];
2649 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2650 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2651 boolean no_delay = (tape.warp_forward);
2652 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2653 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2654 int anim_mode = graphic_info[graphic].anim_mode;
2655 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2656 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2658 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2660 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2662 if (anim_mode == ANIM_DEFAULT)
2663 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2665 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2668 Delay(wait_delay_value);
2670 WaitForEventToContinue();
2672 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2674 if (anim_mode != ANIM_NONE)
2675 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2677 if (anim_mode == ANIM_DEFAULT)
2678 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2680 game.envelope_active = FALSE;
2682 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2684 redraw_mask |= REDRAW_FIELD;
2688 static void setRequestBasePosition(int *x, int *y)
2690 int sx_base, sy_base;
2692 if (request.x != -1)
2693 sx_base = request.x;
2694 else if (request.align == ALIGN_LEFT)
2696 else if (request.align == ALIGN_RIGHT)
2697 sx_base = SX + SXSIZE;
2699 sx_base = SX + SXSIZE / 2;
2701 if (request.y != -1)
2702 sy_base = request.y;
2703 else if (request.valign == VALIGN_TOP)
2705 else if (request.valign == VALIGN_BOTTOM)
2706 sy_base = SY + SYSIZE;
2708 sy_base = SY + SYSIZE / 2;
2714 static void setRequestPositionExt(int *x, int *y, int width, int height,
2715 boolean add_border_size)
2717 int border_size = request.border_size;
2718 int sx_base, sy_base;
2721 setRequestBasePosition(&sx_base, &sy_base);
2723 if (request.align == ALIGN_LEFT)
2725 else if (request.align == ALIGN_RIGHT)
2726 sx = sx_base - width;
2728 sx = sx_base - width / 2;
2730 if (request.valign == VALIGN_TOP)
2732 else if (request.valign == VALIGN_BOTTOM)
2733 sy = sy_base - height;
2735 sy = sy_base - height / 2;
2737 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2738 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2740 if (add_border_size)
2750 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2752 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2755 void DrawEnvelopeRequest(char *text)
2757 char *text_final = text;
2758 char *text_door_style = NULL;
2759 int graphic = IMG_BACKGROUND_REQUEST;
2760 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2761 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2762 int font_nr = FONT_REQUEST;
2763 int font_width = getFontWidth(font_nr);
2764 int font_height = getFontHeight(font_nr);
2765 int border_size = request.border_size;
2766 int line_spacing = request.line_spacing;
2767 int line_height = font_height + line_spacing;
2768 int max_text_width = request.width - 2 * border_size;
2769 int max_text_height = request.height - 2 * border_size;
2770 int line_length = max_text_width / font_width;
2771 int max_lines = max_text_height / line_height;
2772 int text_width = line_length * font_width;
2773 int width = request.width;
2774 int height = request.height;
2775 int tile_size = MAX(request.step_offset, 1);
2776 int x_steps = width / tile_size;
2777 int y_steps = height / tile_size;
2778 int sx_offset = border_size;
2779 int sy_offset = border_size;
2783 if (request.centered)
2784 sx_offset = (request.width - text_width) / 2;
2786 if (request.wrap_single_words && !request.autowrap)
2788 char *src_text_ptr, *dst_text_ptr;
2790 text_door_style = checked_malloc(2 * strlen(text) + 1);
2792 src_text_ptr = text;
2793 dst_text_ptr = text_door_style;
2795 while (*src_text_ptr)
2797 if (*src_text_ptr == ' ' ||
2798 *src_text_ptr == '?' ||
2799 *src_text_ptr == '!')
2800 *dst_text_ptr++ = '\n';
2802 if (*src_text_ptr != ' ')
2803 *dst_text_ptr++ = *src_text_ptr;
2808 *dst_text_ptr = '\0';
2810 text_final = text_door_style;
2813 setRequestPosition(&sx, &sy, FALSE);
2815 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2817 for (y = 0; y < y_steps; y++)
2818 for (x = 0; x < x_steps; x++)
2819 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2820 x, y, x_steps, y_steps,
2821 tile_size, tile_size);
2823 /* force DOOR font inside door area */
2824 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2826 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2827 line_length, -1, max_lines, line_spacing, mask_mode,
2828 request.autowrap, request.centered, FALSE);
2832 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2833 RedrawGadget(tool_gadget[i]);
2835 // store readily prepared envelope request for later use when animating
2836 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2838 if (text_door_style)
2839 free(text_door_style);
2842 void AnimateEnvelopeRequest(int anim_mode, int action)
2844 int graphic = IMG_BACKGROUND_REQUEST;
2845 boolean draw_masked = graphic_info[graphic].draw_masked;
2846 int delay_value_normal = request.step_delay;
2847 int delay_value_fast = delay_value_normal / 2;
2848 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2849 boolean no_delay = (tape.warp_forward);
2850 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2851 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
2852 unsigned int anim_delay = 0;
2854 int tile_size = MAX(request.step_offset, 1);
2855 int max_xsize = request.width / tile_size;
2856 int max_ysize = request.height / tile_size;
2857 int max_xsize_inner = max_xsize - 2;
2858 int max_ysize_inner = max_ysize - 2;
2860 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2861 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2862 int xend = max_xsize_inner;
2863 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2864 int xstep = (xstart < xend ? 1 : 0);
2865 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2867 int end = MAX(xend - xstart, yend - ystart);
2870 if (setup.quick_doors)
2877 for (i = start; i <= end; i++)
2879 int last_frame = end; // last frame of this "for" loop
2880 int x = xstart + i * xstep;
2881 int y = ystart + i * ystep;
2882 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2883 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2884 int xsize_size_left = (xsize - 1) * tile_size;
2885 int ysize_size_top = (ysize - 1) * tile_size;
2886 int max_xsize_pos = (max_xsize - 1) * tile_size;
2887 int max_ysize_pos = (max_ysize - 1) * tile_size;
2888 int width = xsize * tile_size;
2889 int height = ysize * tile_size;
2894 setRequestPosition(&src_x, &src_y, FALSE);
2895 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2897 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2899 for (yy = 0; yy < 2; yy++)
2901 for (xx = 0; xx < 2; xx++)
2903 int src_xx = src_x + xx * max_xsize_pos;
2904 int src_yy = src_y + yy * max_ysize_pos;
2905 int dst_xx = dst_x + xx * xsize_size_left;
2906 int dst_yy = dst_y + yy * ysize_size_top;
2907 int xx_size = (xx ? tile_size : xsize_size_left);
2908 int yy_size = (yy ? tile_size : ysize_size_top);
2911 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
2912 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2914 BlitBitmap(bitmap_db_store_2, backbuffer,
2915 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2919 redraw_mask |= REDRAW_FIELD;
2923 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2927 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2929 int graphic = IMG_BACKGROUND_REQUEST;
2930 int sound_opening = SND_REQUEST_OPENING;
2931 int sound_closing = SND_REQUEST_CLOSING;
2932 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2933 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2934 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2935 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2936 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2938 if (game_status == GAME_MODE_PLAYING)
2939 BlitScreenToBitmap(backbuffer);
2941 SetDrawtoField(DRAW_TO_BACKBUFFER);
2943 // SetDrawBackgroundMask(REDRAW_NONE);
2945 if (action == ACTION_OPENING)
2947 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2949 if (req_state & REQ_ASK)
2951 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2952 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2954 else if (req_state & REQ_CONFIRM)
2956 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2958 else if (req_state & REQ_PLAYER)
2960 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2961 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2962 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2963 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2966 DrawEnvelopeRequest(text);
2969 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2971 if (action == ACTION_OPENING)
2973 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2975 if (anim_mode == ANIM_DEFAULT)
2976 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2978 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2982 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2984 if (anim_mode != ANIM_NONE)
2985 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2987 if (anim_mode == ANIM_DEFAULT)
2988 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2991 game.envelope_active = FALSE;
2993 if (action == ACTION_CLOSING)
2994 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2996 // SetDrawBackgroundMask(last_draw_background_mask);
2998 redraw_mask |= REDRAW_FIELD;
3002 if (action == ACTION_CLOSING &&
3003 game_status == GAME_MODE_PLAYING &&
3004 level.game_engine_type == GAME_ENGINE_TYPE_RND)
3005 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3008 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
3010 if (IS_MM_WALL(element))
3012 DrawSizedWall_MM(dst_x, dst_y, element, tilesize, el2preimg);
3018 int graphic = el2preimg(element);
3020 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
3021 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize,
3026 void DrawLevel(int draw_background_mask)
3030 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
3031 SetDrawBackgroundMask(draw_background_mask);
3035 for (x = BX1; x <= BX2; x++)
3036 for (y = BY1; y <= BY2; y++)
3037 DrawScreenField(x, y);
3039 redraw_mask |= REDRAW_FIELD;
3042 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
3047 for (x = 0; x < size_x; x++)
3048 for (y = 0; y < size_y; y++)
3049 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
3051 redraw_mask |= REDRAW_FIELD;
3054 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3058 for (x = 0; x < size_x; x++)
3059 for (y = 0; y < size_y; y++)
3060 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3062 redraw_mask |= REDRAW_FIELD;
3065 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3067 boolean show_level_border = (BorderElement != EL_EMPTY);
3068 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3069 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3070 int tile_size = preview.tile_size;
3071 int preview_width = preview.xsize * tile_size;
3072 int preview_height = preview.ysize * tile_size;
3073 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3074 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3075 int real_preview_width = real_preview_xsize * tile_size;
3076 int real_preview_height = real_preview_ysize * tile_size;
3077 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3078 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3081 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3084 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3086 dst_x += (preview_width - real_preview_width) / 2;
3087 dst_y += (preview_height - real_preview_height) / 2;
3089 for (x = 0; x < real_preview_xsize; x++)
3091 for (y = 0; y < real_preview_ysize; y++)
3093 int lx = from_x + x + (show_level_border ? -1 : 0);
3094 int ly = from_y + y + (show_level_border ? -1 : 0);
3095 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3096 getBorderElement(lx, ly));
3098 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3099 element, tile_size);
3103 redraw_mask |= REDRAW_FIELD;
3106 #define MICROLABEL_EMPTY 0
3107 #define MICROLABEL_LEVEL_NAME 1
3108 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3109 #define MICROLABEL_LEVEL_AUTHOR 3
3110 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3111 #define MICROLABEL_IMPORTED_FROM 5
3112 #define MICROLABEL_IMPORTED_BY_HEAD 6
3113 #define MICROLABEL_IMPORTED_BY 7
3115 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3117 int max_text_width = SXSIZE;
3118 int font_width = getFontWidth(font_nr);
3120 if (pos->align == ALIGN_CENTER)
3121 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3122 else if (pos->align == ALIGN_RIGHT)
3123 max_text_width = pos->x;
3125 max_text_width = SXSIZE - pos->x;
3127 return max_text_width / font_width;
3130 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3132 char label_text[MAX_OUTPUT_LINESIZE + 1];
3133 int max_len_label_text;
3134 int font_nr = pos->font;
3137 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3140 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3141 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3142 mode == MICROLABEL_IMPORTED_BY_HEAD)
3143 font_nr = pos->font_alt;
3145 max_len_label_text = getMaxTextLength(pos, font_nr);
3147 if (pos->size != -1)
3148 max_len_label_text = pos->size;
3150 for (i = 0; i < max_len_label_text; i++)
3151 label_text[i] = ' ';
3152 label_text[max_len_label_text] = '\0';
3154 if (strlen(label_text) > 0)
3155 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3158 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3159 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3160 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3161 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3162 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3163 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3164 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3165 max_len_label_text);
3166 label_text[max_len_label_text] = '\0';
3168 if (strlen(label_text) > 0)
3169 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3171 redraw_mask |= REDRAW_FIELD;
3174 static void DrawPreviewLevelLabel(int mode)
3176 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3179 static void DrawPreviewLevelInfo(int mode)
3181 if (mode == MICROLABEL_LEVEL_NAME)
3182 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3183 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3184 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3187 static void DrawPreviewLevelExt(boolean restart)
3189 static unsigned int scroll_delay = 0;
3190 static unsigned int label_delay = 0;
3191 static int from_x, from_y, scroll_direction;
3192 static int label_state, label_counter;
3193 unsigned int scroll_delay_value = preview.step_delay;
3194 boolean show_level_border = (BorderElement != EL_EMPTY);
3195 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3196 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3203 if (preview.anim_mode == ANIM_CENTERED)
3205 if (level_xsize > preview.xsize)
3206 from_x = (level_xsize - preview.xsize) / 2;
3207 if (level_ysize > preview.ysize)
3208 from_y = (level_ysize - preview.ysize) / 2;
3211 from_x += preview.xoffset;
3212 from_y += preview.yoffset;
3214 scroll_direction = MV_RIGHT;
3218 DrawPreviewLevelPlayfield(from_x, from_y);
3219 DrawPreviewLevelLabel(label_state);
3221 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3222 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3224 /* initialize delay counters */
3225 DelayReached(&scroll_delay, 0);
3226 DelayReached(&label_delay, 0);
3228 if (leveldir_current->name)
3230 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3231 char label_text[MAX_OUTPUT_LINESIZE + 1];
3232 int font_nr = pos->font;
3233 int max_len_label_text = getMaxTextLength(pos, font_nr);
3235 if (pos->size != -1)
3236 max_len_label_text = pos->size;
3238 strncpy(label_text, leveldir_current->name, max_len_label_text);
3239 label_text[max_len_label_text] = '\0';
3241 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3242 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3248 /* scroll preview level, if needed */
3249 if (preview.anim_mode != ANIM_NONE &&
3250 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3251 DelayReached(&scroll_delay, scroll_delay_value))
3253 switch (scroll_direction)
3258 from_x -= preview.step_offset;
3259 from_x = (from_x < 0 ? 0 : from_x);
3262 scroll_direction = MV_UP;
3266 if (from_x < level_xsize - preview.xsize)
3268 from_x += preview.step_offset;
3269 from_x = (from_x > level_xsize - preview.xsize ?
3270 level_xsize - preview.xsize : from_x);
3273 scroll_direction = MV_DOWN;
3279 from_y -= preview.step_offset;
3280 from_y = (from_y < 0 ? 0 : from_y);
3283 scroll_direction = MV_RIGHT;
3287 if (from_y < level_ysize - preview.ysize)
3289 from_y += preview.step_offset;
3290 from_y = (from_y > level_ysize - preview.ysize ?
3291 level_ysize - preview.ysize : from_y);
3294 scroll_direction = MV_LEFT;
3301 DrawPreviewLevelPlayfield(from_x, from_y);
3304 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
3305 /* redraw micro level label, if needed */
3306 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3307 !strEqual(level.author, ANONYMOUS_NAME) &&
3308 !strEqual(level.author, leveldir_current->name) &&
3309 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3311 int max_label_counter = 23;
3313 if (leveldir_current->imported_from != NULL &&
3314 strlen(leveldir_current->imported_from) > 0)
3315 max_label_counter += 14;
3316 if (leveldir_current->imported_by != NULL &&
3317 strlen(leveldir_current->imported_by) > 0)
3318 max_label_counter += 14;
3320 label_counter = (label_counter + 1) % max_label_counter;
3321 label_state = (label_counter >= 0 && label_counter <= 7 ?
3322 MICROLABEL_LEVEL_NAME :
3323 label_counter >= 9 && label_counter <= 12 ?
3324 MICROLABEL_LEVEL_AUTHOR_HEAD :
3325 label_counter >= 14 && label_counter <= 21 ?
3326 MICROLABEL_LEVEL_AUTHOR :
3327 label_counter >= 23 && label_counter <= 26 ?
3328 MICROLABEL_IMPORTED_FROM_HEAD :
3329 label_counter >= 28 && label_counter <= 35 ?
3330 MICROLABEL_IMPORTED_FROM :
3331 label_counter >= 37 && label_counter <= 40 ?
3332 MICROLABEL_IMPORTED_BY_HEAD :
3333 label_counter >= 42 && label_counter <= 49 ?
3334 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3336 if (leveldir_current->imported_from == NULL &&
3337 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3338 label_state == MICROLABEL_IMPORTED_FROM))
3339 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3340 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3342 DrawPreviewLevelLabel(label_state);
3346 void DrawPreviewLevelInitial()
3348 DrawPreviewLevelExt(TRUE);
3351 void DrawPreviewLevelAnimation()
3353 DrawPreviewLevelExt(FALSE);
3356 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3357 int graphic, int sync_frame,
3360 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3362 if (mask_mode == USE_MASKING)
3363 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3365 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3368 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3369 int graphic, int sync_frame, int mask_mode)
3371 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3373 if (mask_mode == USE_MASKING)
3374 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3376 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3379 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3381 int lx = LEVELX(x), ly = LEVELY(y);
3383 if (!IN_SCR_FIELD(x, y))
3386 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3387 graphic, GfxFrame[lx][ly], NO_MASKING);
3389 MarkTileDirty(x, y);
3392 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3394 int lx = LEVELX(x), ly = LEVELY(y);
3396 if (!IN_SCR_FIELD(x, y))
3399 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3400 graphic, GfxFrame[lx][ly], NO_MASKING);
3401 MarkTileDirty(x, y);
3404 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3406 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3409 void DrawLevelElementAnimation(int x, int y, int element)
3411 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3413 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3416 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3418 int sx = SCREENX(x), sy = SCREENY(y);
3420 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3423 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3426 DrawGraphicAnimation(sx, sy, graphic);
3429 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3430 DrawLevelFieldCrumbled(x, y);
3432 if (GFX_CRUMBLED(Feld[x][y]))
3433 DrawLevelFieldCrumbled(x, y);
3437 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3439 int sx = SCREENX(x), sy = SCREENY(y);
3442 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3445 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3447 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3450 DrawGraphicAnimation(sx, sy, graphic);
3452 if (GFX_CRUMBLED(element))
3453 DrawLevelFieldCrumbled(x, y);
3456 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3458 if (player->use_murphy)
3460 /* this works only because currently only one player can be "murphy" ... */
3461 static int last_horizontal_dir = MV_LEFT;
3462 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3464 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3465 last_horizontal_dir = move_dir;
3467 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3469 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3471 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3477 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3480 static boolean equalGraphics(int graphic1, int graphic2)
3482 struct GraphicInfo *g1 = &graphic_info[graphic1];
3483 struct GraphicInfo *g2 = &graphic_info[graphic2];
3485 return (g1->bitmap == g2->bitmap &&
3486 g1->src_x == g2->src_x &&
3487 g1->src_y == g2->src_y &&
3488 g1->anim_frames == g2->anim_frames &&
3489 g1->anim_delay == g2->anim_delay &&
3490 g1->anim_mode == g2->anim_mode);
3493 void DrawAllPlayers()
3497 for (i = 0; i < MAX_PLAYERS; i++)
3498 if (stored_player[i].active)
3499 DrawPlayer(&stored_player[i]);
3502 void DrawPlayerField(int x, int y)
3504 if (!IS_PLAYER(x, y))
3507 DrawPlayer(PLAYERINFO(x, y));
3510 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3512 void DrawPlayer(struct PlayerInfo *player)
3514 int jx = player->jx;
3515 int jy = player->jy;
3516 int move_dir = player->MovDir;
3517 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3518 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3519 int last_jx = (player->is_moving ? jx - dx : jx);
3520 int last_jy = (player->is_moving ? jy - dy : jy);
3521 int next_jx = jx + dx;
3522 int next_jy = jy + dy;
3523 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3524 boolean player_is_opaque = FALSE;
3525 int sx = SCREENX(jx), sy = SCREENY(jy);
3526 int sxx = 0, syy = 0;
3527 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3529 int action = ACTION_DEFAULT;
3530 int last_player_graphic = getPlayerGraphic(player, move_dir);
3531 int last_player_frame = player->Frame;
3534 /* GfxElement[][] is set to the element the player is digging or collecting;
3535 remove also for off-screen player if the player is not moving anymore */
3536 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3537 GfxElement[jx][jy] = EL_UNDEFINED;
3539 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3543 if (!IN_LEV_FIELD(jx, jy))
3545 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3546 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3547 printf("DrawPlayerField(): This should never happen!\n");
3552 if (element == EL_EXPLOSION)
3555 action = (player->is_pushing ? ACTION_PUSHING :
3556 player->is_digging ? ACTION_DIGGING :
3557 player->is_collecting ? ACTION_COLLECTING :
3558 player->is_moving ? ACTION_MOVING :
3559 player->is_snapping ? ACTION_SNAPPING :
3560 player->is_dropping ? ACTION_DROPPING :
3561 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3563 if (player->is_waiting)
3564 move_dir = player->dir_waiting;
3566 InitPlayerGfxAnimation(player, action, move_dir);
3568 /* ----------------------------------------------------------------------- */
3569 /* draw things in the field the player is leaving, if needed */
3570 /* ----------------------------------------------------------------------- */
3572 if (player->is_moving)
3574 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3576 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3578 if (last_element == EL_DYNAMITE_ACTIVE ||
3579 last_element == EL_EM_DYNAMITE_ACTIVE ||
3580 last_element == EL_SP_DISK_RED_ACTIVE)
3581 DrawDynamite(last_jx, last_jy);
3583 DrawLevelFieldThruMask(last_jx, last_jy);
3585 else if (last_element == EL_DYNAMITE_ACTIVE ||
3586 last_element == EL_EM_DYNAMITE_ACTIVE ||
3587 last_element == EL_SP_DISK_RED_ACTIVE)
3588 DrawDynamite(last_jx, last_jy);
3590 /* !!! this is not enough to prevent flickering of players which are
3591 moving next to each others without a free tile between them -- this
3592 can only be solved by drawing all players layer by layer (first the
3593 background, then the foreground etc.) !!! => TODO */
3594 else if (!IS_PLAYER(last_jx, last_jy))
3595 DrawLevelField(last_jx, last_jy);
3598 DrawLevelField(last_jx, last_jy);
3601 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3602 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3605 if (!IN_SCR_FIELD(sx, sy))
3608 /* ----------------------------------------------------------------------- */
3609 /* draw things behind the player, if needed */
3610 /* ----------------------------------------------------------------------- */
3613 DrawLevelElement(jx, jy, Back[jx][jy]);
3614 else if (IS_ACTIVE_BOMB(element))
3615 DrawLevelElement(jx, jy, EL_EMPTY);
3618 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3620 int old_element = GfxElement[jx][jy];
3621 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3622 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3624 if (GFX_CRUMBLED(old_element))
3625 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3627 DrawGraphic(sx, sy, old_graphic, frame);
3629 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3630 player_is_opaque = TRUE;
3634 GfxElement[jx][jy] = EL_UNDEFINED;
3636 /* make sure that pushed elements are drawn with correct frame rate */
3637 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3639 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3640 GfxFrame[jx][jy] = player->StepFrame;
3642 DrawLevelField(jx, jy);
3646 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3647 /* ----------------------------------------------------------------------- */
3648 /* draw player himself */
3649 /* ----------------------------------------------------------------------- */
3651 graphic = getPlayerGraphic(player, move_dir);
3653 /* in the case of changed player action or direction, prevent the current
3654 animation frame from being restarted for identical animations */
3655 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3656 player->Frame = last_player_frame;
3658 frame = getGraphicAnimationFrame(graphic, player->Frame);
3662 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3663 sxx = player->GfxPos;
3665 syy = player->GfxPos;
3668 if (player_is_opaque)
3669 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3671 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3673 if (SHIELD_ON(player))
3675 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3676 IMG_SHIELD_NORMAL_ACTIVE);
3677 int frame = getGraphicAnimationFrame(graphic, -1);
3679 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3683 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3686 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3687 sxx = player->GfxPos;
3689 syy = player->GfxPos;
3693 /* ----------------------------------------------------------------------- */
3694 /* draw things the player is pushing, if needed */
3695 /* ----------------------------------------------------------------------- */
3697 if (player->is_pushing && player->is_moving)
3699 int px = SCREENX(jx), py = SCREENY(jy);
3700 int pxx = (TILEX - ABS(sxx)) * dx;
3701 int pyy = (TILEY - ABS(syy)) * dy;
3702 int gfx_frame = GfxFrame[jx][jy];
3708 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3710 element = Feld[next_jx][next_jy];
3711 gfx_frame = GfxFrame[next_jx][next_jy];
3714 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3716 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3717 frame = getGraphicAnimationFrame(graphic, sync_frame);
3719 /* draw background element under pushed element (like the Sokoban field) */
3720 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3722 /* this allows transparent pushing animation over non-black background */
3725 DrawLevelElement(jx, jy, Back[jx][jy]);
3727 DrawLevelElement(jx, jy, EL_EMPTY);
3729 if (Back[next_jx][next_jy])
3730 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3732 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3734 else if (Back[next_jx][next_jy])
3735 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3738 /* do not draw (EM style) pushing animation when pushing is finished */
3739 /* (two-tile animations usually do not contain start and end frame) */
3740 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3741 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3743 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3745 /* masked drawing is needed for EMC style (double) movement graphics */
3746 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3747 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3751 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3752 /* ----------------------------------------------------------------------- */
3753 /* draw player himself */
3754 /* ----------------------------------------------------------------------- */
3756 graphic = getPlayerGraphic(player, move_dir);
3758 /* in the case of changed player action or direction, prevent the current
3759 animation frame from being restarted for identical animations */
3760 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3761 player->Frame = last_player_frame;
3763 frame = getGraphicAnimationFrame(graphic, player->Frame);
3767 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3768 sxx = player->GfxPos;
3770 syy = player->GfxPos;
3773 if (player_is_opaque)
3774 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3776 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3778 if (SHIELD_ON(player))
3780 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3781 IMG_SHIELD_NORMAL_ACTIVE);
3782 int frame = getGraphicAnimationFrame(graphic, -1);
3784 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3788 /* ----------------------------------------------------------------------- */
3789 /* draw things in front of player (active dynamite or dynabombs) */
3790 /* ----------------------------------------------------------------------- */
3792 if (IS_ACTIVE_BOMB(element))
3794 graphic = el2img(element);
3795 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3797 if (game.emulation == EMU_SUPAPLEX)
3798 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3800 DrawGraphicThruMask(sx, sy, graphic, frame);
3803 if (player_is_moving && last_element == EL_EXPLOSION)
3805 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3806 GfxElement[last_jx][last_jy] : EL_EMPTY);
3807 int graphic = el_act2img(element, ACTION_EXPLODING);
3808 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3809 int phase = ExplodePhase[last_jx][last_jy] - 1;
3810 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3813 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3816 /* ----------------------------------------------------------------------- */
3817 /* draw elements the player is just walking/passing through/under */
3818 /* ----------------------------------------------------------------------- */
3820 if (player_is_moving)
3822 /* handle the field the player is leaving ... */
3823 if (IS_ACCESSIBLE_INSIDE(last_element))
3824 DrawLevelField(last_jx, last_jy);
3825 else if (IS_ACCESSIBLE_UNDER(last_element))
3826 DrawLevelFieldThruMask(last_jx, last_jy);
3829 /* do not redraw accessible elements if the player is just pushing them */
3830 if (!player_is_moving || !player->is_pushing)
3832 /* ... and the field the player is entering */
3833 if (IS_ACCESSIBLE_INSIDE(element))
3834 DrawLevelField(jx, jy);
3835 else if (IS_ACCESSIBLE_UNDER(element))
3836 DrawLevelFieldThruMask(jx, jy);
3839 MarkTileDirty(sx, sy);
3842 /* ------------------------------------------------------------------------- */
3844 void WaitForEventToContinue()
3846 boolean still_wait = TRUE;
3848 if (program.headless)
3851 /* simulate releasing mouse button over last gadget, if still pressed */
3853 HandleGadgets(-1, -1, 0);
3855 button_status = MB_RELEASED;
3863 if (NextValidEvent(&event))
3867 case EVENT_BUTTONPRESS:
3868 case EVENT_KEYPRESS:
3869 #if defined(TARGET_SDL2)
3870 case SDL_CONTROLLERBUTTONDOWN:
3872 case SDL_JOYBUTTONDOWN:
3876 case EVENT_KEYRELEASE:
3877 ClearPlayerAction();
3881 HandleOtherEvents(&event);
3885 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3894 #define MAX_REQUEST_LINES 13
3895 #define MAX_REQUEST_LINE_FONT1_LEN 7
3896 #define MAX_REQUEST_LINE_FONT2_LEN 10
3898 static int RequestHandleEvents(unsigned int req_state)
3900 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3901 local_player->LevelSolved_GameEnd);
3902 int width = request.width;
3903 int height = request.height;
3907 setRequestPosition(&sx, &sy, FALSE);
3909 button_status = MB_RELEASED;
3911 request_gadget_id = -1;
3918 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3920 HandleGameActions();
3922 SetDrawtoField(DRAW_TO_BACKBUFFER);
3924 if (global.use_envelope_request)
3926 /* copy current state of request area to middle of playfield area */
3927 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
3935 while (NextValidEvent(&event))
3939 case EVENT_BUTTONPRESS:
3940 case EVENT_BUTTONRELEASE:
3941 case EVENT_MOTIONNOTIFY:
3945 if (event.type == EVENT_MOTIONNOTIFY)
3950 motion_status = TRUE;
3951 mx = ((MotionEvent *) &event)->x;
3952 my = ((MotionEvent *) &event)->y;
3956 motion_status = FALSE;
3957 mx = ((ButtonEvent *) &event)->x;
3958 my = ((ButtonEvent *) &event)->y;
3959 if (event.type == EVENT_BUTTONPRESS)
3960 button_status = ((ButtonEvent *) &event)->button;
3962 button_status = MB_RELEASED;
3965 /* this sets 'request_gadget_id' */
3966 HandleGadgets(mx, my, button_status);
3968 switch (request_gadget_id)
3970 case TOOL_CTRL_ID_YES:
3973 case TOOL_CTRL_ID_NO:
3976 case TOOL_CTRL_ID_CONFIRM:
3977 result = TRUE | FALSE;
3980 case TOOL_CTRL_ID_PLAYER_1:
3983 case TOOL_CTRL_ID_PLAYER_2:
3986 case TOOL_CTRL_ID_PLAYER_3:
3989 case TOOL_CTRL_ID_PLAYER_4:
4000 #if defined(TARGET_SDL2)
4001 case SDL_WINDOWEVENT:
4002 HandleWindowEvent((WindowEvent *) &event);
4005 case SDL_APP_WILLENTERBACKGROUND:
4006 case SDL_APP_DIDENTERBACKGROUND:
4007 case SDL_APP_WILLENTERFOREGROUND:
4008 case SDL_APP_DIDENTERFOREGROUND:
4009 HandlePauseResumeEvent((PauseResumeEvent *) &event);
4013 case EVENT_KEYPRESS:
4015 Key key = GetEventKey((KeyEvent *)&event, TRUE);
4020 if (req_state & REQ_CONFIRM)
4025 #if defined(TARGET_SDL2)
4028 #if defined(KSYM_Rewind)
4029 case KSYM_Rewind: /* for Amazon Fire TV remote */
4036 #if defined(TARGET_SDL2)
4038 #if defined(KSYM_FastForward)
4039 case KSYM_FastForward: /* for Amazon Fire TV remote */
4046 HandleKeysDebug(key);
4050 if (req_state & REQ_PLAYER)
4056 case EVENT_KEYRELEASE:
4057 ClearPlayerAction();
4060 #if defined(TARGET_SDL2)
4061 case SDL_CONTROLLERBUTTONDOWN:
4062 switch (event.cbutton.button)
4064 case SDL_CONTROLLER_BUTTON_A:
4065 case SDL_CONTROLLER_BUTTON_X:
4066 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4070 case SDL_CONTROLLER_BUTTON_B:
4071 case SDL_CONTROLLER_BUTTON_Y:
4072 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4073 case SDL_CONTROLLER_BUTTON_BACK:
4078 if (req_state & REQ_PLAYER)
4083 case SDL_CONTROLLERBUTTONUP:
4084 HandleJoystickEvent(&event);
4085 ClearPlayerAction();
4090 HandleOtherEvents(&event);
4095 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4097 int joy = AnyJoystick();
4099 if (joy & JOY_BUTTON_1)
4101 else if (joy & JOY_BUTTON_2)
4107 if (global.use_envelope_request)
4109 /* copy back current state of pressed buttons inside request area */
4110 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4120 static boolean RequestDoor(char *text, unsigned int req_state)
4122 unsigned int old_door_state;
4123 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4124 int font_nr = FONT_TEXT_2;
4129 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4131 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4132 font_nr = FONT_TEXT_1;
4135 if (game_status == GAME_MODE_PLAYING)
4136 BlitScreenToBitmap(backbuffer);
4138 /* disable deactivated drawing when quick-loading level tape recording */
4139 if (tape.playing && tape.deactivate_display)
4140 TapeDeactivateDisplayOff(TRUE);
4142 SetMouseCursor(CURSOR_DEFAULT);
4144 #if defined(NETWORK_AVALIABLE)
4145 /* pause network game while waiting for request to answer */
4146 if (options.network &&
4147 game_status == GAME_MODE_PLAYING &&
4148 req_state & REQUEST_WAIT_FOR_INPUT)
4149 SendToServer_PausePlaying();
4152 old_door_state = GetDoorState();
4154 /* simulate releasing mouse button over last gadget, if still pressed */
4156 HandleGadgets(-1, -1, 0);
4160 /* draw released gadget before proceeding */
4163 if (old_door_state & DOOR_OPEN_1)
4165 CloseDoor(DOOR_CLOSE_1);
4167 /* save old door content */
4168 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4169 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4172 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4173 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4175 /* clear door drawing field */
4176 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4178 /* force DOOR font inside door area */
4179 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4181 /* write text for request */
4182 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4184 char text_line[max_request_line_len + 1];
4190 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4192 tc = *(text_ptr + tx);
4193 // if (!tc || tc == ' ')
4194 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4198 if ((tc == '?' || tc == '!') && tl == 0)
4208 strncpy(text_line, text_ptr, tl);
4211 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4212 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4213 text_line, font_nr);
4215 text_ptr += tl + (tc == ' ' ? 1 : 0);
4216 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4221 if (req_state & REQ_ASK)
4223 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4224 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4226 else if (req_state & REQ_CONFIRM)
4228 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4230 else if (req_state & REQ_PLAYER)
4232 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4233 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4234 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4235 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4238 /* copy request gadgets to door backbuffer */
4239 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4241 OpenDoor(DOOR_OPEN_1);
4243 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4245 if (game_status == GAME_MODE_PLAYING)
4247 SetPanelBackground();
4248 SetDrawBackgroundMask(REDRAW_DOOR_1);
4252 SetDrawBackgroundMask(REDRAW_FIELD);
4258 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4260 // ---------- handle request buttons ----------
4261 result = RequestHandleEvents(req_state);
4265 if (!(req_state & REQ_STAY_OPEN))
4267 CloseDoor(DOOR_CLOSE_1);
4269 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4270 (req_state & REQ_REOPEN))
4271 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4276 if (game_status == GAME_MODE_PLAYING)
4278 SetPanelBackground();
4279 SetDrawBackgroundMask(REDRAW_DOOR_1);
4283 SetDrawBackgroundMask(REDRAW_FIELD);
4286 #if defined(NETWORK_AVALIABLE)
4287 /* continue network game after request */
4288 if (options.network &&
4289 game_status == GAME_MODE_PLAYING &&
4290 req_state & REQUEST_WAIT_FOR_INPUT)
4291 SendToServer_ContinuePlaying();
4294 /* restore deactivated drawing when quick-loading level tape recording */
4295 if (tape.playing && tape.deactivate_display)
4296 TapeDeactivateDisplayOn();
4301 static boolean RequestEnvelope(char *text, unsigned int req_state)
4305 if (game_status == GAME_MODE_PLAYING)
4306 BlitScreenToBitmap(backbuffer);
4308 /* disable deactivated drawing when quick-loading level tape recording */
4309 if (tape.playing && tape.deactivate_display)
4310 TapeDeactivateDisplayOff(TRUE);
4312 SetMouseCursor(CURSOR_DEFAULT);
4314 #if defined(NETWORK_AVALIABLE)
4315 /* pause network game while waiting for request to answer */
4316 if (options.network &&
4317 game_status == GAME_MODE_PLAYING &&
4318 req_state & REQUEST_WAIT_FOR_INPUT)
4319 SendToServer_PausePlaying();
4322 /* simulate releasing mouse button over last gadget, if still pressed */
4324 HandleGadgets(-1, -1, 0);
4328 // (replace with setting corresponding request background)
4329 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4330 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4332 /* clear door drawing field */
4333 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4335 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4337 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4339 if (game_status == GAME_MODE_PLAYING)
4341 SetPanelBackground();
4342 SetDrawBackgroundMask(REDRAW_DOOR_1);
4346 SetDrawBackgroundMask(REDRAW_FIELD);
4352 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4354 // ---------- handle request buttons ----------
4355 result = RequestHandleEvents(req_state);
4359 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4363 if (game_status == GAME_MODE_PLAYING)
4365 SetPanelBackground();
4366 SetDrawBackgroundMask(REDRAW_DOOR_1);
4370 SetDrawBackgroundMask(REDRAW_FIELD);
4373 #if defined(NETWORK_AVALIABLE)
4374 /* continue network game after request */
4375 if (options.network &&
4376 game_status == GAME_MODE_PLAYING &&
4377 req_state & REQUEST_WAIT_FOR_INPUT)
4378 SendToServer_ContinuePlaying();
4381 /* restore deactivated drawing when quick-loading level tape recording */
4382 if (tape.playing && tape.deactivate_display)
4383 TapeDeactivateDisplayOn();
4388 boolean Request(char *text, unsigned int req_state)
4390 boolean overlay_active = GetOverlayActive();
4393 SetOverlayActive(FALSE);
4395 if (global.use_envelope_request)
4396 result = RequestEnvelope(text, req_state);
4398 result = RequestDoor(text, req_state);
4400 SetOverlayActive(overlay_active);
4405 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4407 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4408 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4411 if (dpo1->sort_priority != dpo2->sort_priority)
4412 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4414 compare_result = dpo1->nr - dpo2->nr;
4416 return compare_result;
4419 void InitGraphicCompatibilityInfo_Doors()
4425 struct DoorInfo *door;
4429 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4430 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4432 { -1, -1, -1, NULL }
4434 struct Rect door_rect_list[] =
4436 { DX, DY, DXSIZE, DYSIZE },
4437 { VX, VY, VXSIZE, VYSIZE }
4441 for (i = 0; doors[i].door_token != -1; i++)
4443 int door_token = doors[i].door_token;
4444 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4445 int part_1 = doors[i].part_1;
4446 int part_8 = doors[i].part_8;
4447 int part_2 = part_1 + 1;
4448 int part_3 = part_1 + 2;
4449 struct DoorInfo *door = doors[i].door;
4450 struct Rect *door_rect = &door_rect_list[door_index];
4451 boolean door_gfx_redefined = FALSE;
4453 /* check if any door part graphic definitions have been redefined */
4455 for (j = 0; door_part_controls[j].door_token != -1; j++)
4457 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4458 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4460 if (dpc->door_token == door_token && fi->redefined)
4461 door_gfx_redefined = TRUE;
4464 /* check for old-style door graphic/animation modifications */
4466 if (!door_gfx_redefined)
4468 if (door->anim_mode & ANIM_STATIC_PANEL)
4470 door->panel.step_xoffset = 0;
4471 door->panel.step_yoffset = 0;
4474 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4476 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4477 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4478 int num_door_steps, num_panel_steps;
4480 /* remove door part graphics other than the two default wings */
4482 for (j = 0; door_part_controls[j].door_token != -1; j++)
4484 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4485 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4487 if (dpc->graphic >= part_3 &&
4488 dpc->graphic <= part_8)
4492 /* set graphics and screen positions of the default wings */
4494 g_part_1->width = door_rect->width;
4495 g_part_1->height = door_rect->height;
4496 g_part_2->width = door_rect->width;
4497 g_part_2->height = door_rect->height;
4498 g_part_2->src_x = door_rect->width;
4499 g_part_2->src_y = g_part_1->src_y;
4501 door->part_2.x = door->part_1.x;
4502 door->part_2.y = door->part_1.y;
4504 if (door->width != -1)
4506 g_part_1->width = door->width;
4507 g_part_2->width = door->width;
4509 // special treatment for graphics and screen position of right wing
4510 g_part_2->src_x += door_rect->width - door->width;
4511 door->part_2.x += door_rect->width - door->width;
4514 if (door->height != -1)
4516 g_part_1->height = door->height;
4517 g_part_2->height = door->height;
4519 // special treatment for graphics and screen position of bottom wing
4520 g_part_2->src_y += door_rect->height - door->height;
4521 door->part_2.y += door_rect->height - door->height;
4524 /* set animation delays for the default wings and panels */
4526 door->part_1.step_delay = door->step_delay;
4527 door->part_2.step_delay = door->step_delay;
4528 door->panel.step_delay = door->step_delay;
4530 /* set animation draw order for the default wings */
4532 door->part_1.sort_priority = 2; /* draw left wing over ... */
4533 door->part_2.sort_priority = 1; /* ... right wing */
4535 /* set animation draw offset for the default wings */
4537 if (door->anim_mode & ANIM_HORIZONTAL)
4539 door->part_1.step_xoffset = door->step_offset;
4540 door->part_1.step_yoffset = 0;
4541 door->part_2.step_xoffset = door->step_offset * -1;
4542 door->part_2.step_yoffset = 0;
4544 num_door_steps = g_part_1->width / door->step_offset;
4546 else // ANIM_VERTICAL
4548 door->part_1.step_xoffset = 0;
4549 door->part_1.step_yoffset = door->step_offset;
4550 door->part_2.step_xoffset = 0;
4551 door->part_2.step_yoffset = door->step_offset * -1;
4553 num_door_steps = g_part_1->height / door->step_offset;
4556 /* set animation draw offset for the default panels */
4558 if (door->step_offset > 1)
4560 num_panel_steps = 2 * door_rect->height / door->step_offset;
4561 door->panel.start_step = num_panel_steps - num_door_steps;
4562 door->panel.start_step_closing = door->panel.start_step;
4566 num_panel_steps = door_rect->height / door->step_offset;
4567 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4568 door->panel.start_step_closing = door->panel.start_step;
4569 door->panel.step_delay *= 2;
4580 for (i = 0; door_part_controls[i].door_token != -1; i++)
4582 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4583 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4585 /* initialize "start_step_opening" and "start_step_closing", if needed */
4586 if (dpc->pos->start_step_opening == 0 &&
4587 dpc->pos->start_step_closing == 0)
4589 // dpc->pos->start_step_opening = dpc->pos->start_step;
4590 dpc->pos->start_step_closing = dpc->pos->start_step;
4593 /* fill structure for door part draw order (sorted below) */
4595 dpo->sort_priority = dpc->pos->sort_priority;
4598 /* sort door part controls according to sort_priority and graphic number */
4599 qsort(door_part_order, MAX_DOOR_PARTS,
4600 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4603 unsigned int OpenDoor(unsigned int door_state)
4605 if (door_state & DOOR_COPY_BACK)
4607 if (door_state & DOOR_OPEN_1)
4608 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4609 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4611 if (door_state & DOOR_OPEN_2)
4612 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4613 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4615 door_state &= ~DOOR_COPY_BACK;
4618 return MoveDoor(door_state);
4621 unsigned int CloseDoor(unsigned int door_state)
4623 unsigned int old_door_state = GetDoorState();
4625 if (!(door_state & DOOR_NO_COPY_BACK))
4627 if (old_door_state & DOOR_OPEN_1)
4628 BlitBitmap(backbuffer, bitmap_db_door_1,
4629 DX, DY, DXSIZE, DYSIZE, 0, 0);
4631 if (old_door_state & DOOR_OPEN_2)
4632 BlitBitmap(backbuffer, bitmap_db_door_2,
4633 VX, VY, VXSIZE, VYSIZE, 0, 0);
4635 door_state &= ~DOOR_NO_COPY_BACK;
4638 return MoveDoor(door_state);
4641 unsigned int GetDoorState()
4643 return MoveDoor(DOOR_GET_STATE);
4646 unsigned int SetDoorState(unsigned int door_state)
4648 return MoveDoor(door_state | DOOR_SET_STATE);
4651 int euclid(int a, int b)
4653 return (b ? euclid(b, a % b) : a);
4656 unsigned int MoveDoor(unsigned int door_state)
4658 struct Rect door_rect_list[] =
4660 { DX, DY, DXSIZE, DYSIZE },
4661 { VX, VY, VXSIZE, VYSIZE }
4663 static int door1 = DOOR_CLOSE_1;
4664 static int door2 = DOOR_CLOSE_2;
4665 unsigned int door_delay = 0;
4666 unsigned int door_delay_value;
4669 if (door_state == DOOR_GET_STATE)
4670 return (door1 | door2);
4672 if (door_state & DOOR_SET_STATE)
4674 if (door_state & DOOR_ACTION_1)
4675 door1 = door_state & DOOR_ACTION_1;
4676 if (door_state & DOOR_ACTION_2)
4677 door2 = door_state & DOOR_ACTION_2;
4679 return (door1 | door2);
4682 if (!(door_state & DOOR_FORCE_REDRAW))
4684 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4685 door_state &= ~DOOR_OPEN_1;
4686 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4687 door_state &= ~DOOR_CLOSE_1;
4688 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4689 door_state &= ~DOOR_OPEN_2;
4690 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4691 door_state &= ~DOOR_CLOSE_2;
4694 if (global.autoplay_leveldir)
4696 door_state |= DOOR_NO_DELAY;
4697 door_state &= ~DOOR_CLOSE_ALL;
4700 if (game_status == GAME_MODE_EDITOR)
4701 door_state |= DOOR_NO_DELAY;
4703 if (door_state & DOOR_ACTION)
4705 boolean door_panel_drawn[NUM_DOORS];
4706 boolean panel_has_doors[NUM_DOORS];
4707 boolean door_part_skip[MAX_DOOR_PARTS];
4708 boolean door_part_done[MAX_DOOR_PARTS];
4709 boolean door_part_done_all;
4710 int num_steps[MAX_DOOR_PARTS];
4711 int max_move_delay = 0; // delay for complete animations of all doors
4712 int max_step_delay = 0; // delay (ms) between two animation frames
4713 int num_move_steps = 0; // number of animation steps for all doors
4714 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4715 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4716 int current_move_delay = 0;
4720 for (i = 0; i < NUM_DOORS; i++)
4721 panel_has_doors[i] = FALSE;
4723 for (i = 0; i < MAX_DOOR_PARTS; i++)
4725 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4726 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4727 int door_token = dpc->door_token;
4729 door_part_done[i] = FALSE;
4730 door_part_skip[i] = (!(door_state & door_token) ||
4734 for (i = 0; i < MAX_DOOR_PARTS; i++)
4736 int nr = door_part_order[i].nr;
4737 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4738 struct DoorPartPosInfo *pos = dpc->pos;
4739 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4740 int door_token = dpc->door_token;
4741 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4742 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4743 int step_xoffset = ABS(pos->step_xoffset);
4744 int step_yoffset = ABS(pos->step_yoffset);
4745 int step_delay = pos->step_delay;
4746 int current_door_state = door_state & door_token;
4747 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4748 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4749 boolean part_opening = (is_panel ? door_closing : door_opening);
4750 int start_step = (part_opening ? pos->start_step_opening :
4751 pos->start_step_closing);
4752 float move_xsize = (step_xoffset ? g->width : 0);
4753 float move_ysize = (step_yoffset ? g->height : 0);
4754 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4755 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4756 int move_steps = (move_xsteps && move_ysteps ?
4757 MIN(move_xsteps, move_ysteps) :
4758 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4759 int move_delay = move_steps * step_delay;
4761 if (door_part_skip[nr])
4764 max_move_delay = MAX(max_move_delay, move_delay);
4765 max_step_delay = (max_step_delay == 0 ? step_delay :
4766 euclid(max_step_delay, step_delay));
4767 num_steps[nr] = move_steps;
4771 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4773 panel_has_doors[door_index] = TRUE;
4777 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4779 num_move_steps = max_move_delay / max_step_delay;
4780 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4782 door_delay_value = max_step_delay;
4784 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4786 start = num_move_steps - 1;
4790 /* opening door sound has priority over simultaneously closing door */
4791 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4792 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4793 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4794 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4797 for (k = start; k < num_move_steps; k++)
4799 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4801 door_part_done_all = TRUE;
4803 for (i = 0; i < NUM_DOORS; i++)
4804 door_panel_drawn[i] = FALSE;
4806 for (i = 0; i < MAX_DOOR_PARTS; i++)
4808 int nr = door_part_order[i].nr;
4809 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4810 struct DoorPartPosInfo *pos = dpc->pos;
4811 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4812 int door_token = dpc->door_token;
4813 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4814 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4815 boolean is_panel_and_door_has_closed = FALSE;
4816 struct Rect *door_rect = &door_rect_list[door_index];
4817 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4819 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4820 int current_door_state = door_state & door_token;
4821 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4822 boolean door_closing = !door_opening;
4823 boolean part_opening = (is_panel ? door_closing : door_opening);
4824 boolean part_closing = !part_opening;
4825 int start_step = (part_opening ? pos->start_step_opening :
4826 pos->start_step_closing);
4827 int step_delay = pos->step_delay;
4828 int step_factor = step_delay / max_step_delay;
4829 int k1 = (step_factor ? k / step_factor + 1 : k);
4830 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4831 int kk = MAX(0, k2);
4834 int src_x, src_y, src_xx, src_yy;
4835 int dst_x, dst_y, dst_xx, dst_yy;
4838 if (door_part_skip[nr])
4841 if (!(door_state & door_token))
4849 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4850 int kk_door = MAX(0, k2_door);
4851 int sync_frame = kk_door * door_delay_value;
4852 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4854 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
4855 &g_src_x, &g_src_y);
4860 if (!door_panel_drawn[door_index])
4862 ClearRectangle(drawto, door_rect->x, door_rect->y,
4863 door_rect->width, door_rect->height);
4865 door_panel_drawn[door_index] = TRUE;
4868 // draw opening or closing door parts
4870 if (pos->step_xoffset < 0) // door part on right side
4873 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4876 if (dst_xx + width > door_rect->width)
4877 width = door_rect->width - dst_xx;
4879 else // door part on left side
4882 dst_xx = pos->x - kk * pos->step_xoffset;
4886 src_xx = ABS(dst_xx);
4890 width = g->width - src_xx;
4892 if (width > door_rect->width)
4893 width = door_rect->width;
4895 // printf("::: k == %d [%d] \n", k, start_step);
4898 if (pos->step_yoffset < 0) // door part on bottom side
4901 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4904 if (dst_yy + height > door_rect->height)
4905 height = door_rect->height - dst_yy;
4907 else // door part on top side
4910 dst_yy = pos->y - kk * pos->step_yoffset;
4914 src_yy = ABS(dst_yy);
4918 height = g->height - src_yy;
4921 src_x = g_src_x + src_xx;
4922 src_y = g_src_y + src_yy;
4924 dst_x = door_rect->x + dst_xx;
4925 dst_y = door_rect->y + dst_yy;
4927 is_panel_and_door_has_closed =
4930 panel_has_doors[door_index] &&
4931 k >= num_move_steps_doors_only - 1);
4933 if (width >= 0 && width <= g->width &&
4934 height >= 0 && height <= g->height &&
4935 !is_panel_and_door_has_closed)
4937 if (is_panel || !pos->draw_masked)
4938 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4941 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4945 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4947 if ((part_opening && (width < 0 || height < 0)) ||
4948 (part_closing && (width >= g->width && height >= g->height)))
4949 door_part_done[nr] = TRUE;
4951 // continue door part animations, but not panel after door has closed
4952 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4953 door_part_done_all = FALSE;
4956 if (!(door_state & DOOR_NO_DELAY))
4960 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4962 current_move_delay += max_step_delay;
4964 /* prevent OS (Windows) from complaining about program not responding */
4968 if (door_part_done_all)
4973 if (door_state & DOOR_ACTION_1)
4974 door1 = door_state & DOOR_ACTION_1;
4975 if (door_state & DOOR_ACTION_2)
4976 door2 = door_state & DOOR_ACTION_2;
4978 // draw masked border over door area
4979 DrawMaskedBorder(REDRAW_DOOR_1);
4980 DrawMaskedBorder(REDRAW_DOOR_2);
4982 return (door1 | door2);
4985 static boolean useSpecialEditorDoor()
4987 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4988 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4990 // do not draw special editor door if editor border defined or redefined
4991 if (graphic_info[graphic].bitmap != NULL || redefined)
4994 // do not draw special editor door if global border defined to be empty
4995 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4998 // do not draw special editor door if viewport definitions do not match
5002 EY + EYSIZE != VY + VYSIZE)
5008 void DrawSpecialEditorDoor()
5010 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5011 int top_border_width = gfx1->width;
5012 int top_border_height = gfx1->height;
5013 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5014 int ex = EX - outer_border;
5015 int ey = EY - outer_border;
5016 int vy = VY - outer_border;
5017 int exsize = EXSIZE + 2 * outer_border;
5019 if (!useSpecialEditorDoor())
5022 /* draw bigger level editor toolbox window */
5023 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
5024 top_border_width, top_border_height, ex, ey - top_border_height);
5025 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
5026 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
5028 redraw_mask |= REDRAW_ALL;
5031 void UndrawSpecialEditorDoor()
5033 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
5034 int top_border_width = gfx1->width;
5035 int top_border_height = gfx1->height;
5036 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
5037 int ex = EX - outer_border;
5038 int ey = EY - outer_border;
5039 int ey_top = ey - top_border_height;
5040 int exsize = EXSIZE + 2 * outer_border;
5041 int eysize = EYSIZE + 2 * outer_border;
5043 if (!useSpecialEditorDoor())
5046 /* draw normal tape recorder window */
5047 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
5049 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5050 ex, ey_top, top_border_width, top_border_height,
5052 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
5053 ex, ey, exsize, eysize, ex, ey);
5057 // if screen background is set to "[NONE]", clear editor toolbox window
5058 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5059 ClearRectangle(drawto, ex, ey, exsize, eysize);
5062 redraw_mask |= REDRAW_ALL;
5066 /* ---------- new tool button stuff ---------------------------------------- */
5071 struct TextPosInfo *pos;
5074 } toolbutton_info[NUM_TOOL_BUTTONS] =
5077 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5078 TOOL_CTRL_ID_YES, "yes"
5081 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5082 TOOL_CTRL_ID_NO, "no"
5085 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5086 TOOL_CTRL_ID_CONFIRM, "confirm"
5089 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5090 TOOL_CTRL_ID_PLAYER_1, "player 1"
5093 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5094 TOOL_CTRL_ID_PLAYER_2, "player 2"
5097 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5098 TOOL_CTRL_ID_PLAYER_3, "player 3"
5101 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5102 TOOL_CTRL_ID_PLAYER_4, "player 4"
5106 void CreateToolButtons()
5110 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5112 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
5113 struct TextPosInfo *pos = toolbutton_info[i].pos;
5114 struct GadgetInfo *gi;
5115 Bitmap *deco_bitmap = None;
5116 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5117 unsigned int event_mask = GD_EVENT_RELEASED;
5120 int gd_x = gfx->src_x;
5121 int gd_y = gfx->src_y;
5122 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5123 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5126 if (global.use_envelope_request)
5127 setRequestPosition(&dx, &dy, TRUE);
5129 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5131 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5133 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5134 pos->size, &deco_bitmap, &deco_x, &deco_y);
5135 deco_xpos = (gfx->width - pos->size) / 2;
5136 deco_ypos = (gfx->height - pos->size) / 2;
5139 gi = CreateGadget(GDI_CUSTOM_ID, id,
5140 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5141 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
5142 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
5143 GDI_WIDTH, gfx->width,
5144 GDI_HEIGHT, gfx->height,
5145 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5146 GDI_STATE, GD_BUTTON_UNPRESSED,
5147 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5148 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5149 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5150 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5151 GDI_DECORATION_SIZE, pos->size, pos->size,
5152 GDI_DECORATION_SHIFTING, 1, 1,
5153 GDI_DIRECT_DRAW, FALSE,
5154 GDI_EVENT_MASK, event_mask,
5155 GDI_CALLBACK_ACTION, HandleToolButtons,
5159 Error(ERR_EXIT, "cannot create gadget");
5161 tool_gadget[id] = gi;
5165 void FreeToolButtons()
5169 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5170 FreeGadget(tool_gadget[i]);
5173 static void UnmapToolButtons()
5177 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5178 UnmapGadget(tool_gadget[i]);
5181 static void HandleToolButtons(struct GadgetInfo *gi)
5183 request_gadget_id = gi->custom_id;
5186 static struct Mapping_EM_to_RND_object
5189 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
5190 boolean is_backside; /* backside of moving element */
5196 em_object_mapping_list[] =
5199 Xblank, TRUE, FALSE,
5203 Yacid_splash_eB, FALSE, FALSE,
5204 EL_ACID_SPLASH_RIGHT, -1, -1
5207 Yacid_splash_wB, FALSE, FALSE,
5208 EL_ACID_SPLASH_LEFT, -1, -1
5211 #ifdef EM_ENGINE_BAD_ROLL
5213 Xstone_force_e, FALSE, FALSE,
5214 EL_ROCK, -1, MV_BIT_RIGHT
5217 Xstone_force_w, FALSE, FALSE,
5218 EL_ROCK, -1, MV_BIT_LEFT
5221 Xnut_force_e, FALSE, FALSE,
5222 EL_NUT, -1, MV_BIT_RIGHT
5225 Xnut_force_w, FALSE, FALSE,
5226 EL_NUT, -1, MV_BIT_LEFT
5229 Xspring_force_e, FALSE, FALSE,
5230 EL_SPRING, -1, MV_BIT_RIGHT
5233 Xspring_force_w, FALSE, FALSE,
5234 EL_SPRING, -1, MV_BIT_LEFT
5237 Xemerald_force_e, FALSE, FALSE,
5238 EL_EMERALD, -1, MV_BIT_RIGHT
5241 Xemerald_force_w, FALSE, FALSE,
5242 EL_EMERALD, -1, MV_BIT_LEFT
5245 Xdiamond_force_e, FALSE, FALSE,
5246 EL_DIAMOND, -1, MV_BIT_RIGHT
5249 Xdiamond_force_w, FALSE, FALSE,
5250 EL_DIAMOND, -1, MV_BIT_LEFT
5253 Xbomb_force_e, FALSE, FALSE,
5254 EL_BOMB, -1, MV_BIT_RIGHT
5257 Xbomb_force_w, FALSE, FALSE,
5258 EL_BOMB, -1, MV_BIT_LEFT
5260 #endif /* EM_ENGINE_BAD_ROLL */
5263 Xstone, TRUE, FALSE,
5267 Xstone_pause, FALSE, FALSE,
5271 Xstone_fall, FALSE, FALSE,
5275 Ystone_s, FALSE, FALSE,
5276 EL_ROCK, ACTION_FALLING, -1
5279 Ystone_sB, FALSE, TRUE,
5280 EL_ROCK, ACTION_FALLING, -1
5283 Ystone_e, FALSE, FALSE,
5284 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5287 Ystone_eB, FALSE, TRUE,
5288 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5291 Ystone_w, FALSE, FALSE,
5292 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5295 Ystone_wB, FALSE, TRUE,
5296 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5303 Xnut_pause, FALSE, FALSE,
5307 Xnut_fall, FALSE, FALSE,
5311 Ynut_s, FALSE, FALSE,
5312 EL_NUT, ACTION_FALLING, -1
5315 Ynut_sB, FALSE, TRUE,
5316 EL_NUT, ACTION_FALLING, -1
5319 Ynut_e, FALSE, FALSE,
5320 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5323 Ynut_eB, FALSE, TRUE,
5324 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5327 Ynut_w, FALSE, FALSE,
5328 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5331 Ynut_wB, FALSE, TRUE,
5332 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5335 Xbug_n, TRUE, FALSE,
5339 Xbug_e, TRUE, FALSE,
5340 EL_BUG_RIGHT, -1, -1
5343 Xbug_s, TRUE, FALSE,
5347 Xbug_w, TRUE, FALSE,
5351 Xbug_gon, FALSE, FALSE,
5355 Xbug_goe, FALSE, FALSE,
5356 EL_BUG_RIGHT, -1, -1
5359 Xbug_gos, FALSE, FALSE,
5363 Xbug_gow, FALSE, FALSE,
5367 Ybug_n, FALSE, FALSE,
5368 EL_BUG, ACTION_MOVING, MV_BIT_UP
5371 Ybug_nB, FALSE, TRUE,
5372 EL_BUG, ACTION_MOVING, MV_BIT_UP
5375 Ybug_e, FALSE, FALSE,
5376 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5379 Ybug_eB, FALSE, TRUE,
5380 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5383 Ybug_s, FALSE, FALSE,
5384 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5387 Ybug_sB, FALSE, TRUE,
5388 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5391 Ybug_w, FALSE, FALSE,
5392 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5395 Ybug_wB, FALSE, TRUE,
5396 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5399 Ybug_w_n, FALSE, FALSE,
5400 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5403 Ybug_n_e, FALSE, FALSE,
5404 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5407 Ybug_e_s, FALSE, FALSE,
5408 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5411 Ybug_s_w, FALSE, FALSE,
5412 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5415 Ybug_e_n, FALSE, FALSE,
5416 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5419 Ybug_s_e, FALSE, FALSE,
5420 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5423 Ybug_w_s, FALSE, FALSE,
5424 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5427 Ybug_n_w, FALSE, FALSE,
5428 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5431 Ybug_stone, FALSE, FALSE,
5432 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5435 Ybug_spring, FALSE, FALSE,
5436 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5439 Xtank_n, TRUE, FALSE,
5440 EL_SPACESHIP_UP, -1, -1
5443 Xtank_e, TRUE, FALSE,
5444 EL_SPACESHIP_RIGHT, -1, -1
5447 Xtank_s, TRUE, FALSE,
5448 EL_SPACESHIP_DOWN, -1, -1
5451 Xtank_w, TRUE, FALSE,
5452 EL_SPACESHIP_LEFT, -1, -1
5455 Xtank_gon, FALSE, FALSE,
5456 EL_SPACESHIP_UP, -1, -1
5459 Xtank_goe, FALSE, FALSE,
5460 EL_SPACESHIP_RIGHT, -1, -1
5463 Xtank_gos, FALSE, FALSE,
5464 EL_SPACESHIP_DOWN, -1, -1
5467 Xtank_gow, FALSE, FALSE,
5468 EL_SPACESHIP_LEFT, -1, -1
5471 Ytank_n, FALSE, FALSE,
5472 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5475 Ytank_nB, FALSE, TRUE,
5476 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5479 Ytank_e, FALSE, FALSE,
5480 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5483 Ytank_eB, FALSE, TRUE,
5484 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5487 Ytank_s, FALSE, FALSE,
5488 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5491 Ytank_sB, FALSE, TRUE,
5492 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5495 Ytank_w, FALSE, FALSE,
5496 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5499 Ytank_wB, FALSE, TRUE,
5500 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5503 Ytank_w_n, FALSE, FALSE,
5504 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5507 Ytank_n_e, FALSE, FALSE,
5508 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5511 Ytank_e_s, FALSE, FALSE,
5512 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5515 Ytank_s_w, FALSE, FALSE,
5516 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5519 Ytank_e_n, FALSE, FALSE,
5520 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5523 Ytank_s_e, FALSE, FALSE,
5524 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5527 Ytank_w_s, FALSE, FALSE,
5528 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5531 Ytank_n_w, FALSE, FALSE,
5532 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5535 Ytank_stone, FALSE, FALSE,
5536 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5539 Ytank_spring, FALSE, FALSE,
5540 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5543 Xandroid, TRUE, FALSE,
5544 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5547 Xandroid_1_n, FALSE, FALSE,
5548 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5551 Xandroid_2_n, FALSE, FALSE,
5552 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5555 Xandroid_1_e, FALSE, FALSE,
5556 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5559 Xandroid_2_e, FALSE, FALSE,
5560 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5563 Xandroid_1_w, FALSE, FALSE,
5564 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5567 Xandroid_2_w, FALSE, FALSE,
5568 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5571 Xandroid_1_s, FALSE, FALSE,
5572 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5575 Xandroid_2_s, FALSE, FALSE,
5576 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5579 Yandroid_n, FALSE, FALSE,
5580 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5583 Yandroid_nB, FALSE, TRUE,
5584 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5587 Yandroid_ne, FALSE, FALSE,
5588 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5591 Yandroid_neB, FALSE, TRUE,
5592 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5595 Yandroid_e, FALSE, FALSE,
5596 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5599 Yandroid_eB, FALSE, TRUE,
5600 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5603 Yandroid_se, FALSE, FALSE,
5604 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5607 Yandroid_seB, FALSE, TRUE,
5608 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5611 Yandroid_s, FALSE, FALSE,
5612 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5615 Yandroid_sB, FALSE, TRUE,
5616 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5619 Yandroid_sw, FALSE, FALSE,
5620 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5623 Yandroid_swB, FALSE, TRUE,
5624 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5627 Yandroid_w, FALSE, FALSE,
5628 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5631 Yandroid_wB, FALSE, TRUE,
5632 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5635 Yandroid_nw, FALSE, FALSE,
5636 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5639 Yandroid_nwB, FALSE, TRUE,
5640 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5643 Xspring, TRUE, FALSE,
5647 Xspring_pause, FALSE, FALSE,
5651 Xspring_e, FALSE, FALSE,
5655 Xspring_w, FALSE, FALSE,
5659 Xspring_fall, FALSE, FALSE,
5663 Yspring_s, FALSE, FALSE,
5664 EL_SPRING, ACTION_FALLING, -1
5667 Yspring_sB, FALSE, TRUE,
5668 EL_SPRING, ACTION_FALLING, -1
5671 Yspring_e, FALSE, FALSE,
5672 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5675 Yspring_eB, FALSE, TRUE,
5676 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5679 Yspring_w, FALSE, FALSE,
5680 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5683 Yspring_wB, FALSE, TRUE,
5684 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5687 Yspring_kill_e, FALSE, FALSE,
5688 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5691 Yspring_kill_eB, FALSE, TRUE,
5692 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5695 Yspring_kill_w, FALSE, FALSE,
5696 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5699 Yspring_kill_wB, FALSE, TRUE,
5700 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5703 Xeater_n, TRUE, FALSE,
5704 EL_YAMYAM_UP, -1, -1
5707 Xeater_e, TRUE, FALSE,
5708 EL_YAMYAM_RIGHT, -1, -1
5711 Xeater_w, TRUE, FALSE,
5712 EL_YAMYAM_LEFT, -1, -1
5715 Xeater_s, TRUE, FALSE,
5716 EL_YAMYAM_DOWN, -1, -1
5719 Yeater_n, FALSE, FALSE,
5720 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5723 Yeater_nB, FALSE, TRUE,
5724 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5727 Yeater_e, FALSE, FALSE,
5728 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5731 Yeater_eB, FALSE, TRUE,
5732 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5735 Yeater_s, FALSE, FALSE,
5736 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5739 Yeater_sB, FALSE, TRUE,
5740 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5743 Yeater_w, FALSE, FALSE,
5744 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5747 Yeater_wB, FALSE, TRUE,
5748 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5751 Yeater_stone, FALSE, FALSE,
5752 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5755 Yeater_spring, FALSE, FALSE,
5756 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5759 Xalien, TRUE, FALSE,
5763 Xalien_pause, FALSE, FALSE,
5767 Yalien_n, FALSE, FALSE,
5768 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5771 Yalien_nB, FALSE, TRUE,
5772 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5775 Yalien_e, FALSE, FALSE,
5776 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5779 Yalien_eB, FALSE, TRUE,
5780 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5783 Yalien_s, FALSE, FALSE,
5784 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5787 Yalien_sB, FALSE, TRUE,
5788 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5791 Yalien_w, FALSE, FALSE,
5792 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5795 Yalien_wB, FALSE, TRUE,
5796 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5799 Yalien_stone, FALSE, FALSE,
5800 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5803 Yalien_spring, FALSE, FALSE,
5804 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5807 Xemerald, TRUE, FALSE,
5811 Xemerald_pause, FALSE, FALSE,
5815 Xemerald_fall, FALSE, FALSE,
5819 Xemerald_shine, FALSE, FALSE,
5820 EL_EMERALD, ACTION_TWINKLING, -1
5823 Yemerald_s, FALSE, FALSE,
5824 EL_EMERALD, ACTION_FALLING, -1
5827 Yemerald_sB, FALSE, TRUE,
5828 EL_EMERALD, ACTION_FALLING, -1
5831 Yemerald_e, FALSE, FALSE,
5832 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5835 Yemerald_eB, FALSE, TRUE,
5836 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5839 Yemerald_w, FALSE, FALSE,
5840 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5843 Yemerald_wB, FALSE, TRUE,
5844 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5847 Yemerald_eat, FALSE, FALSE,
5848 EL_EMERALD, ACTION_COLLECTING, -1
5851 Yemerald_stone, FALSE, FALSE,
5852 EL_NUT, ACTION_BREAKING, -1
5855 Xdiamond, TRUE, FALSE,
5859 Xdiamond_pause, FALSE, FALSE,
5863 Xdiamond_fall, FALSE, FALSE,
5867 Xdiamond_shine, FALSE, FALSE,
5868 EL_DIAMOND, ACTION_TWINKLING, -1
5871 Ydiamond_s, FALSE, FALSE,
5872 EL_DIAMOND, ACTION_FALLING, -1
5875 Ydiamond_sB, FALSE, TRUE,
5876 EL_DIAMOND, ACTION_FALLING, -1
5879 Ydiamond_e, FALSE, FALSE,
5880 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5883 Ydiamond_eB, FALSE, TRUE,
5884 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5887 Ydiamond_w, FALSE, FALSE,
5888 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5891 Ydiamond_wB, FALSE, TRUE,
5892 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5895 Ydiamond_eat, FALSE, FALSE,
5896 EL_DIAMOND, ACTION_COLLECTING, -1
5899 Ydiamond_stone, FALSE, FALSE,
5900 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5903 Xdrip_fall, TRUE, FALSE,
5904 EL_AMOEBA_DROP, -1, -1
5907 Xdrip_stretch, FALSE, FALSE,
5908 EL_AMOEBA_DROP, ACTION_FALLING, -1
5911 Xdrip_stretchB, FALSE, TRUE,
5912 EL_AMOEBA_DROP, ACTION_FALLING, -1
5915 Xdrip_eat, FALSE, FALSE,
5916 EL_AMOEBA_DROP, ACTION_GROWING, -1
5919 Ydrip_s1, FALSE, FALSE,
5920 EL_AMOEBA_DROP, ACTION_FALLING, -1
5923 Ydrip_s1B, FALSE, TRUE,
5924 EL_AMOEBA_DROP, ACTION_FALLING, -1
5927 Ydrip_s2, FALSE, FALSE,
5928 EL_AMOEBA_DROP, ACTION_FALLING, -1
5931 Ydrip_s2B, FALSE, TRUE,
5932 EL_AMOEBA_DROP, ACTION_FALLING, -1
5939 Xbomb_pause, FALSE, FALSE,
5943 Xbomb_fall, FALSE, FALSE,
5947 Ybomb_s, FALSE, FALSE,
5948 EL_BOMB, ACTION_FALLING, -1
5951 Ybomb_sB, FALSE, TRUE,
5952 EL_BOMB, ACTION_FALLING, -1
5955 Ybomb_e, FALSE, FALSE,
5956 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5959 Ybomb_eB, FALSE, TRUE,
5960 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5963 Ybomb_w, FALSE, FALSE,
5964 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5967 Ybomb_wB, FALSE, TRUE,
5968 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5971 Ybomb_eat, FALSE, FALSE,
5972 EL_BOMB, ACTION_ACTIVATING, -1
5975 Xballoon, TRUE, FALSE,
5979 Yballoon_n, FALSE, FALSE,
5980 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5983 Yballoon_nB, FALSE, TRUE,
5984 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5987 Yballoon_e, FALSE, FALSE,
5988 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5991 Yballoon_eB, FALSE, TRUE,
5992 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5995 Yballoon_s, FALSE, FALSE,
5996 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5999 Yballoon_sB, FALSE, TRUE,
6000 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
6003 Yballoon_w, FALSE, FALSE,
6004 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6007 Yballoon_wB, FALSE, TRUE,
6008 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
6011 Xgrass, TRUE, FALSE,
6012 EL_EMC_GRASS, -1, -1
6015 Ygrass_nB, FALSE, FALSE,
6016 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
6019 Ygrass_eB, FALSE, FALSE,
6020 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
6023 Ygrass_sB, FALSE, FALSE,
6024 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
6027 Ygrass_wB, FALSE, FALSE,
6028 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
6035 Ydirt_nB, FALSE, FALSE,
6036 EL_SAND, ACTION_DIGGING, MV_BIT_UP
6039 Ydirt_eB, FALSE, FALSE,
6040 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
6043 Ydirt_sB, FALSE, FALSE,
6044 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
6047 Ydirt_wB, FALSE, FALSE,
6048 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
6051 Xacid_ne, TRUE, FALSE,
6052 EL_ACID_POOL_TOPRIGHT, -1, -1
6055 Xacid_se, TRUE, FALSE,
6056 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6059 Xacid_s, TRUE, FALSE,
6060 EL_ACID_POOL_BOTTOM, -1, -1
6063 Xacid_sw, TRUE, FALSE,
6064 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6067 Xacid_nw, TRUE, FALSE,
6068 EL_ACID_POOL_TOPLEFT, -1, -1
6071 Xacid_1, TRUE, FALSE,
6075 Xacid_2, FALSE, FALSE,
6079 Xacid_3, FALSE, FALSE,
6083 Xacid_4, FALSE, FALSE,
6087 Xacid_5, FALSE, FALSE,
6091 Xacid_6, FALSE, FALSE,
6095 Xacid_7, FALSE, FALSE,
6099 Xacid_8, FALSE, FALSE,
6103 Xball_1, TRUE, FALSE,
6104 EL_EMC_MAGIC_BALL, -1, -1
6107 Xball_1B, FALSE, FALSE,
6108 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6111 Xball_2, FALSE, FALSE,
6112 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6115 Xball_2B, FALSE, FALSE,
6116 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6119 Yball_eat, FALSE, FALSE,
6120 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6123 Ykey_1_eat, FALSE, FALSE,
6124 EL_EM_KEY_1, ACTION_COLLECTING, -1
6127 Ykey_2_eat, FALSE, FALSE,
6128 EL_EM_KEY_2, ACTION_COLLECTING, -1
6131 Ykey_3_eat, FALSE, FALSE,
6132 EL_EM_KEY_3, ACTION_COLLECTING, -1
6135 Ykey_4_eat, FALSE, FALSE,
6136 EL_EM_KEY_4, ACTION_COLLECTING, -1
6139 Ykey_5_eat, FALSE, FALSE,
6140 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6143 Ykey_6_eat, FALSE, FALSE,
6144 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6147 Ykey_7_eat, FALSE, FALSE,
6148 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6151 Ykey_8_eat, FALSE, FALSE,
6152 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6155 Ylenses_eat, FALSE, FALSE,
6156 EL_EMC_LENSES, ACTION_COLLECTING, -1
6159 Ymagnify_eat, FALSE, FALSE,
6160 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6163 Ygrass_eat, FALSE, FALSE,
6164 EL_EMC_GRASS, ACTION_SNAPPING, -1
6167 Ydirt_eat, FALSE, FALSE,
6168 EL_SAND, ACTION_SNAPPING, -1
6171 Xgrow_ns, TRUE, FALSE,
6172 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6175 Ygrow_ns_eat, FALSE, FALSE,
6176 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6179 Xgrow_ew, TRUE, FALSE,
6180 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6183 Ygrow_ew_eat, FALSE, FALSE,
6184 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6187 Xwonderwall, TRUE, FALSE,
6188 EL_MAGIC_WALL, -1, -1
6191 XwonderwallB, FALSE, FALSE,
6192 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6195 Xamoeba_1, TRUE, FALSE,
6196 EL_AMOEBA_DRY, ACTION_OTHER, -1
6199 Xamoeba_2, FALSE, FALSE,
6200 EL_AMOEBA_DRY, ACTION_OTHER, -1
6203 Xamoeba_3, FALSE, FALSE,
6204 EL_AMOEBA_DRY, ACTION_OTHER, -1
6207 Xamoeba_4, FALSE, FALSE,
6208 EL_AMOEBA_DRY, ACTION_OTHER, -1
6211 Xamoeba_5, TRUE, FALSE,
6212 EL_AMOEBA_WET, ACTION_OTHER, -1
6215 Xamoeba_6, FALSE, FALSE,
6216 EL_AMOEBA_WET, ACTION_OTHER, -1
6219 Xamoeba_7, FALSE, FALSE,
6220 EL_AMOEBA_WET, ACTION_OTHER, -1
6223 Xamoeba_8, FALSE, FALSE,
6224 EL_AMOEBA_WET, ACTION_OTHER, -1
6227 Xdoor_1, TRUE, FALSE,
6228 EL_EM_GATE_1, -1, -1
6231 Xdoor_2, TRUE, FALSE,
6232 EL_EM_GATE_2, -1, -1
6235 Xdoor_3, TRUE, FALSE,
6236 EL_EM_GATE_3, -1, -1
6239 Xdoor_4, TRUE, FALSE,
6240 EL_EM_GATE_4, -1, -1
6243 Xdoor_5, TRUE, FALSE,
6244 EL_EMC_GATE_5, -1, -1
6247 Xdoor_6, TRUE, FALSE,
6248 EL_EMC_GATE_6, -1, -1
6251 Xdoor_7, TRUE, FALSE,
6252 EL_EMC_GATE_7, -1, -1
6255 Xdoor_8, TRUE, FALSE,
6256 EL_EMC_GATE_8, -1, -1
6259 Xkey_1, TRUE, FALSE,
6263 Xkey_2, TRUE, FALSE,
6267 Xkey_3, TRUE, FALSE,
6271 Xkey_4, TRUE, FALSE,
6275 Xkey_5, TRUE, FALSE,
6276 EL_EMC_KEY_5, -1, -1
6279 Xkey_6, TRUE, FALSE,
6280 EL_EMC_KEY_6, -1, -1
6283 Xkey_7, TRUE, FALSE,
6284 EL_EMC_KEY_7, -1, -1
6287 Xkey_8, TRUE, FALSE,
6288 EL_EMC_KEY_8, -1, -1
6291 Xwind_n, TRUE, FALSE,
6292 EL_BALLOON_SWITCH_UP, -1, -1
6295 Xwind_e, TRUE, FALSE,
6296 EL_BALLOON_SWITCH_RIGHT, -1, -1
6299 Xwind_s, TRUE, FALSE,
6300 EL_BALLOON_SWITCH_DOWN, -1, -1
6303 Xwind_w, TRUE, FALSE,
6304 EL_BALLOON_SWITCH_LEFT, -1, -1
6307 Xwind_nesw, TRUE, FALSE,
6308 EL_BALLOON_SWITCH_ANY, -1, -1
6311 Xwind_stop, TRUE, FALSE,
6312 EL_BALLOON_SWITCH_NONE, -1, -1
6316 EL_EM_EXIT_CLOSED, -1, -1
6319 Xexit_1, TRUE, FALSE,
6320 EL_EM_EXIT_OPEN, -1, -1
6323 Xexit_2, FALSE, FALSE,
6324 EL_EM_EXIT_OPEN, -1, -1
6327 Xexit_3, FALSE, FALSE,
6328 EL_EM_EXIT_OPEN, -1, -1
6331 Xdynamite, TRUE, FALSE,
6332 EL_EM_DYNAMITE, -1, -1
6335 Ydynamite_eat, FALSE, FALSE,
6336 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6339 Xdynamite_1, TRUE, FALSE,
6340 EL_EM_DYNAMITE_ACTIVE, -1, -1
6343 Xdynamite_2, FALSE, FALSE,
6344 EL_EM_DYNAMITE_ACTIVE, -1, -1
6347 Xdynamite_3, FALSE, FALSE,
6348 EL_EM_DYNAMITE_ACTIVE, -1, -1
6351 Xdynamite_4, FALSE, FALSE,
6352 EL_EM_DYNAMITE_ACTIVE, -1, -1
6355 Xbumper, TRUE, FALSE,
6356 EL_EMC_SPRING_BUMPER, -1, -1
6359 XbumperB, FALSE, FALSE,
6360 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6363 Xwheel, TRUE, FALSE,
6364 EL_ROBOT_WHEEL, -1, -1
6367 XwheelB, FALSE, FALSE,
6368 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6371 Xswitch, TRUE, FALSE,
6372 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6375 XswitchB, FALSE, FALSE,
6376 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6380 EL_QUICKSAND_EMPTY, -1, -1
6383 Xsand_stone, TRUE, FALSE,
6384 EL_QUICKSAND_FULL, -1, -1
6387 Xsand_stonein_1, FALSE, TRUE,
6388 EL_ROCK, ACTION_FILLING, -1
6391 Xsand_stonein_2, FALSE, TRUE,
6392 EL_ROCK, ACTION_FILLING, -1
6395 Xsand_stonein_3, FALSE, TRUE,
6396 EL_ROCK, ACTION_FILLING, -1
6399 Xsand_stonein_4, FALSE, TRUE,
6400 EL_ROCK, ACTION_FILLING, -1
6403 Xsand_stonesand_1, FALSE, FALSE,
6404 EL_QUICKSAND_EMPTYING, -1, -1
6407 Xsand_stonesand_2, FALSE, FALSE,
6408 EL_QUICKSAND_EMPTYING, -1, -1
6411 Xsand_stonesand_3, FALSE, FALSE,
6412 EL_QUICKSAND_EMPTYING, -1, -1
6415 Xsand_stonesand_4, FALSE, FALSE,
6416 EL_QUICKSAND_EMPTYING, -1, -1
6419 Xsand_stonesand_quickout_1, FALSE, FALSE,
6420 EL_QUICKSAND_EMPTYING, -1, -1
6423 Xsand_stonesand_quickout_2, FALSE, FALSE,
6424 EL_QUICKSAND_EMPTYING, -1, -1
6427 Xsand_stoneout_1, FALSE, FALSE,
6428 EL_ROCK, ACTION_EMPTYING, -1
6431 Xsand_stoneout_2, FALSE, FALSE,
6432 EL_ROCK, ACTION_EMPTYING, -1
6435 Xsand_sandstone_1, FALSE, FALSE,
6436 EL_QUICKSAND_FILLING, -1, -1
6439 Xsand_sandstone_2, FALSE, FALSE,
6440 EL_QUICKSAND_FILLING, -1, -1
6443 Xsand_sandstone_3, FALSE, FALSE,
6444 EL_QUICKSAND_FILLING, -1, -1
6447 Xsand_sandstone_4, FALSE, FALSE,
6448 EL_QUICKSAND_FILLING, -1, -1
6451 Xplant, TRUE, FALSE,
6452 EL_EMC_PLANT, -1, -1
6455 Yplant, FALSE, FALSE,
6456 EL_EMC_PLANT, -1, -1
6459 Xlenses, TRUE, FALSE,
6460 EL_EMC_LENSES, -1, -1
6463 Xmagnify, TRUE, FALSE,
6464 EL_EMC_MAGNIFIER, -1, -1
6467 Xdripper, TRUE, FALSE,
6468 EL_EMC_DRIPPER, -1, -1
6471 XdripperB, FALSE, FALSE,
6472 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6475 Xfake_blank, TRUE, FALSE,
6476 EL_INVISIBLE_WALL, -1, -1
6479 Xfake_blankB, FALSE, FALSE,
6480 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6483 Xfake_grass, TRUE, FALSE,
6484 EL_EMC_FAKE_GRASS, -1, -1
6487 Xfake_grassB, FALSE, FALSE,
6488 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6491 Xfake_door_1, TRUE, FALSE,
6492 EL_EM_GATE_1_GRAY, -1, -1
6495 Xfake_door_2, TRUE, FALSE,
6496 EL_EM_GATE_2_GRAY, -1, -1
6499 Xfake_door_3, TRUE, FALSE,
6500 EL_EM_GATE_3_GRAY, -1, -1
6503 Xfake_door_4, TRUE, FALSE,
6504 EL_EM_GATE_4_GRAY, -1, -1
6507 Xfake_door_5, TRUE, FALSE,
6508 EL_EMC_GATE_5_GRAY, -1, -1
6511 Xfake_door_6, TRUE, FALSE,
6512 EL_EMC_GATE_6_GRAY, -1, -1
6515 Xfake_door_7, TRUE, FALSE,
6516 EL_EMC_GATE_7_GRAY, -1, -1
6519 Xfake_door_8, TRUE, FALSE,
6520 EL_EMC_GATE_8_GRAY, -1, -1
6523 Xfake_acid_1, TRUE, FALSE,
6524 EL_EMC_FAKE_ACID, -1, -1
6527 Xfake_acid_2, FALSE, FALSE,
6528 EL_EMC_FAKE_ACID, -1, -1
6531 Xfake_acid_3, FALSE, FALSE,
6532 EL_EMC_FAKE_ACID, -1, -1
6535 Xfake_acid_4, FALSE, FALSE,
6536 EL_EMC_FAKE_ACID, -1, -1
6539 Xfake_acid_5, FALSE, FALSE,
6540 EL_EMC_FAKE_ACID, -1, -1
6543 Xfake_acid_6, FALSE, FALSE,
6544 EL_EMC_FAKE_ACID, -1, -1
6547 Xfake_acid_7, FALSE, FALSE,
6548 EL_EMC_FAKE_ACID, -1, -1
6551 Xfake_acid_8, FALSE, FALSE,
6552 EL_EMC_FAKE_ACID, -1, -1
6555 Xsteel_1, TRUE, FALSE,
6556 EL_STEELWALL, -1, -1
6559 Xsteel_2, TRUE, FALSE,
6560 EL_EMC_STEELWALL_2, -1, -1
6563 Xsteel_3, TRUE, FALSE,
6564 EL_EMC_STEELWALL_3, -1, -1
6567 Xsteel_4, TRUE, FALSE,
6568 EL_EMC_STEELWALL_4, -1, -1
6571 Xwall_1, TRUE, FALSE,
6575 Xwall_2, TRUE, FALSE,
6576 EL_EMC_WALL_14, -1, -1
6579 Xwall_3, TRUE, FALSE,
6580 EL_EMC_WALL_15, -1, -1
6583 Xwall_4, TRUE, FALSE,
6584 EL_EMC_WALL_16, -1, -1
6587 Xround_wall_1, TRUE, FALSE,
6588 EL_WALL_SLIPPERY, -1, -1
6591 Xround_wall_2, TRUE, FALSE,
6592 EL_EMC_WALL_SLIPPERY_2, -1, -1
6595 Xround_wall_3, TRUE, FALSE,
6596 EL_EMC_WALL_SLIPPERY_3, -1, -1
6599 Xround_wall_4, TRUE, FALSE,
6600 EL_EMC_WALL_SLIPPERY_4, -1, -1
6603 Xdecor_1, TRUE, FALSE,
6604 EL_EMC_WALL_8, -1, -1
6607 Xdecor_2, TRUE, FALSE,
6608 EL_EMC_WALL_6, -1, -1
6611 Xdecor_3, TRUE, FALSE,
6612 EL_EMC_WALL_4, -1, -1
6615 Xdecor_4, TRUE, FALSE,
6616 EL_EMC_WALL_7, -1, -1
6619 Xdecor_5, TRUE, FALSE,
6620 EL_EMC_WALL_5, -1, -1
6623 Xdecor_6, TRUE, FALSE,
6624 EL_EMC_WALL_9, -1, -1
6627 Xdecor_7, TRUE, FALSE,
6628 EL_EMC_WALL_10, -1, -1
6631 Xdecor_8, TRUE, FALSE,
6632 EL_EMC_WALL_1, -1, -1
6635 Xdecor_9, TRUE, FALSE,
6636 EL_EMC_WALL_2, -1, -1
6639 Xdecor_10, TRUE, FALSE,
6640 EL_EMC_WALL_3, -1, -1
6643 Xdecor_11, TRUE, FALSE,
6644 EL_EMC_WALL_11, -1, -1
6647 Xdecor_12, TRUE, FALSE,
6648 EL_EMC_WALL_12, -1, -1
6651 Xalpha_0, TRUE, FALSE,
6652 EL_CHAR('0'), -1, -1
6655 Xalpha_1, TRUE, FALSE,
6656 EL_CHAR('1'), -1, -1
6659 Xalpha_2, TRUE, FALSE,
6660 EL_CHAR('2'), -1, -1
6663 Xalpha_3, TRUE, FALSE,
6664 EL_CHAR('3'), -1, -1
6667 Xalpha_4, TRUE, FALSE,
6668 EL_CHAR('4'), -1, -1
6671 Xalpha_5, TRUE, FALSE,
6672 EL_CHAR('5'), -1, -1
6675 Xalpha_6, TRUE, FALSE,
6676 EL_CHAR('6'), -1, -1
6679 Xalpha_7, TRUE, FALSE,
6680 EL_CHAR('7'), -1, -1
6683 Xalpha_8, TRUE, FALSE,
6684 EL_CHAR('8'), -1, -1
6687 Xalpha_9, TRUE, FALSE,
6688 EL_CHAR('9'), -1, -1
6691 Xalpha_excla, TRUE, FALSE,
6692 EL_CHAR('!'), -1, -1
6695 Xalpha_quote, TRUE, FALSE,
6696 EL_CHAR('"'), -1, -1
6699 Xalpha_comma, TRUE, FALSE,
6700 EL_CHAR(','), -1, -1
6703 Xalpha_minus, TRUE, FALSE,
6704 EL_CHAR('-'), -1, -1
6707 Xalpha_perio, TRUE, FALSE,
6708 EL_CHAR('.'), -1, -1
6711 Xalpha_colon, TRUE, FALSE,
6712 EL_CHAR(':'), -1, -1
6715 Xalpha_quest, TRUE, FALSE,
6716 EL_CHAR('?'), -1, -1
6719 Xalpha_a, TRUE, FALSE,
6720 EL_CHAR('A'), -1, -1
6723 Xalpha_b, TRUE, FALSE,
6724 EL_CHAR('B'), -1, -1
6727 Xalpha_c, TRUE, FALSE,
6728 EL_CHAR('C'), -1, -1
6731 Xalpha_d, TRUE, FALSE,
6732 EL_CHAR('D'), -1, -1
6735 Xalpha_e, TRUE, FALSE,
6736 EL_CHAR('E'), -1, -1
6739 Xalpha_f, TRUE, FALSE,
6740 EL_CHAR('F'), -1, -1
6743 Xalpha_g, TRUE, FALSE,
6744 EL_CHAR('G'), -1, -1
6747 Xalpha_h, TRUE, FALSE,
6748 EL_CHAR('H'), -1, -1
6751 Xalpha_i, TRUE, FALSE,
6752 EL_CHAR('I'), -1, -1
6755 Xalpha_j, TRUE, FALSE,
6756 EL_CHAR('J'), -1, -1
6759 Xalpha_k, TRUE, FALSE,
6760 EL_CHAR('K'), -1, -1
6763 Xalpha_l, TRUE, FALSE,
6764 EL_CHAR('L'), -1, -1
6767 Xalpha_m, TRUE, FALSE,
6768 EL_CHAR('M'), -1, -1
6771 Xalpha_n, TRUE, FALSE,
6772 EL_CHAR('N'), -1, -1
6775 Xalpha_o, TRUE, FALSE,
6776 EL_CHAR('O'), -1, -1
6779 Xalpha_p, TRUE, FALSE,
6780 EL_CHAR('P'), -1, -1
6783 Xalpha_q, TRUE, FALSE,
6784 EL_CHAR('Q'), -1, -1
6787 Xalpha_r, TRUE, FALSE,
6788 EL_CHAR('R'), -1, -1
6791 Xalpha_s, TRUE, FALSE,
6792 EL_CHAR('S'), -1, -1
6795 Xalpha_t, TRUE, FALSE,
6796 EL_CHAR('T'), -1, -1
6799 Xalpha_u, TRUE, FALSE,
6800 EL_CHAR('U'), -1, -1
6803 Xalpha_v, TRUE, FALSE,
6804 EL_CHAR('V'), -1, -1
6807 Xalpha_w, TRUE, FALSE,
6808 EL_CHAR('W'), -1, -1
6811 Xalpha_x, TRUE, FALSE,
6812 EL_CHAR('X'), -1, -1
6815 Xalpha_y, TRUE, FALSE,
6816 EL_CHAR('Y'), -1, -1
6819 Xalpha_z, TRUE, FALSE,
6820 EL_CHAR('Z'), -1, -1
6823 Xalpha_arrow_e, TRUE, FALSE,
6824 EL_CHAR('>'), -1, -1
6827 Xalpha_arrow_w, TRUE, FALSE,
6828 EL_CHAR('<'), -1, -1
6831 Xalpha_copyr, TRUE, FALSE,
6832 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6836 Xboom_bug, FALSE, FALSE,
6837 EL_BUG, ACTION_EXPLODING, -1
6840 Xboom_bomb, FALSE, FALSE,
6841 EL_BOMB, ACTION_EXPLODING, -1
6844 Xboom_android, FALSE, FALSE,
6845 EL_EMC_ANDROID, ACTION_OTHER, -1
6848 Xboom_1, FALSE, FALSE,
6849 EL_DEFAULT, ACTION_EXPLODING, -1
6852 Xboom_2, FALSE, FALSE,
6853 EL_DEFAULT, ACTION_EXPLODING, -1
6856 Znormal, FALSE, FALSE,
6860 Zdynamite, FALSE, FALSE,
6864 Zplayer, FALSE, FALSE,
6868 ZBORDER, FALSE, FALSE,
6878 static struct Mapping_EM_to_RND_player
6887 em_player_mapping_list[] =
6891 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6895 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6899 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6903 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6907 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6911 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6915 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6919 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6923 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6927 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6931 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6935 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6939 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6943 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6947 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6951 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6955 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6959 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6963 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6967 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6971 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6975 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6979 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6983 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6987 EL_PLAYER_1, ACTION_DEFAULT, -1,
6991 EL_PLAYER_2, ACTION_DEFAULT, -1,
6995 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6999 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
7003 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
7007 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
7011 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
7015 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
7019 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
7023 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
7027 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
7031 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
7035 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
7039 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
7043 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
7047 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
7051 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7055 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7059 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7063 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7067 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7071 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7075 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7079 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7083 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7087 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7091 EL_PLAYER_3, ACTION_DEFAULT, -1,
7095 EL_PLAYER_4, ACTION_DEFAULT, -1,
7104 int map_element_RND_to_EM(int element_rnd)
7106 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7107 static boolean mapping_initialized = FALSE;
7109 if (!mapping_initialized)
7113 /* return "Xalpha_quest" for all undefined elements in mapping array */
7114 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7115 mapping_RND_to_EM[i] = Xalpha_quest;
7117 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7118 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7119 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7120 em_object_mapping_list[i].element_em;
7122 mapping_initialized = TRUE;
7125 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7126 return mapping_RND_to_EM[element_rnd];
7128 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7133 int map_element_EM_to_RND(int element_em)
7135 static unsigned short mapping_EM_to_RND[TILE_MAX];
7136 static boolean mapping_initialized = FALSE;
7138 if (!mapping_initialized)
7142 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
7143 for (i = 0; i < TILE_MAX; i++)
7144 mapping_EM_to_RND[i] = EL_UNKNOWN;
7146 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7147 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7148 em_object_mapping_list[i].element_rnd;
7150 mapping_initialized = TRUE;
7153 if (element_em >= 0 && element_em < TILE_MAX)
7154 return mapping_EM_to_RND[element_em];
7156 Error(ERR_WARN, "invalid EM level element %d", element_em);
7161 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7163 struct LevelInfo_EM *level_em = level->native_em_level;
7164 struct LEVEL *lev = level_em->lev;
7167 for (i = 0; i < TILE_MAX; i++)
7168 lev->android_array[i] = Xblank;
7170 for (i = 0; i < level->num_android_clone_elements; i++)
7172 int element_rnd = level->android_clone_element[i];
7173 int element_em = map_element_RND_to_EM(element_rnd);
7175 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7176 if (em_object_mapping_list[j].element_rnd == element_rnd)
7177 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7181 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7183 struct LevelInfo_EM *level_em = level->native_em_level;
7184 struct LEVEL *lev = level_em->lev;
7187 level->num_android_clone_elements = 0;
7189 for (i = 0; i < TILE_MAX; i++)
7191 int element_em = lev->android_array[i];
7193 boolean element_found = FALSE;
7195 if (element_em == Xblank)
7198 element_rnd = map_element_EM_to_RND(element_em);
7200 for (j = 0; j < level->num_android_clone_elements; j++)
7201 if (level->android_clone_element[j] == element_rnd)
7202 element_found = TRUE;
7206 level->android_clone_element[level->num_android_clone_elements++] =
7209 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7214 if (level->num_android_clone_elements == 0)
7216 level->num_android_clone_elements = 1;
7217 level->android_clone_element[0] = EL_EMPTY;
7221 int map_direction_RND_to_EM(int direction)
7223 return (direction == MV_UP ? 0 :
7224 direction == MV_RIGHT ? 1 :
7225 direction == MV_DOWN ? 2 :
7226 direction == MV_LEFT ? 3 :
7230 int map_direction_EM_to_RND(int direction)
7232 return (direction == 0 ? MV_UP :
7233 direction == 1 ? MV_RIGHT :
7234 direction == 2 ? MV_DOWN :
7235 direction == 3 ? MV_LEFT :
7239 int map_element_RND_to_SP(int element_rnd)
7241 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
7243 if (element_rnd >= EL_SP_START &&
7244 element_rnd <= EL_SP_END)
7245 element_sp = element_rnd - EL_SP_START;
7246 else if (element_rnd == EL_EMPTY_SPACE)
7248 else if (element_rnd == EL_INVISIBLE_WALL)
7254 int map_element_SP_to_RND(int element_sp)
7256 int element_rnd = EL_UNKNOWN;
7258 if (element_sp >= 0x00 &&
7260 element_rnd = EL_SP_START + element_sp;
7261 else if (element_sp == 0x28)
7262 element_rnd = EL_INVISIBLE_WALL;
7267 int map_action_SP_to_RND(int action_sp)
7271 case actActive: return ACTION_ACTIVE;
7272 case actImpact: return ACTION_IMPACT;
7273 case actExploding: return ACTION_EXPLODING;
7274 case actDigging: return ACTION_DIGGING;
7275 case actSnapping: return ACTION_SNAPPING;
7276 case actCollecting: return ACTION_COLLECTING;
7277 case actPassing: return ACTION_PASSING;
7278 case actPushing: return ACTION_PUSHING;
7279 case actDropping: return ACTION_DROPPING;
7281 default: return ACTION_DEFAULT;
7285 int map_element_RND_to_MM(int element_rnd)
7287 return (element_rnd >= EL_MM_START_1 &&
7288 element_rnd <= EL_MM_END_1 ?
7289 EL_MM_START_1_NATIVE + element_rnd - EL_MM_START_1 :
7291 element_rnd >= EL_MM_START_2 &&
7292 element_rnd <= EL_MM_END_2 ?
7293 EL_MM_START_2_NATIVE + element_rnd - EL_MM_START_2 :
7295 element_rnd >= EL_CHAR_START &&
7296 element_rnd <= EL_CHAR_END ?
7297 EL_MM_CHAR_START_NATIVE + element_rnd - EL_CHAR_START :
7299 element_rnd >= EL_MM_RUNTIME_START &&
7300 element_rnd <= EL_MM_RUNTIME_END ?
7301 EL_MM_RUNTIME_START_NATIVE + element_rnd - EL_MM_RUNTIME_START :
7303 element_rnd >= EL_MM_DUMMY_START &&
7304 element_rnd <= EL_MM_DUMMY_END ?
7305 EL_MM_DUMMY_START_NATIVE + element_rnd - EL_MM_DUMMY_START :
7307 EL_MM_EMPTY_NATIVE);
7310 int map_element_MM_to_RND(int element_mm)
7312 return (element_mm == EL_MM_EMPTY_NATIVE ||
7313 element_mm == EL_DF_EMPTY_NATIVE ?
7316 element_mm >= EL_MM_START_1_NATIVE &&
7317 element_mm <= EL_MM_END_1_NATIVE ?
7318 EL_MM_START_1 + element_mm - EL_MM_START_1_NATIVE :
7320 element_mm >= EL_MM_START_2_NATIVE &&
7321 element_mm <= EL_MM_END_2_NATIVE ?
7322 EL_MM_START_2 + element_mm - EL_MM_START_2_NATIVE :
7324 element_mm >= EL_MM_CHAR_START_NATIVE &&
7325 element_mm <= EL_MM_CHAR_END_NATIVE ?
7326 EL_CHAR_START + element_mm - EL_MM_CHAR_START_NATIVE :
7328 element_mm >= EL_MM_RUNTIME_START_NATIVE &&
7329 element_mm <= EL_MM_RUNTIME_END_NATIVE ?
7330 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE :
7332 element_mm >= EL_MM_DUMMY_START_NATIVE &&
7333 element_mm <= EL_MM_DUMMY_END_NATIVE ?
7334 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE :
7339 int map_mm_wall_element(int element)
7341 return (element >= EL_MM_STEEL_WALL_START &&
7342 element <= EL_MM_STEEL_WALL_END ?
7345 element >= EL_MM_WOODEN_WALL_START &&
7346 element <= EL_MM_WOODEN_WALL_END ?
7349 element >= EL_MM_ICE_WALL_START &&
7350 element <= EL_MM_ICE_WALL_END ?
7353 element >= EL_MM_AMOEBA_WALL_START &&
7354 element <= EL_MM_AMOEBA_WALL_END ?
7357 element >= EL_DF_STEEL_WALL_START &&
7358 element <= EL_DF_STEEL_WALL_END ?
7361 element >= EL_DF_WOODEN_WALL_START &&
7362 element <= EL_DF_WOODEN_WALL_END ?
7368 int map_mm_wall_element_editor(int element)
7372 case EL_MM_STEEL_WALL: return EL_MM_STEEL_WALL_START;
7373 case EL_MM_WOODEN_WALL: return EL_MM_WOODEN_WALL_START;
7374 case EL_MM_ICE_WALL: return EL_MM_ICE_WALL_START;
7375 case EL_MM_AMOEBA_WALL: return EL_MM_AMOEBA_WALL_START;
7376 case EL_DF_STEEL_WALL: return EL_DF_STEEL_WALL_START;
7377 case EL_DF_WOODEN_WALL: return EL_DF_WOODEN_WALL_START;
7379 default: return element;
7383 int get_next_element(int element)
7387 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7388 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7389 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7390 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7391 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7392 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7393 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7394 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7395 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7396 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7397 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7399 default: return element;
7403 int el2img_mm(int element_mm)
7405 return el2img(map_element_MM_to_RND(element_mm));
7408 int el_act_dir2img(int element, int action, int direction)
7410 element = GFX_ELEMENT(element);
7411 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7413 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7414 return element_info[element].direction_graphic[action][direction];
7417 static int el_act_dir2crm(int element, int action, int direction)
7419 element = GFX_ELEMENT(element);
7420 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7422 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7423 return element_info[element].direction_crumbled[action][direction];
7426 int el_act2img(int element, int action)
7428 element = GFX_ELEMENT(element);
7430 return element_info[element].graphic[action];
7433 int el_act2crm(int element, int action)
7435 element = GFX_ELEMENT(element);
7437 return element_info[element].crumbled[action];
7440 int el_dir2img(int element, int direction)
7442 element = GFX_ELEMENT(element);
7444 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7447 int el2baseimg(int element)
7449 return element_info[element].graphic[ACTION_DEFAULT];
7452 int el2img(int element)
7454 element = GFX_ELEMENT(element);
7456 return element_info[element].graphic[ACTION_DEFAULT];
7459 int el2edimg(int element)
7461 element = GFX_ELEMENT(element);
7463 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7466 int el2preimg(int element)
7468 element = GFX_ELEMENT(element);
7470 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7473 int el2panelimg(int element)
7475 element = GFX_ELEMENT(element);
7477 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7480 int font2baseimg(int font_nr)
7482 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7485 int getBeltNrFromBeltElement(int element)
7487 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7488 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7489 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
7492 int getBeltNrFromBeltActiveElement(int element)
7494 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
7495 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
7496 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
7499 int getBeltNrFromBeltSwitchElement(int element)
7501 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
7502 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
7503 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
7506 int getBeltDirNrFromBeltElement(int element)
7508 static int belt_base_element[4] =
7510 EL_CONVEYOR_BELT_1_LEFT,
7511 EL_CONVEYOR_BELT_2_LEFT,
7512 EL_CONVEYOR_BELT_3_LEFT,
7513 EL_CONVEYOR_BELT_4_LEFT
7516 int belt_nr = getBeltNrFromBeltElement(element);
7517 int belt_dir_nr = element - belt_base_element[belt_nr];
7519 return (belt_dir_nr % 3);
7522 int getBeltDirNrFromBeltSwitchElement(int element)
7524 static int belt_base_element[4] =
7526 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7527 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7528 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7529 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7532 int belt_nr = getBeltNrFromBeltSwitchElement(element);
7533 int belt_dir_nr = element - belt_base_element[belt_nr];
7535 return (belt_dir_nr % 3);
7538 int getBeltDirFromBeltElement(int element)
7540 static int belt_move_dir[3] =
7547 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7549 return belt_move_dir[belt_dir_nr];
7552 int getBeltDirFromBeltSwitchElement(int element)
7554 static int belt_move_dir[3] =
7561 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7563 return belt_move_dir[belt_dir_nr];
7566 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7568 static int belt_base_element[4] =
7570 EL_CONVEYOR_BELT_1_LEFT,
7571 EL_CONVEYOR_BELT_2_LEFT,
7572 EL_CONVEYOR_BELT_3_LEFT,
7573 EL_CONVEYOR_BELT_4_LEFT
7576 return belt_base_element[belt_nr] + belt_dir_nr;
7579 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7581 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7583 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7586 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7588 static int belt_base_element[4] =
7590 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7591 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7592 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7593 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7596 return belt_base_element[belt_nr] + belt_dir_nr;
7599 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7601 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7603 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7606 boolean getTeamMode_EM()
7608 return game.team_mode;
7611 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7613 int game_frame_delay_value;
7615 game_frame_delay_value =
7616 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7617 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7620 if (tape.playing && tape.warp_forward && !tape.pausing)
7621 game_frame_delay_value = 0;
7623 return game_frame_delay_value;
7626 unsigned int InitRND(int seed)
7628 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7629 return InitEngineRandom_EM(seed);
7630 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7631 return InitEngineRandom_SP(seed);
7632 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
7633 return InitEngineRandom_MM(seed);
7635 return InitEngineRandom_RND(seed);
7638 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7639 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7641 inline static int get_effective_element_EM(int tile, int frame_em)
7643 int element = object_mapping[tile].element_rnd;
7644 int action = object_mapping[tile].action;
7645 boolean is_backside = object_mapping[tile].is_backside;
7646 boolean action_removing = (action == ACTION_DIGGING ||
7647 action == ACTION_SNAPPING ||
7648 action == ACTION_COLLECTING);
7654 case Yacid_splash_eB:
7655 case Yacid_splash_wB:
7656 return (frame_em > 5 ? EL_EMPTY : element);
7662 else /* frame_em == 7 */
7666 case Yacid_splash_eB:
7667 case Yacid_splash_wB:
7670 case Yemerald_stone:
7673 case Ydiamond_stone:
7677 case Xdrip_stretchB:
7696 case Xsand_stonein_1:
7697 case Xsand_stonein_2:
7698 case Xsand_stonein_3:
7699 case Xsand_stonein_4:
7703 return (is_backside || action_removing ? EL_EMPTY : element);
7708 inline static boolean check_linear_animation_EM(int tile)
7712 case Xsand_stonesand_1:
7713 case Xsand_stonesand_quickout_1:
7714 case Xsand_sandstone_1:
7715 case Xsand_stonein_1:
7716 case Xsand_stoneout_1:
7735 case Yacid_splash_eB:
7736 case Yacid_splash_wB:
7737 case Yemerald_stone:
7744 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7745 boolean has_crumbled_graphics,
7746 int crumbled, int sync_frame)
7748 /* if element can be crumbled, but certain action graphics are just empty
7749 space (like instantly snapping sand to empty space in 1 frame), do not
7750 treat these empty space graphics as crumbled graphics in EMC engine */
7751 if (crumbled == IMG_EMPTY_SPACE)
7752 has_crumbled_graphics = FALSE;
7754 if (has_crumbled_graphics)
7756 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7757 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7758 g_crumbled->anim_delay,
7759 g_crumbled->anim_mode,
7760 g_crumbled->anim_start_frame,
7763 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7764 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7766 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7767 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
7769 g_em->has_crumbled_graphics = TRUE;
7773 g_em->crumbled_bitmap = NULL;
7774 g_em->crumbled_src_x = 0;
7775 g_em->crumbled_src_y = 0;
7776 g_em->crumbled_border_size = 0;
7777 g_em->crumbled_tile_size = 0;
7779 g_em->has_crumbled_graphics = FALSE;
7783 void ResetGfxAnimation_EM(int x, int y, int tile)
7788 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7789 int tile, int frame_em, int x, int y)
7791 int action = object_mapping[tile].action;
7792 int direction = object_mapping[tile].direction;
7793 int effective_element = get_effective_element_EM(tile, frame_em);
7794 int graphic = (direction == MV_NONE ?
7795 el_act2img(effective_element, action) :
7796 el_act_dir2img(effective_element, action, direction));
7797 struct GraphicInfo *g = &graphic_info[graphic];
7799 boolean action_removing = (action == ACTION_DIGGING ||
7800 action == ACTION_SNAPPING ||
7801 action == ACTION_COLLECTING);
7802 boolean action_moving = (action == ACTION_FALLING ||
7803 action == ACTION_MOVING ||
7804 action == ACTION_PUSHING ||
7805 action == ACTION_EATING ||
7806 action == ACTION_FILLING ||
7807 action == ACTION_EMPTYING);
7808 boolean action_falling = (action == ACTION_FALLING ||
7809 action == ACTION_FILLING ||
7810 action == ACTION_EMPTYING);
7812 /* special case: graphic uses "2nd movement tile" and has defined
7813 7 frames for movement animation (or less) => use default graphic
7814 for last (8th) frame which ends the movement animation */
7815 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7817 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7818 graphic = (direction == MV_NONE ?
7819 el_act2img(effective_element, action) :
7820 el_act_dir2img(effective_element, action, direction));
7822 g = &graphic_info[graphic];
7825 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7829 else if (action_moving)
7831 boolean is_backside = object_mapping[tile].is_backside;
7835 int direction = object_mapping[tile].direction;
7836 int move_dir = (action_falling ? MV_DOWN : direction);
7841 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7842 if (g->double_movement && frame_em == 0)
7846 if (move_dir == MV_LEFT)
7847 GfxFrame[x - 1][y] = GfxFrame[x][y];
7848 else if (move_dir == MV_RIGHT)
7849 GfxFrame[x + 1][y] = GfxFrame[x][y];
7850 else if (move_dir == MV_UP)
7851 GfxFrame[x][y - 1] = GfxFrame[x][y];
7852 else if (move_dir == MV_DOWN)
7853 GfxFrame[x][y + 1] = GfxFrame[x][y];
7860 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7861 if (tile == Xsand_stonesand_quickout_1 ||
7862 tile == Xsand_stonesand_quickout_2)
7866 if (graphic_info[graphic].anim_global_sync)
7867 sync_frame = FrameCounter;
7868 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7869 sync_frame = GfxFrame[x][y];
7871 sync_frame = 0; /* playfield border (pseudo steel) */
7873 SetRandomAnimationValue(x, y);
7875 int frame = getAnimationFrame(g->anim_frames,
7878 g->anim_start_frame,
7881 g_em->unique_identifier =
7882 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7885 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7886 int tile, int frame_em, int x, int y)
7888 int action = object_mapping[tile].action;
7889 int direction = object_mapping[tile].direction;
7890 boolean is_backside = object_mapping[tile].is_backside;
7891 int effective_element = get_effective_element_EM(tile, frame_em);
7892 int effective_action = action;
7893 int graphic = (direction == MV_NONE ?
7894 el_act2img(effective_element, effective_action) :
7895 el_act_dir2img(effective_element, effective_action,
7897 int crumbled = (direction == MV_NONE ?
7898 el_act2crm(effective_element, effective_action) :
7899 el_act_dir2crm(effective_element, effective_action,
7901 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7902 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7903 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7904 struct GraphicInfo *g = &graphic_info[graphic];
7907 /* special case: graphic uses "2nd movement tile" and has defined
7908 7 frames for movement animation (or less) => use default graphic
7909 for last (8th) frame which ends the movement animation */
7910 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7912 effective_action = ACTION_DEFAULT;
7913 graphic = (direction == MV_NONE ?
7914 el_act2img(effective_element, effective_action) :
7915 el_act_dir2img(effective_element, effective_action,
7917 crumbled = (direction == MV_NONE ?
7918 el_act2crm(effective_element, effective_action) :
7919 el_act_dir2crm(effective_element, effective_action,
7922 g = &graphic_info[graphic];
7925 if (graphic_info[graphic].anim_global_sync)
7926 sync_frame = FrameCounter;
7927 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7928 sync_frame = GfxFrame[x][y];
7930 sync_frame = 0; /* playfield border (pseudo steel) */
7932 SetRandomAnimationValue(x, y);
7934 int frame = getAnimationFrame(g->anim_frames,
7937 g->anim_start_frame,
7940 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7941 g->double_movement && is_backside);
7943 /* (updating the "crumbled" graphic definitions is probably not really needed,
7944 as animations for crumbled graphics can't be longer than one EMC cycle) */
7945 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7949 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7950 int player_nr, int anim, int frame_em)
7952 int element = player_mapping[player_nr][anim].element_rnd;
7953 int action = player_mapping[player_nr][anim].action;
7954 int direction = player_mapping[player_nr][anim].direction;
7955 int graphic = (direction == MV_NONE ?
7956 el_act2img(element, action) :
7957 el_act_dir2img(element, action, direction));
7958 struct GraphicInfo *g = &graphic_info[graphic];
7961 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7963 stored_player[player_nr].StepFrame = frame_em;
7965 sync_frame = stored_player[player_nr].Frame;
7967 int frame = getAnimationFrame(g->anim_frames,
7970 g->anim_start_frame,
7973 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7974 &g_em->src_x, &g_em->src_y, FALSE);
7977 void InitGraphicInfo_EM(void)
7982 int num_em_gfx_errors = 0;
7984 if (graphic_info_em_object[0][0].bitmap == NULL)
7986 /* EM graphics not yet initialized in em_open_all() */
7991 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7994 /* always start with reliable default values */
7995 for (i = 0; i < TILE_MAX; i++)
7997 object_mapping[i].element_rnd = EL_UNKNOWN;
7998 object_mapping[i].is_backside = FALSE;
7999 object_mapping[i].action = ACTION_DEFAULT;
8000 object_mapping[i].direction = MV_NONE;
8003 /* always start with reliable default values */
8004 for (p = 0; p < MAX_PLAYERS; p++)
8006 for (i = 0; i < SPR_MAX; i++)
8008 player_mapping[p][i].element_rnd = EL_UNKNOWN;
8009 player_mapping[p][i].action = ACTION_DEFAULT;
8010 player_mapping[p][i].direction = MV_NONE;
8014 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
8016 int e = em_object_mapping_list[i].element_em;
8018 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
8019 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
8021 if (em_object_mapping_list[i].action != -1)
8022 object_mapping[e].action = em_object_mapping_list[i].action;
8024 if (em_object_mapping_list[i].direction != -1)
8025 object_mapping[e].direction =
8026 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
8029 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
8031 int a = em_player_mapping_list[i].action_em;
8032 int p = em_player_mapping_list[i].player_nr;
8034 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
8036 if (em_player_mapping_list[i].action != -1)
8037 player_mapping[p][a].action = em_player_mapping_list[i].action;
8039 if (em_player_mapping_list[i].direction != -1)
8040 player_mapping[p][a].direction =
8041 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
8044 for (i = 0; i < TILE_MAX; i++)
8046 int element = object_mapping[i].element_rnd;
8047 int action = object_mapping[i].action;
8048 int direction = object_mapping[i].direction;
8049 boolean is_backside = object_mapping[i].is_backside;
8050 boolean action_exploding = ((action == ACTION_EXPLODING ||
8051 action == ACTION_SMASHED_BY_ROCK ||
8052 action == ACTION_SMASHED_BY_SPRING) &&
8053 element != EL_DIAMOND);
8054 boolean action_active = (action == ACTION_ACTIVE);
8055 boolean action_other = (action == ACTION_OTHER);
8057 for (j = 0; j < 8; j++)
8059 int effective_element = get_effective_element_EM(i, j);
8060 int effective_action = (j < 7 ? action :
8061 i == Xdrip_stretch ? action :
8062 i == Xdrip_stretchB ? action :
8063 i == Ydrip_s1 ? action :
8064 i == Ydrip_s1B ? action :
8065 i == Xball_1B ? action :
8066 i == Xball_2 ? action :
8067 i == Xball_2B ? action :
8068 i == Yball_eat ? action :
8069 i == Ykey_1_eat ? action :
8070 i == Ykey_2_eat ? action :
8071 i == Ykey_3_eat ? action :
8072 i == Ykey_4_eat ? action :
8073 i == Ykey_5_eat ? action :
8074 i == Ykey_6_eat ? action :
8075 i == Ykey_7_eat ? action :
8076 i == Ykey_8_eat ? action :
8077 i == Ylenses_eat ? action :
8078 i == Ymagnify_eat ? action :
8079 i == Ygrass_eat ? action :
8080 i == Ydirt_eat ? action :
8081 i == Xsand_stonein_1 ? action :
8082 i == Xsand_stonein_2 ? action :
8083 i == Xsand_stonein_3 ? action :
8084 i == Xsand_stonein_4 ? action :
8085 i == Xsand_stoneout_1 ? action :
8086 i == Xsand_stoneout_2 ? action :
8087 i == Xboom_android ? ACTION_EXPLODING :
8088 action_exploding ? ACTION_EXPLODING :
8089 action_active ? action :
8090 action_other ? action :
8092 int graphic = (el_act_dir2img(effective_element, effective_action,
8094 int crumbled = (el_act_dir2crm(effective_element, effective_action,
8096 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
8097 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
8098 boolean has_action_graphics = (graphic != base_graphic);
8099 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
8100 struct GraphicInfo *g = &graphic_info[graphic];
8101 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8104 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
8105 boolean special_animation = (action != ACTION_DEFAULT &&
8106 g->anim_frames == 3 &&
8107 g->anim_delay == 2 &&
8108 g->anim_mode & ANIM_LINEAR);
8109 int sync_frame = (i == Xdrip_stretch ? 7 :
8110 i == Xdrip_stretchB ? 7 :
8111 i == Ydrip_s2 ? j + 8 :
8112 i == Ydrip_s2B ? j + 8 :
8121 i == Xfake_acid_1 ? 0 :
8122 i == Xfake_acid_2 ? 10 :
8123 i == Xfake_acid_3 ? 20 :
8124 i == Xfake_acid_4 ? 30 :
8125 i == Xfake_acid_5 ? 40 :
8126 i == Xfake_acid_6 ? 50 :
8127 i == Xfake_acid_7 ? 60 :
8128 i == Xfake_acid_8 ? 70 :
8130 i == Xball_2B ? j + 8 :
8131 i == Yball_eat ? j + 1 :
8132 i == Ykey_1_eat ? j + 1 :
8133 i == Ykey_2_eat ? j + 1 :
8134 i == Ykey_3_eat ? j + 1 :
8135 i == Ykey_4_eat ? j + 1 :
8136 i == Ykey_5_eat ? j + 1 :
8137 i == Ykey_6_eat ? j + 1 :
8138 i == Ykey_7_eat ? j + 1 :
8139 i == Ykey_8_eat ? j + 1 :
8140 i == Ylenses_eat ? j + 1 :
8141 i == Ymagnify_eat ? j + 1 :
8142 i == Ygrass_eat ? j + 1 :
8143 i == Ydirt_eat ? j + 1 :
8144 i == Xamoeba_1 ? 0 :
8145 i == Xamoeba_2 ? 1 :
8146 i == Xamoeba_3 ? 2 :
8147 i == Xamoeba_4 ? 3 :
8148 i == Xamoeba_5 ? 0 :
8149 i == Xamoeba_6 ? 1 :
8150 i == Xamoeba_7 ? 2 :
8151 i == Xamoeba_8 ? 3 :
8152 i == Xexit_2 ? j + 8 :
8153 i == Xexit_3 ? j + 16 :
8154 i == Xdynamite_1 ? 0 :
8155 i == Xdynamite_2 ? 8 :
8156 i == Xdynamite_3 ? 16 :
8157 i == Xdynamite_4 ? 24 :
8158 i == Xsand_stonein_1 ? j + 1 :
8159 i == Xsand_stonein_2 ? j + 9 :
8160 i == Xsand_stonein_3 ? j + 17 :
8161 i == Xsand_stonein_4 ? j + 25 :
8162 i == Xsand_stoneout_1 && j == 0 ? 0 :
8163 i == Xsand_stoneout_1 && j == 1 ? 0 :
8164 i == Xsand_stoneout_1 && j == 2 ? 1 :
8165 i == Xsand_stoneout_1 && j == 3 ? 2 :
8166 i == Xsand_stoneout_1 && j == 4 ? 2 :
8167 i == Xsand_stoneout_1 && j == 5 ? 3 :
8168 i == Xsand_stoneout_1 && j == 6 ? 4 :
8169 i == Xsand_stoneout_1 && j == 7 ? 4 :
8170 i == Xsand_stoneout_2 && j == 0 ? 5 :
8171 i == Xsand_stoneout_2 && j == 1 ? 6 :
8172 i == Xsand_stoneout_2 && j == 2 ? 7 :
8173 i == Xsand_stoneout_2 && j == 3 ? 8 :
8174 i == Xsand_stoneout_2 && j == 4 ? 9 :
8175 i == Xsand_stoneout_2 && j == 5 ? 11 :
8176 i == Xsand_stoneout_2 && j == 6 ? 13 :
8177 i == Xsand_stoneout_2 && j == 7 ? 15 :
8178 i == Xboom_bug && j == 1 ? 2 :
8179 i == Xboom_bug && j == 2 ? 2 :
8180 i == Xboom_bug && j == 3 ? 4 :
8181 i == Xboom_bug && j == 4 ? 4 :
8182 i == Xboom_bug && j == 5 ? 2 :
8183 i == Xboom_bug && j == 6 ? 2 :
8184 i == Xboom_bug && j == 7 ? 0 :
8185 i == Xboom_bomb && j == 1 ? 2 :
8186 i == Xboom_bomb && j == 2 ? 2 :
8187 i == Xboom_bomb && j == 3 ? 4 :
8188 i == Xboom_bomb && j == 4 ? 4 :
8189 i == Xboom_bomb && j == 5 ? 2 :
8190 i == Xboom_bomb && j == 6 ? 2 :
8191 i == Xboom_bomb && j == 7 ? 0 :
8192 i == Xboom_android && j == 7 ? 6 :
8193 i == Xboom_1 && j == 1 ? 2 :
8194 i == Xboom_1 && j == 2 ? 2 :
8195 i == Xboom_1 && j == 3 ? 4 :
8196 i == Xboom_1 && j == 4 ? 4 :
8197 i == Xboom_1 && j == 5 ? 6 :
8198 i == Xboom_1 && j == 6 ? 6 :
8199 i == Xboom_1 && j == 7 ? 8 :
8200 i == Xboom_2 && j == 0 ? 8 :
8201 i == Xboom_2 && j == 1 ? 8 :
8202 i == Xboom_2 && j == 2 ? 10 :
8203 i == Xboom_2 && j == 3 ? 10 :
8204 i == Xboom_2 && j == 4 ? 10 :
8205 i == Xboom_2 && j == 5 ? 12 :
8206 i == Xboom_2 && j == 6 ? 12 :
8207 i == Xboom_2 && j == 7 ? 12 :
8208 special_animation && j == 4 ? 3 :
8209 effective_action != action ? 0 :
8213 Bitmap *debug_bitmap = g_em->bitmap;
8214 int debug_src_x = g_em->src_x;
8215 int debug_src_y = g_em->src_y;
8218 int frame = getAnimationFrame(g->anim_frames,
8221 g->anim_start_frame,
8224 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8225 g->double_movement && is_backside);
8227 g_em->bitmap = src_bitmap;
8228 g_em->src_x = src_x;
8229 g_em->src_y = src_y;
8230 g_em->src_offset_x = 0;
8231 g_em->src_offset_y = 0;
8232 g_em->dst_offset_x = 0;
8233 g_em->dst_offset_y = 0;
8234 g_em->width = TILEX;
8235 g_em->height = TILEY;
8237 g_em->preserve_background = FALSE;
8239 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8242 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8243 effective_action == ACTION_MOVING ||
8244 effective_action == ACTION_PUSHING ||
8245 effective_action == ACTION_EATING)) ||
8246 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8247 effective_action == ACTION_EMPTYING)))
8250 (effective_action == ACTION_FALLING ||
8251 effective_action == ACTION_FILLING ||
8252 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8253 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8254 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8255 int num_steps = (i == Ydrip_s1 ? 16 :
8256 i == Ydrip_s1B ? 16 :
8257 i == Ydrip_s2 ? 16 :
8258 i == Ydrip_s2B ? 16 :
8259 i == Xsand_stonein_1 ? 32 :
8260 i == Xsand_stonein_2 ? 32 :
8261 i == Xsand_stonein_3 ? 32 :
8262 i == Xsand_stonein_4 ? 32 :
8263 i == Xsand_stoneout_1 ? 16 :
8264 i == Xsand_stoneout_2 ? 16 : 8);
8265 int cx = ABS(dx) * (TILEX / num_steps);
8266 int cy = ABS(dy) * (TILEY / num_steps);
8267 int step_frame = (i == Ydrip_s2 ? j + 8 :
8268 i == Ydrip_s2B ? j + 8 :
8269 i == Xsand_stonein_2 ? j + 8 :
8270 i == Xsand_stonein_3 ? j + 16 :
8271 i == Xsand_stonein_4 ? j + 24 :
8272 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8273 int step = (is_backside ? step_frame : num_steps - step_frame);
8275 if (is_backside) /* tile where movement starts */
8277 if (dx < 0 || dy < 0)
8279 g_em->src_offset_x = cx * step;
8280 g_em->src_offset_y = cy * step;
8284 g_em->dst_offset_x = cx * step;
8285 g_em->dst_offset_y = cy * step;
8288 else /* tile where movement ends */
8290 if (dx < 0 || dy < 0)
8292 g_em->dst_offset_x = cx * step;
8293 g_em->dst_offset_y = cy * step;
8297 g_em->src_offset_x = cx * step;
8298 g_em->src_offset_y = cy * step;
8302 g_em->width = TILEX - cx * step;
8303 g_em->height = TILEY - cy * step;
8306 /* create unique graphic identifier to decide if tile must be redrawn */
8307 /* bit 31 - 16 (16 bit): EM style graphic
8308 bit 15 - 12 ( 4 bit): EM style frame
8309 bit 11 - 6 ( 6 bit): graphic width
8310 bit 5 - 0 ( 6 bit): graphic height */
8311 g_em->unique_identifier =
8312 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8316 /* skip check for EMC elements not contained in original EMC artwork */
8317 if (element == EL_EMC_FAKE_ACID)
8320 if (g_em->bitmap != debug_bitmap ||
8321 g_em->src_x != debug_src_x ||
8322 g_em->src_y != debug_src_y ||
8323 g_em->src_offset_x != 0 ||
8324 g_em->src_offset_y != 0 ||
8325 g_em->dst_offset_x != 0 ||
8326 g_em->dst_offset_y != 0 ||
8327 g_em->width != TILEX ||
8328 g_em->height != TILEY)
8330 static int last_i = -1;
8338 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8339 i, element, element_info[element].token_name,
8340 element_action_info[effective_action].suffix, direction);
8342 if (element != effective_element)
8343 printf(" [%d ('%s')]",
8345 element_info[effective_element].token_name);
8349 if (g_em->bitmap != debug_bitmap)
8350 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8351 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8353 if (g_em->src_x != debug_src_x ||
8354 g_em->src_y != debug_src_y)
8355 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8356 j, (is_backside ? 'B' : 'F'),
8357 g_em->src_x, g_em->src_y,
8358 g_em->src_x / 32, g_em->src_y / 32,
8359 debug_src_x, debug_src_y,
8360 debug_src_x / 32, debug_src_y / 32);
8362 if (g_em->src_offset_x != 0 ||
8363 g_em->src_offset_y != 0 ||
8364 g_em->dst_offset_x != 0 ||
8365 g_em->dst_offset_y != 0)
8366 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8368 g_em->src_offset_x, g_em->src_offset_y,
8369 g_em->dst_offset_x, g_em->dst_offset_y);
8371 if (g_em->width != TILEX ||
8372 g_em->height != TILEY)
8373 printf(" %d (%d): size %d,%d should be %d,%d\n",
8375 g_em->width, g_em->height, TILEX, TILEY);
8377 num_em_gfx_errors++;
8384 for (i = 0; i < TILE_MAX; i++)
8386 for (j = 0; j < 8; j++)
8388 int element = object_mapping[i].element_rnd;
8389 int action = object_mapping[i].action;
8390 int direction = object_mapping[i].direction;
8391 boolean is_backside = object_mapping[i].is_backside;
8392 int graphic_action = el_act_dir2img(element, action, direction);
8393 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8395 if ((action == ACTION_SMASHED_BY_ROCK ||
8396 action == ACTION_SMASHED_BY_SPRING ||
8397 action == ACTION_EATING) &&
8398 graphic_action == graphic_default)
8400 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8401 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8402 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8403 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8406 /* no separate animation for "smashed by rock" -- use rock instead */
8407 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8408 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8410 g_em->bitmap = g_xx->bitmap;
8411 g_em->src_x = g_xx->src_x;
8412 g_em->src_y = g_xx->src_y;
8413 g_em->src_offset_x = g_xx->src_offset_x;
8414 g_em->src_offset_y = g_xx->src_offset_y;
8415 g_em->dst_offset_x = g_xx->dst_offset_x;
8416 g_em->dst_offset_y = g_xx->dst_offset_y;
8417 g_em->width = g_xx->width;
8418 g_em->height = g_xx->height;
8419 g_em->unique_identifier = g_xx->unique_identifier;
8422 g_em->preserve_background = TRUE;
8427 for (p = 0; p < MAX_PLAYERS; p++)
8429 for (i = 0; i < SPR_MAX; i++)
8431 int element = player_mapping[p][i].element_rnd;
8432 int action = player_mapping[p][i].action;
8433 int direction = player_mapping[p][i].direction;
8435 for (j = 0; j < 8; j++)
8437 int effective_element = element;
8438 int effective_action = action;
8439 int graphic = (direction == MV_NONE ?
8440 el_act2img(effective_element, effective_action) :
8441 el_act_dir2img(effective_element, effective_action,
8443 struct GraphicInfo *g = &graphic_info[graphic];
8444 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8450 Bitmap *debug_bitmap = g_em->bitmap;
8451 int debug_src_x = g_em->src_x;
8452 int debug_src_y = g_em->src_y;
8455 int frame = getAnimationFrame(g->anim_frames,
8458 g->anim_start_frame,
8461 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8463 g_em->bitmap = src_bitmap;
8464 g_em->src_x = src_x;
8465 g_em->src_y = src_y;
8466 g_em->src_offset_x = 0;
8467 g_em->src_offset_y = 0;
8468 g_em->dst_offset_x = 0;
8469 g_em->dst_offset_y = 0;
8470 g_em->width = TILEX;
8471 g_em->height = TILEY;
8475 /* skip check for EMC elements not contained in original EMC artwork */
8476 if (element == EL_PLAYER_3 ||
8477 element == EL_PLAYER_4)
8480 if (g_em->bitmap != debug_bitmap ||
8481 g_em->src_x != debug_src_x ||
8482 g_em->src_y != debug_src_y)
8484 static int last_i = -1;
8492 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
8493 p, i, element, element_info[element].token_name,
8494 element_action_info[effective_action].suffix, direction);
8496 if (element != effective_element)
8497 printf(" [%d ('%s')]",
8499 element_info[effective_element].token_name);
8503 if (g_em->bitmap != debug_bitmap)
8504 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
8505 j, (int)(g_em->bitmap), (int)(debug_bitmap));
8507 if (g_em->src_x != debug_src_x ||
8508 g_em->src_y != debug_src_y)
8509 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8511 g_em->src_x, g_em->src_y,
8512 g_em->src_x / 32, g_em->src_y / 32,
8513 debug_src_x, debug_src_y,
8514 debug_src_x / 32, debug_src_y / 32);
8516 num_em_gfx_errors++;
8526 printf("::: [%d errors found]\n", num_em_gfx_errors);
8532 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
8533 boolean any_player_moving,
8534 boolean any_player_snapping,
8535 boolean any_player_dropping)
8537 if (frame == 0 && !any_player_dropping)
8539 if (!local_player->was_waiting)
8541 if (!CheckSaveEngineSnapshotToList())
8544 local_player->was_waiting = TRUE;
8547 else if (any_player_moving || any_player_snapping || any_player_dropping)
8549 local_player->was_waiting = FALSE;
8553 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8554 boolean murphy_is_dropping)
8556 if (murphy_is_waiting)
8558 if (!local_player->was_waiting)
8560 if (!CheckSaveEngineSnapshotToList())
8563 local_player->was_waiting = TRUE;
8568 local_player->was_waiting = FALSE;
8572 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8573 boolean any_player_moving,
8574 boolean any_player_snapping,
8575 boolean any_player_dropping)
8577 if (tape.single_step && tape.recording && !tape.pausing)
8578 if (frame == 0 && !any_player_dropping)
8579 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8581 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8582 any_player_snapping, any_player_dropping);
8585 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8586 boolean murphy_is_dropping)
8588 boolean murphy_starts_dropping = FALSE;
8591 for (i = 0; i < MAX_PLAYERS; i++)
8592 if (stored_player[i].force_dropping)
8593 murphy_starts_dropping = TRUE;
8595 if (tape.single_step && tape.recording && !tape.pausing)
8596 if (murphy_is_waiting && !murphy_starts_dropping)
8597 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8599 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8602 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8603 int graphic, int sync_frame, int x, int y)
8605 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8607 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8610 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8612 return (IS_NEXT_FRAME(sync_frame, graphic));
8615 int getGraphicInfo_Delay(int graphic)
8617 return graphic_info[graphic].anim_delay;
8620 void PlayMenuSoundExt(int sound)
8622 if (sound == SND_UNDEFINED)
8625 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8626 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8629 if (IS_LOOP_SOUND(sound))
8630 PlaySoundLoop(sound);
8635 void PlayMenuSound()
8637 PlayMenuSoundExt(menu.sound[game_status]);
8640 void PlayMenuSoundStereo(int sound, int stereo_position)
8642 if (sound == SND_UNDEFINED)
8645 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8646 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8649 if (IS_LOOP_SOUND(sound))
8650 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8652 PlaySoundStereo(sound, stereo_position);
8655 void PlayMenuSoundIfLoopExt(int sound)
8657 if (sound == SND_UNDEFINED)
8660 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8661 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8664 if (IS_LOOP_SOUND(sound))
8665 PlaySoundLoop(sound);
8668 void PlayMenuSoundIfLoop()
8670 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8673 void PlayMenuMusicExt(int music)
8675 if (music == MUS_UNDEFINED)
8678 if (!setup.sound_music)
8684 void PlayMenuMusic()
8686 char *curr_music = getCurrentlyPlayingMusicFilename();
8687 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8689 if (!strEqual(curr_music, next_music))
8690 PlayMenuMusicExt(menu.music[game_status]);
8693 void PlayMenuSoundsAndMusic()
8699 static void FadeMenuSounds()
8704 static void FadeMenuMusic()
8706 char *curr_music = getCurrentlyPlayingMusicFilename();
8707 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8709 if (!strEqual(curr_music, next_music))
8713 void FadeMenuSoundsAndMusic()
8719 void PlaySoundActivating()
8722 PlaySound(SND_MENU_ITEM_ACTIVATING);
8726 void PlaySoundSelecting()
8729 PlaySound(SND_MENU_ITEM_SELECTING);
8733 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8735 boolean change_fullscreen = (setup.fullscreen !=
8736 video.fullscreen_enabled);
8737 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8738 setup.window_scaling_percent !=
8739 video.window_scaling_percent);
8741 if (change_window_scaling_percent && video.fullscreen_enabled)
8744 if (!change_window_scaling_percent && !video.fullscreen_available)
8747 #if defined(TARGET_SDL2)
8748 if (change_window_scaling_percent)
8750 SDLSetWindowScaling(setup.window_scaling_percent);
8754 else if (change_fullscreen)
8756 SDLSetWindowFullscreen(setup.fullscreen);
8758 /* set setup value according to successfully changed fullscreen mode */
8759 setup.fullscreen = video.fullscreen_enabled;
8765 if (change_fullscreen ||
8766 change_window_scaling_percent)
8768 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8770 /* save backbuffer content which gets lost when toggling fullscreen mode */
8771 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8773 if (change_window_scaling_percent)
8775 /* keep window mode, but change window scaling */
8776 video.fullscreen_enabled = TRUE; /* force new window scaling */
8779 /* toggle fullscreen */
8780 ChangeVideoModeIfNeeded(setup.fullscreen);
8782 /* set setup value according to successfully changed fullscreen mode */
8783 setup.fullscreen = video.fullscreen_enabled;
8785 /* restore backbuffer content from temporary backbuffer backup bitmap */
8786 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8788 FreeBitmap(tmp_backbuffer);
8790 /* update visible window/screen */
8791 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8795 void JoinRectangles(int *x, int *y, int *width, int *height,
8796 int x2, int y2, int width2, int height2)
8798 // do not join with "off-screen" rectangle
8799 if (x2 == -1 || y2 == -1)
8804 *width = MAX(*width, width2);
8805 *height = MAX(*height, height2);
8808 void SetAnimStatus(int anim_status_new)
8810 if (anim_status_new == GAME_MODE_MAIN)
8811 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
8812 else if (anim_status_new == GAME_MODE_SCORES)
8813 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
8815 global.anim_status_next = anim_status_new;
8817 // directly set screen modes that are entered without fading
8818 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
8819 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
8820 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
8821 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
8822 global.anim_status = global.anim_status_next;
8825 void SetGameStatus(int game_status_new)
8827 if (game_status_new != game_status)
8828 game_status_last_screen = game_status;
8830 game_status = game_status_new;
8832 SetAnimStatus(game_status_new);
8835 void SetFontStatus(int game_status_new)
8837 static int last_game_status = -1;
8839 if (game_status_new != -1)
8841 // set game status for font use after storing last game status
8842 last_game_status = game_status;
8843 game_status = game_status_new;
8847 // reset game status after font use from last stored game status
8848 game_status = last_game_status;
8852 void ResetFontStatus()
8857 void ChangeViewportPropertiesIfNeeded()
8859 int gfx_game_mode = game_status;
8860 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8862 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
8863 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8864 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8865 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8866 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8867 int new_win_xsize = vp_window->width;
8868 int new_win_ysize = vp_window->height;
8869 int border_size = vp_playfield->border_size;
8870 int new_sx = vp_playfield->x + border_size;
8871 int new_sy = vp_playfield->y + border_size;
8872 int new_sxsize = vp_playfield->width - 2 * border_size;
8873 int new_sysize = vp_playfield->height - 2 * border_size;
8874 int new_real_sx = vp_playfield->x;
8875 int new_real_sy = vp_playfield->y;
8876 int new_full_sxsize = vp_playfield->width;
8877 int new_full_sysize = vp_playfield->height;
8878 int new_dx = vp_door_1->x;
8879 int new_dy = vp_door_1->y;
8880 int new_dxsize = vp_door_1->width;
8881 int new_dysize = vp_door_1->height;
8882 int new_vx = vp_door_2->x;
8883 int new_vy = vp_door_2->y;
8884 int new_vxsize = vp_door_2->width;
8885 int new_vysize = vp_door_2->height;
8886 int new_ex = vp_door_3->x;
8887 int new_ey = vp_door_3->y;
8888 int new_exsize = vp_door_3->width;
8889 int new_eysize = vp_door_3->height;
8890 int new_tilesize_var =
8891 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8893 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8894 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8895 int new_scr_fieldx = new_sxsize / tilesize;
8896 int new_scr_fieldy = new_sysize / tilesize;
8897 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8898 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8899 boolean init_gfx_buffers = FALSE;
8900 boolean init_video_buffer = FALSE;
8901 boolean init_gadgets_and_anims = FALSE;
8902 boolean init_em_graphics = FALSE;
8904 if (new_win_xsize != WIN_XSIZE ||
8905 new_win_ysize != WIN_YSIZE)
8907 WIN_XSIZE = new_win_xsize;
8908 WIN_YSIZE = new_win_ysize;
8910 init_video_buffer = TRUE;
8911 init_gfx_buffers = TRUE;
8912 init_gadgets_and_anims = TRUE;
8914 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8917 if (new_scr_fieldx != SCR_FIELDX ||
8918 new_scr_fieldy != SCR_FIELDY)
8920 /* this always toggles between MAIN and GAME when using small tile size */
8922 SCR_FIELDX = new_scr_fieldx;
8923 SCR_FIELDY = new_scr_fieldy;
8925 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8936 new_sxsize != SXSIZE ||
8937 new_sysize != SYSIZE ||
8938 new_dxsize != DXSIZE ||
8939 new_dysize != DYSIZE ||
8940 new_vxsize != VXSIZE ||
8941 new_vysize != VYSIZE ||
8942 new_exsize != EXSIZE ||
8943 new_eysize != EYSIZE ||
8944 new_real_sx != REAL_SX ||
8945 new_real_sy != REAL_SY ||
8946 new_full_sxsize != FULL_SXSIZE ||
8947 new_full_sysize != FULL_SYSIZE ||
8948 new_tilesize_var != TILESIZE_VAR
8951 // ------------------------------------------------------------------------
8952 // determine next fading area for changed viewport definitions
8953 // ------------------------------------------------------------------------
8955 // start with current playfield area (default fading area)
8958 FADE_SXSIZE = FULL_SXSIZE;
8959 FADE_SYSIZE = FULL_SYSIZE;
8961 // add new playfield area if position or size has changed
8962 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8963 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8965 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8966 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8969 // add current and new door 1 area if position or size has changed
8970 if (new_dx != DX || new_dy != DY ||
8971 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8973 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8974 DX, DY, DXSIZE, DYSIZE);
8975 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8976 new_dx, new_dy, new_dxsize, new_dysize);
8979 // add current and new door 2 area if position or size has changed
8980 if (new_dx != VX || new_dy != VY ||
8981 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8983 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8984 VX, VY, VXSIZE, VYSIZE);
8985 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8986 new_vx, new_vy, new_vxsize, new_vysize);
8989 // ------------------------------------------------------------------------
8990 // handle changed tile size
8991 // ------------------------------------------------------------------------
8993 if (new_tilesize_var != TILESIZE_VAR)
8995 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8997 // changing tile size invalidates scroll values of engine snapshots
8998 FreeEngineSnapshotSingle();
9000 // changing tile size requires update of graphic mapping for EM engine
9001 init_em_graphics = TRUE;
9012 SXSIZE = new_sxsize;
9013 SYSIZE = new_sysize;
9014 DXSIZE = new_dxsize;
9015 DYSIZE = new_dysize;
9016 VXSIZE = new_vxsize;
9017 VYSIZE = new_vysize;
9018 EXSIZE = new_exsize;
9019 EYSIZE = new_eysize;
9020 REAL_SX = new_real_sx;
9021 REAL_SY = new_real_sy;
9022 FULL_SXSIZE = new_full_sxsize;
9023 FULL_SYSIZE = new_full_sysize;
9024 TILESIZE_VAR = new_tilesize_var;
9026 init_gfx_buffers = TRUE;
9027 init_gadgets_and_anims = TRUE;
9029 // printf("::: viewports: init_gfx_buffers\n");
9030 // printf("::: viewports: init_gadgets_and_anims\n");
9033 if (init_gfx_buffers)
9035 // printf("::: init_gfx_buffers\n");
9037 SCR_FIELDX = new_scr_fieldx_buffers;
9038 SCR_FIELDY = new_scr_fieldy_buffers;
9042 SCR_FIELDX = new_scr_fieldx;
9043 SCR_FIELDY = new_scr_fieldy;
9045 SetDrawDeactivationMask(REDRAW_NONE);
9046 SetDrawBackgroundMask(REDRAW_FIELD);
9049 if (init_video_buffer)
9051 // printf("::: init_video_buffer\n");
9053 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
9054 InitImageTextures();
9057 if (init_gadgets_and_anims)
9059 // printf("::: init_gadgets_and_anims\n");
9062 InitGlobalAnimations();
9065 if (init_em_graphics)
9067 InitGraphicInfo_EM();