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)
363 int level_xsize = level.native_mm_level->fieldx;
364 int full_xsize = level_xsize * TILESIZE_VAR;
366 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
369 int lx = px / TILESIZE_VAR;
374 static int getLevelFromScreenY_MM(int sy)
376 int level_ysize = level.native_mm_level->fieldy;
377 int full_ysize = level_ysize * TILESIZE_VAR;
379 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
382 int ly = py / TILESIZE_VAR;
387 int getLevelFromScreenX(int x)
389 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
390 return getLevelFromScreenX_EM(x);
391 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
392 return getLevelFromScreenX_SP(x);
393 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
394 return getLevelFromScreenX_MM(x);
396 return getLevelFromScreenX_RND(x);
399 int getLevelFromScreenY(int y)
401 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
402 return getLevelFromScreenY_EM(y);
403 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
404 return getLevelFromScreenY_SP(y);
405 if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
406 return getLevelFromScreenY_MM(y);
408 return getLevelFromScreenY_RND(y);
411 void DumpTile(int x, int y)
416 printf_line("-", 79);
417 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
418 printf_line("-", 79);
420 if (!IN_LEV_FIELD(x, y))
422 printf("(not in level field)\n");
428 printf(" Feld: %d\t['%s']\n", Feld[x][y],
429 element_info[Feld[x][y]].token_name);
430 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
431 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
432 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
433 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
434 printf(" MovPos: %d\n", MovPos[x][y]);
435 printf(" MovDir: %d\n", MovDir[x][y]);
436 printf(" MovDelay: %d\n", MovDelay[x][y]);
437 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
438 printf(" CustomValue: %d\n", CustomValue[x][y]);
439 printf(" GfxElement: %d\n", GfxElement[x][y]);
440 printf(" GfxAction: %d\n", GfxAction[x][y]);
441 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
442 printf(" Player x/y: %d, %d\n", local_player->jx, local_player->jy);
446 void DumpTileFromScreen(int sx, int sy)
448 int lx = getLevelFromScreenX(sx);
449 int ly = getLevelFromScreenY(sy);
454 void SetDrawtoField(int mode)
456 if (mode == DRAW_TO_FIELDBUFFER)
462 BX2 = SCR_FIELDX + 1;
463 BY2 = SCR_FIELDY + 1;
465 drawto_field = fieldbuffer;
467 else /* DRAW_TO_BACKBUFFER */
473 BX2 = SCR_FIELDX - 1;
474 BY2 = SCR_FIELDY - 1;
476 drawto_field = backbuffer;
480 static void RedrawPlayfield_RND()
482 if (game.envelope_active)
485 DrawLevel(REDRAW_ALL);
489 void RedrawPlayfield()
491 if (game_status != GAME_MODE_PLAYING)
494 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
495 RedrawPlayfield_EM(TRUE);
496 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
497 RedrawPlayfield_SP(TRUE);
498 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
499 RedrawPlayfield_MM();
500 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
501 RedrawPlayfield_RND();
503 BlitScreenToBitmap(backbuffer);
505 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
509 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
512 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
513 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
515 if (x == -1 && y == -1)
518 if (draw_target == DRAW_TO_SCREEN)
519 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
521 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
524 static void DrawMaskedBorderExt_FIELD(int draw_target)
526 if (global.border_status >= GAME_MODE_MAIN &&
527 global.border_status <= GAME_MODE_PLAYING &&
528 border.draw_masked[global.border_status])
529 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
533 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
535 // when drawing to backbuffer, never draw border over open doors
536 if (draw_target == DRAW_TO_BACKBUFFER &&
537 (GetDoorState() & DOOR_OPEN_1))
540 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
541 (global.border_status != GAME_MODE_EDITOR ||
542 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
543 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
546 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
548 // when drawing to backbuffer, never draw border over open doors
549 if (draw_target == DRAW_TO_BACKBUFFER &&
550 (GetDoorState() & DOOR_OPEN_2))
553 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
554 global.border_status != GAME_MODE_EDITOR)
555 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
558 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
560 /* currently not available */
563 static void DrawMaskedBorderExt_ALL(int draw_target)
565 DrawMaskedBorderExt_FIELD(draw_target);
566 DrawMaskedBorderExt_DOOR_1(draw_target);
567 DrawMaskedBorderExt_DOOR_2(draw_target);
568 DrawMaskedBorderExt_DOOR_3(draw_target);
571 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
573 /* never draw masked screen borders on borderless screens */
574 if (global.border_status == GAME_MODE_LOADING ||
575 global.border_status == GAME_MODE_TITLE)
578 if (redraw_mask & REDRAW_ALL)
579 DrawMaskedBorderExt_ALL(draw_target);
582 if (redraw_mask & REDRAW_FIELD)
583 DrawMaskedBorderExt_FIELD(draw_target);
584 if (redraw_mask & REDRAW_DOOR_1)
585 DrawMaskedBorderExt_DOOR_1(draw_target);
586 if (redraw_mask & REDRAW_DOOR_2)
587 DrawMaskedBorderExt_DOOR_2(draw_target);
588 if (redraw_mask & REDRAW_DOOR_3)
589 DrawMaskedBorderExt_DOOR_3(draw_target);
593 void DrawMaskedBorder_FIELD()
595 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
598 void DrawMaskedBorder(int redraw_mask)
600 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
603 void DrawMaskedBorderToTarget(int draw_target)
605 if (draw_target == DRAW_TO_BACKBUFFER ||
606 draw_target == DRAW_TO_SCREEN)
608 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
612 int last_border_status = global.border_status;
614 if (draw_target == DRAW_TO_FADE_SOURCE)
616 global.border_status = gfx.fade_border_source_status;
617 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
619 else if (draw_target == DRAW_TO_FADE_TARGET)
621 global.border_status = gfx.fade_border_target_status;
622 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
625 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
627 global.border_status = last_border_status;
628 gfx.masked_border_bitmap_ptr = backbuffer;
632 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
634 int fx = getFieldbufferOffsetX_RND();
635 int fy = getFieldbufferOffsetY_RND();
637 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
640 void BlitScreenToBitmap(Bitmap *target_bitmap)
642 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
643 BlitScreenToBitmap_EM(target_bitmap);
644 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
645 BlitScreenToBitmap_SP(target_bitmap);
646 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
647 BlitScreenToBitmap_MM(target_bitmap);
648 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
649 BlitScreenToBitmap_RND(target_bitmap);
651 redraw_mask |= REDRAW_FIELD;
654 void DrawFramesPerSecond()
657 int font_nr = FONT_TEXT_2;
658 int font_width = getFontWidth(font_nr);
659 int draw_deactivation_mask = GetDrawDeactivationMask();
660 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
662 /* draw FPS with leading space (needed if field buffer deactivated) */
663 sprintf(text, " %04.1f fps", global.frames_per_second);
665 /* override draw deactivation mask (required for invisible warp mode) */
666 SetDrawDeactivationMask(REDRAW_NONE);
668 /* draw opaque FPS if field buffer deactivated, else draw masked FPS */
669 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
670 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
672 /* set draw deactivation mask to previous value */
673 SetDrawDeactivationMask(draw_deactivation_mask);
675 /* force full-screen redraw in this frame */
676 redraw_mask = REDRAW_ALL;
680 static void PrintFrameTimeDebugging()
682 static unsigned int last_counter = 0;
683 unsigned int counter = Counter();
684 int diff_1 = counter - last_counter;
685 int diff_2 = diff_1 - GAME_FRAME_DELAY;
687 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
688 char diff_bar[2 * diff_2_max + 5];
692 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
694 for (i = 0; i < diff_2_max; i++)
695 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
696 i >= diff_2_max - diff_2_cut ? '-' : ' ');
698 diff_bar[pos++] = '|';
700 for (i = 0; i < diff_2_max; i++)
701 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
703 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
705 diff_bar[pos++] = '\0';
707 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
710 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
713 last_counter = counter;
717 static int unifiedRedrawMask(int mask)
719 if (mask & REDRAW_ALL)
722 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
728 static boolean equalRedrawMasks(int mask_1, int mask_2)
730 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
735 static int last_redraw_mask = REDRAW_NONE;
737 // force screen redraw in every frame to continue drawing global animations
738 // (but always use the last redraw mask to prevent unwanted side effects)
739 if (redraw_mask == REDRAW_NONE)
740 redraw_mask = last_redraw_mask;
742 last_redraw_mask = redraw_mask;
745 // masked border now drawn immediately when blitting backbuffer to window
747 // draw masked border to all viewports, if defined
748 DrawMaskedBorder(redraw_mask);
751 // draw frames per second (only if debug mode is enabled)
752 if (redraw_mask & REDRAW_FPS)
753 DrawFramesPerSecond();
755 // remove playfield redraw before potentially merging with doors redraw
756 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
757 redraw_mask &= ~REDRAW_FIELD;
759 // redraw complete window if both playfield and (some) doors need redraw
760 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
761 redraw_mask = REDRAW_ALL;
763 /* although redrawing the whole window would be fine for normal gameplay,
764 being able to only redraw the playfield is required for deactivating
765 certain drawing areas (mainly playfield) to work, which is needed for
766 warp-forward to be fast enough (by skipping redraw of most frames) */
768 if (redraw_mask & REDRAW_ALL)
770 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
772 else if (redraw_mask & REDRAW_FIELD)
774 BlitBitmap(backbuffer, window,
775 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
777 else if (redraw_mask & REDRAW_DOORS)
779 // merge door areas to prevent calling screen redraw more than once
785 if (redraw_mask & REDRAW_DOOR_1)
789 x2 = MAX(x2, DX + DXSIZE);
790 y2 = MAX(y2, DY + DYSIZE);
793 if (redraw_mask & REDRAW_DOOR_2)
797 x2 = MAX(x2, VX + VXSIZE);
798 y2 = MAX(y2, VY + VYSIZE);
801 if (redraw_mask & REDRAW_DOOR_3)
805 x2 = MAX(x2, EX + EXSIZE);
806 y2 = MAX(y2, EY + EYSIZE);
809 // make sure that at least one pixel is blitted, and inside the screen
810 // (else nothing is blitted, causing the animations not to be updated)
811 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
812 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
813 x2 = MIN(MAX(1, x2), WIN_XSIZE);
814 y2 = MIN(MAX(1, y2), WIN_YSIZE);
816 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
819 redraw_mask = REDRAW_NONE;
822 PrintFrameTimeDebugging();
826 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
828 unsigned int frame_delay_value_old = GetVideoFrameDelay();
830 SetVideoFrameDelay(frame_delay_value);
834 SetVideoFrameDelay(frame_delay_value_old);
837 static int fade_type_skip = FADE_TYPE_NONE;
839 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
841 void (*draw_border_function)(void) = NULL;
842 int x, y, width, height;
843 int fade_delay, post_delay;
845 if (fade_type == FADE_TYPE_FADE_OUT)
847 if (fade_type_skip != FADE_TYPE_NONE)
849 /* skip all fade operations until specified fade operation */
850 if (fade_type & fade_type_skip)
851 fade_type_skip = FADE_TYPE_NONE;
856 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
860 redraw_mask |= fade_mask;
862 if (fade_type == FADE_TYPE_SKIP)
864 fade_type_skip = fade_mode;
869 fade_delay = fading.fade_delay;
870 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
872 if (fade_type_skip != FADE_TYPE_NONE)
874 /* skip all fade operations until specified fade operation */
875 if (fade_type & fade_type_skip)
876 fade_type_skip = FADE_TYPE_NONE;
881 if (global.autoplay_leveldir)
886 if (fade_mask == REDRAW_FIELD)
891 height = FADE_SYSIZE;
893 if (border.draw_masked_when_fading)
894 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
896 DrawMaskedBorder_FIELD(); /* draw once */
898 else /* REDRAW_ALL */
906 if (!setup.fade_screens ||
908 fading.fade_mode == FADE_MODE_NONE)
910 if (fade_mode == FADE_MODE_FADE_OUT)
913 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
915 redraw_mask &= ~fade_mask;
920 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
921 draw_border_function);
923 redraw_mask &= ~fade_mask;
926 static void SetScreenStates_BeforeFadingIn()
928 // temporarily set screen mode for animations to screen after fading in
929 global.anim_status = global.anim_status_next;
931 // store backbuffer with all animations that will be started after fading in
932 if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
933 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
935 // set screen mode for animations back to fading
936 global.anim_status = GAME_MODE_PSEUDO_FADING;
939 static void SetScreenStates_AfterFadingIn()
941 // store new source screen (to use correct masked border for fading)
942 gfx.fade_border_source_status = global.border_status;
944 global.anim_status = global.anim_status_next;
947 static void SetScreenStates_BeforeFadingOut()
949 // store new target screen (to use correct masked border for fading)
950 gfx.fade_border_target_status = game_status;
952 // set screen mode for animations to fading
953 global.anim_status = GAME_MODE_PSEUDO_FADING;
955 // store backbuffer with all animations that will be stopped for fading out
956 if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
957 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
960 static void SetScreenStates_AfterFadingOut()
962 global.border_status = game_status;
965 void FadeIn(int fade_mask)
967 SetScreenStates_BeforeFadingIn();
970 DrawMaskedBorder(REDRAW_ALL);
973 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
974 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
976 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
980 FADE_SXSIZE = FULL_SXSIZE;
981 FADE_SYSIZE = FULL_SYSIZE;
983 if (game_status == GAME_MODE_PLAYING &&
984 strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
985 SetOverlayActive(TRUE);
987 SetScreenStates_AfterFadingIn();
989 // force update of global animation status in case of rapid screen changes
990 redraw_mask = REDRAW_ALL;
994 void FadeOut(int fade_mask)
996 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
997 if (!equalRedrawMasks(fade_mask, redraw_mask))
1000 SetScreenStates_BeforeFadingOut();
1002 SetOverlayActive(FALSE);
1005 DrawMaskedBorder(REDRAW_ALL);
1008 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
1009 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
1011 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
1013 SetScreenStates_AfterFadingOut();
1016 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
1018 static struct TitleFadingInfo fading_leave_stored;
1021 fading_leave_stored = fading_leave;
1023 fading = fading_leave_stored;
1026 void FadeSetEnterMenu()
1028 fading = menu.enter_menu;
1030 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1033 void FadeSetLeaveMenu()
1035 fading = menu.leave_menu;
1037 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1040 void FadeSetEnterScreen()
1042 fading = menu.enter_screen[game_status];
1044 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
1047 void FadeSetNextScreen()
1049 fading = menu.next_screen[game_status];
1051 // (do not overwrite fade mode set by FadeSetEnterScreen)
1052 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1055 void FadeSetLeaveScreen()
1057 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
1060 void FadeSetFromType(int type)
1062 if (type & TYPE_ENTER_SCREEN)
1063 FadeSetEnterScreen();
1064 else if (type & TYPE_ENTER)
1066 else if (type & TYPE_LEAVE)
1070 void FadeSetDisabled()
1072 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1074 fading = fading_none;
1077 void FadeSkipNextFadeIn()
1079 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1082 void FadeSkipNextFadeOut()
1084 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1087 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1089 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1091 return (graphic == IMG_UNDEFINED ? NULL :
1092 graphic_info[graphic].bitmap != NULL || redefined ?
1093 graphic_info[graphic].bitmap :
1094 graphic_info[default_graphic].bitmap);
1097 Bitmap *getBackgroundBitmap(int graphic)
1099 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1102 Bitmap *getGlobalBorderBitmap(int graphic)
1104 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1107 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1110 (status == GAME_MODE_MAIN ||
1111 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1112 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1113 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1114 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1117 return getGlobalBorderBitmap(graphic);
1120 void SetWindowBackgroundImageIfDefined(int graphic)
1122 if (graphic_info[graphic].bitmap)
1123 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1126 void SetMainBackgroundImageIfDefined(int graphic)
1128 if (graphic_info[graphic].bitmap)
1129 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1132 void SetDoorBackgroundImageIfDefined(int graphic)
1134 if (graphic_info[graphic].bitmap)
1135 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1138 void SetWindowBackgroundImage(int graphic)
1140 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1143 void SetMainBackgroundImage(int graphic)
1145 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1148 void SetDoorBackgroundImage(int graphic)
1150 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1153 void SetPanelBackground()
1155 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1157 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1158 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1160 SetDoorBackgroundBitmap(bitmap_db_panel);
1163 void DrawBackground(int x, int y, int width, int height)
1165 /* "drawto" might still point to playfield buffer here (hall of fame) */
1166 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1168 if (IN_GFX_FIELD_FULL(x, y))
1169 redraw_mask |= REDRAW_FIELD;
1170 else if (IN_GFX_DOOR_1(x, y))
1171 redraw_mask |= REDRAW_DOOR_1;
1172 else if (IN_GFX_DOOR_2(x, y))
1173 redraw_mask |= REDRAW_DOOR_2;
1174 else if (IN_GFX_DOOR_3(x, y))
1175 redraw_mask |= REDRAW_DOOR_3;
1178 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1180 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1182 if (font->bitmap == NULL)
1185 DrawBackground(x, y, width, height);
1188 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1190 struct GraphicInfo *g = &graphic_info[graphic];
1192 if (g->bitmap == NULL)
1195 DrawBackground(x, y, width, height);
1198 static int game_status_last = -1;
1199 static Bitmap *global_border_bitmap_last = NULL;
1200 static Bitmap *global_border_bitmap = NULL;
1201 static int real_sx_last = -1, real_sy_last = -1;
1202 static int full_sxsize_last = -1, full_sysize_last = -1;
1203 static int dx_last = -1, dy_last = -1;
1204 static int dxsize_last = -1, dysize_last = -1;
1205 static int vx_last = -1, vy_last = -1;
1206 static int vxsize_last = -1, vysize_last = -1;
1208 boolean CheckIfGlobalBorderHasChanged()
1210 // if game status has not changed, global border has not changed either
1211 if (game_status == game_status_last)
1214 // determine and store new global border bitmap for current game status
1215 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1217 return (global_border_bitmap_last != global_border_bitmap);
1220 boolean CheckIfGlobalBorderRedrawIsNeeded()
1222 // if game status has not changed, nothing has to be redrawn
1223 if (game_status == game_status_last)
1226 // redraw if last screen was title screen
1227 if (game_status_last == GAME_MODE_TITLE)
1230 // redraw if global screen border has changed
1231 if (CheckIfGlobalBorderHasChanged())
1234 // redraw if position or size of playfield area has changed
1235 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1236 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1239 // redraw if position or size of door area has changed
1240 if (dx_last != DX || dy_last != DY ||
1241 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1244 // redraw if position or size of tape area has changed
1245 if (vx_last != VX || vy_last != VY ||
1246 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1252 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1255 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1257 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1260 void RedrawGlobalBorder()
1262 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1264 RedrawGlobalBorderFromBitmap(bitmap);
1266 redraw_mask = REDRAW_ALL;
1269 static void RedrawGlobalBorderIfNeeded()
1271 if (game_status == game_status_last)
1274 // copy current draw buffer to later copy back areas that have not changed
1275 if (game_status_last != GAME_MODE_TITLE)
1276 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1278 if (CheckIfGlobalBorderRedrawIsNeeded())
1280 // redraw global screen border (or clear, if defined to be empty)
1281 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1283 // copy previous playfield and door areas, if they are defined on both
1284 // previous and current screen and if they still have the same size
1286 if (real_sx_last != -1 && real_sy_last != -1 &&
1287 REAL_SX != -1 && REAL_SY != -1 &&
1288 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1289 BlitBitmap(bitmap_db_store_1, backbuffer,
1290 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1293 if (dx_last != -1 && dy_last != -1 &&
1294 DX != -1 && DY != -1 &&
1295 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1296 BlitBitmap(bitmap_db_store_1, backbuffer,
1297 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1299 if (vx_last != -1 && vy_last != -1 &&
1300 VX != -1 && VY != -1 &&
1301 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1302 BlitBitmap(bitmap_db_store_1, backbuffer,
1303 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1305 redraw_mask = REDRAW_ALL;
1308 game_status_last = game_status;
1310 global_border_bitmap_last = global_border_bitmap;
1312 real_sx_last = REAL_SX;
1313 real_sy_last = REAL_SY;
1314 full_sxsize_last = FULL_SXSIZE;
1315 full_sysize_last = FULL_SYSIZE;
1318 dxsize_last = DXSIZE;
1319 dysize_last = DYSIZE;
1322 vxsize_last = VXSIZE;
1323 vysize_last = VYSIZE;
1328 RedrawGlobalBorderIfNeeded();
1330 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1331 /* (when entering hall of fame after playing) */
1332 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1334 /* !!! maybe this should be done before clearing the background !!! */
1335 if (game_status == GAME_MODE_PLAYING)
1337 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1338 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1342 SetDrawtoField(DRAW_TO_BACKBUFFER);
1346 void MarkTileDirty(int x, int y)
1348 redraw_mask |= REDRAW_FIELD;
1351 void SetBorderElement()
1355 BorderElement = EL_EMPTY;
1357 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1359 for (x = 0; x < lev_fieldx; x++)
1361 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1362 BorderElement = EL_STEELWALL;
1364 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1370 void FloodFillLevel(int from_x, int from_y, int fill_element,
1371 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1372 int max_fieldx, int max_fieldy)
1376 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1377 static int safety = 0;
1379 /* check if starting field still has the desired content */
1380 if (field[from_x][from_y] == fill_element)
1385 if (safety > max_fieldx * max_fieldy)
1386 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1388 old_element = field[from_x][from_y];
1389 field[from_x][from_y] = fill_element;
1391 for (i = 0; i < 4; i++)
1393 x = from_x + check[i][0];
1394 y = from_y + check[i][1];
1396 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1397 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1403 void SetRandomAnimationValue(int x, int y)
1405 gfx.anim_random_frame = GfxRandom[x][y];
1408 int getGraphicAnimationFrame(int graphic, int sync_frame)
1410 /* animation synchronized with global frame counter, not move position */
1411 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1412 sync_frame = FrameCounter;
1414 return getAnimationFrame(graphic_info[graphic].anim_frames,
1415 graphic_info[graphic].anim_delay,
1416 graphic_info[graphic].anim_mode,
1417 graphic_info[graphic].anim_start_frame,
1421 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1423 struct GraphicInfo *g = &graphic_info[graphic];
1424 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1426 if (tilesize == gfx.standard_tile_size)
1427 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1428 else if (tilesize == game.tile_size)
1429 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1431 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1434 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1435 boolean get_backside)
1437 struct GraphicInfo *g = &graphic_info[graphic];
1438 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1439 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1441 if (g->offset_y == 0) /* frames are ordered horizontally */
1443 int max_width = g->anim_frames_per_line * g->width;
1444 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1446 *x = pos % max_width;
1447 *y = src_y % g->height + pos / max_width * g->height;
1449 else if (g->offset_x == 0) /* frames are ordered vertically */
1451 int max_height = g->anim_frames_per_line * g->height;
1452 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1454 *x = src_x % g->width + pos / max_height * g->width;
1455 *y = pos % max_height;
1457 else /* frames are ordered diagonally */
1459 *x = src_x + frame * g->offset_x;
1460 *y = src_y + frame * g->offset_y;
1464 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1465 Bitmap **bitmap, int *x, int *y,
1466 boolean get_backside)
1468 struct GraphicInfo *g = &graphic_info[graphic];
1470 // if no in-game graphics defined, always use standard graphic size
1471 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1472 tilesize = TILESIZE;
1474 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1475 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1477 *x = *x * tilesize / g->tile_size;
1478 *y = *y * tilesize / g->tile_size;
1481 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1482 Bitmap **bitmap, int *x, int *y)
1484 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1487 void getFixedGraphicSource(int graphic, int frame,
1488 Bitmap **bitmap, int *x, int *y)
1490 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1493 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1495 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1498 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1499 int *x, int *y, boolean get_backside)
1501 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1505 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1507 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1510 void DrawGraphic(int x, int y, int graphic, int frame)
1513 if (!IN_SCR_FIELD(x, y))
1515 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1516 printf("DrawGraphic(): This should never happen!\n");
1521 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1524 MarkTileDirty(x, y);
1527 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1530 if (!IN_SCR_FIELD(x, y))
1532 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1533 printf("DrawGraphic(): This should never happen!\n");
1538 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1540 MarkTileDirty(x, y);
1543 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1549 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1551 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1554 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1560 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1561 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1564 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1567 if (!IN_SCR_FIELD(x, y))
1569 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1570 printf("DrawGraphicThruMask(): This should never happen!\n");
1575 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1578 MarkTileDirty(x, y);
1581 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1584 if (!IN_SCR_FIELD(x, y))
1586 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1587 printf("DrawGraphicThruMask(): This should never happen!\n");
1592 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1594 MarkTileDirty(x, y);
1597 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1603 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1605 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1609 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1610 int graphic, int frame)
1615 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1617 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1621 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1623 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1625 MarkTileDirty(x / tilesize, y / tilesize);
1628 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1634 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1635 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1638 void DrawMiniGraphic(int x, int y, int graphic)
1640 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1641 MarkTileDirty(x / 2, y / 2);
1644 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1649 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1650 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1653 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1654 int graphic, int frame,
1655 int cut_mode, int mask_mode)
1660 int width = TILEX, height = TILEY;
1663 if (dx || dy) /* shifted graphic */
1665 if (x < BX1) /* object enters playfield from the left */
1672 else if (x > BX2) /* object enters playfield from the right */
1678 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1684 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1686 else if (dx) /* general horizontal movement */
1687 MarkTileDirty(x + SIGN(dx), y);
1689 if (y < BY1) /* object enters playfield from the top */
1691 if (cut_mode == CUT_BELOW) /* object completely above top border */
1699 else if (y > BY2) /* object enters playfield from the bottom */
1705 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1711 else if (dy > 0 && cut_mode == CUT_ABOVE)
1713 if (y == BY2) /* object completely above bottom border */
1719 MarkTileDirty(x, y + 1);
1720 } /* object leaves playfield to the bottom */
1721 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1723 else if (dy) /* general vertical movement */
1724 MarkTileDirty(x, y + SIGN(dy));
1728 if (!IN_SCR_FIELD(x, y))
1730 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1731 printf("DrawGraphicShifted(): This should never happen!\n");
1736 width = width * TILESIZE_VAR / TILESIZE;
1737 height = height * TILESIZE_VAR / TILESIZE;
1738 cx = cx * TILESIZE_VAR / TILESIZE;
1739 cy = cy * TILESIZE_VAR / TILESIZE;
1740 dx = dx * TILESIZE_VAR / TILESIZE;
1741 dy = dy * TILESIZE_VAR / TILESIZE;
1743 if (width > 0 && height > 0)
1745 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1750 dst_x = FX + x * TILEX_VAR + dx;
1751 dst_y = FY + y * TILEY_VAR + dy;
1753 if (mask_mode == USE_MASKING)
1754 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1757 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1760 MarkTileDirty(x, y);
1764 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1765 int graphic, int frame,
1766 int cut_mode, int mask_mode)
1771 int width = TILEX_VAR, height = TILEY_VAR;
1774 int x2 = x + SIGN(dx);
1775 int y2 = y + SIGN(dy);
1777 /* movement with two-tile animations must be sync'ed with movement position,
1778 not with current GfxFrame (which can be higher when using slow movement) */
1779 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1780 int anim_frames = graphic_info[graphic].anim_frames;
1782 /* (we also need anim_delay here for movement animations with less frames) */
1783 int anim_delay = graphic_info[graphic].anim_delay;
1784 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1786 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1787 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1789 /* re-calculate animation frame for two-tile movement animation */
1790 frame = getGraphicAnimationFrame(graphic, sync_frame);
1792 /* check if movement start graphic inside screen area and should be drawn */
1793 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1795 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1797 dst_x = FX + x1 * TILEX_VAR;
1798 dst_y = FY + y1 * TILEY_VAR;
1800 if (mask_mode == USE_MASKING)
1801 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1804 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1807 MarkTileDirty(x1, y1);
1810 /* check if movement end graphic inside screen area and should be drawn */
1811 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1813 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1815 dst_x = FX + x2 * TILEX_VAR;
1816 dst_y = FY + y2 * TILEY_VAR;
1818 if (mask_mode == USE_MASKING)
1819 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1822 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1825 MarkTileDirty(x2, y2);
1829 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1830 int graphic, int frame,
1831 int cut_mode, int mask_mode)
1835 DrawGraphic(x, y, graphic, frame);
1840 if (graphic_info[graphic].double_movement) /* EM style movement images */
1841 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1843 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1846 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1847 int frame, int cut_mode)
1849 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1852 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1853 int cut_mode, int mask_mode)
1855 int lx = LEVELX(x), ly = LEVELY(y);
1859 if (IN_LEV_FIELD(lx, ly))
1861 SetRandomAnimationValue(lx, ly);
1863 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1864 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1866 /* do not use double (EM style) movement graphic when not moving */
1867 if (graphic_info[graphic].double_movement && !dx && !dy)
1869 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1870 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1873 else /* border element */
1875 graphic = el2img(element);
1876 frame = getGraphicAnimationFrame(graphic, -1);
1879 if (element == EL_EXPANDABLE_WALL)
1881 boolean left_stopped = FALSE, right_stopped = FALSE;
1883 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1884 left_stopped = TRUE;
1885 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1886 right_stopped = TRUE;
1888 if (left_stopped && right_stopped)
1890 else if (left_stopped)
1892 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1893 frame = graphic_info[graphic].anim_frames - 1;
1895 else if (right_stopped)
1897 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1898 frame = graphic_info[graphic].anim_frames - 1;
1903 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1904 else if (mask_mode == USE_MASKING)
1905 DrawGraphicThruMask(x, y, graphic, frame);
1907 DrawGraphic(x, y, graphic, frame);
1910 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1911 int cut_mode, int mask_mode)
1913 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1914 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1915 cut_mode, mask_mode);
1918 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1921 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1924 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1927 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1930 void DrawLevelElementThruMask(int x, int y, int element)
1932 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1935 void DrawLevelFieldThruMask(int x, int y)
1937 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1940 /* !!! implementation of quicksand is totally broken !!! */
1941 #define IS_CRUMBLED_TILE(x, y, e) \
1942 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1943 !IS_MOVING(x, y) || \
1944 (e) == EL_QUICKSAND_EMPTYING || \
1945 (e) == EL_QUICKSAND_FAST_EMPTYING))
1947 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1952 int width, height, cx, cy;
1953 int sx = SCREENX(x), sy = SCREENY(y);
1954 int crumbled_border_size = graphic_info[graphic].border_size;
1955 int crumbled_tile_size = graphic_info[graphic].tile_size;
1956 int crumbled_border_size_var =
1957 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
1960 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1962 for (i = 1; i < 4; i++)
1964 int dxx = (i & 1 ? dx : 0);
1965 int dyy = (i & 2 ? dy : 0);
1968 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1971 /* check if neighbour field is of same crumble type */
1972 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1973 graphic_info[graphic].class ==
1974 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1976 /* return if check prevents inner corner */
1977 if (same == (dxx == dx && dyy == dy))
1981 /* if we reach this point, we have an inner corner */
1983 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1985 width = crumbled_border_size_var;
1986 height = crumbled_border_size_var;
1987 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1988 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1990 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1991 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1994 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1999 int width, height, bx, by, cx, cy;
2000 int sx = SCREENX(x), sy = SCREENY(y);
2001 int crumbled_border_size = graphic_info[graphic].border_size;
2002 int crumbled_tile_size = graphic_info[graphic].tile_size;
2003 int crumbled_border_size_var =
2004 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
2005 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
2008 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2010 /* draw simple, sloppy, non-corner-accurate crumbled border */
2012 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
2013 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
2014 cx = (dir == 2 ? crumbled_border_pos_var : 0);
2015 cy = (dir == 3 ? crumbled_border_pos_var : 0);
2017 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
2018 FX + sx * TILEX_VAR + cx,
2019 FY + sy * TILEY_VAR + cy);
2021 /* (remaining middle border part must be at least as big as corner part) */
2022 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
2023 crumbled_border_size_var >= TILESIZE_VAR / 3)
2026 /* correct corners of crumbled border, if needed */
2028 for (i = -1; i <= 1; i += 2)
2030 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2031 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2032 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2035 /* check if neighbour field is of same crumble type */
2036 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2037 graphic_info[graphic].class ==
2038 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2040 /* no crumbled corner, but continued crumbled border */
2042 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2043 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2044 int b1 = (i == 1 ? crumbled_border_size_var :
2045 TILESIZE_VAR - 2 * crumbled_border_size_var);
2047 width = crumbled_border_size_var;
2048 height = crumbled_border_size_var;
2050 if (dir == 1 || dir == 2)
2065 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2067 FX + sx * TILEX_VAR + cx,
2068 FY + sy * TILEY_VAR + cy);
2073 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2075 int sx = SCREENX(x), sy = SCREENY(y);
2078 static int xy[4][2] =
2086 if (!IN_LEV_FIELD(x, y))
2089 element = TILE_GFX_ELEMENT(x, y);
2091 if (IS_CRUMBLED_TILE(x, y, element)) /* crumble field itself */
2093 if (!IN_SCR_FIELD(sx, sy))
2096 /* crumble field borders towards direct neighbour fields */
2097 for (i = 0; i < 4; i++)
2099 int xx = x + xy[i][0];
2100 int yy = y + xy[i][1];
2102 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2105 /* check if neighbour field is of same crumble type */
2106 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2107 graphic_info[graphic].class ==
2108 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2111 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2114 /* crumble inner field corners towards corner neighbour fields */
2115 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2116 graphic_info[graphic].anim_frames == 2)
2118 for (i = 0; i < 4; i++)
2120 int dx = (i & 1 ? +1 : -1);
2121 int dy = (i & 2 ? +1 : -1);
2123 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2127 MarkTileDirty(sx, sy);
2129 else /* center field is not crumbled -- crumble neighbour fields */
2131 /* crumble field borders of direct neighbour fields */
2132 for (i = 0; i < 4; i++)
2134 int xx = x + xy[i][0];
2135 int yy = y + xy[i][1];
2136 int sxx = sx + xy[i][0];
2137 int syy = sy + xy[i][1];
2139 if (!IN_LEV_FIELD(xx, yy) ||
2140 !IN_SCR_FIELD(sxx, syy))
2143 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2146 element = TILE_GFX_ELEMENT(xx, yy);
2148 if (!IS_CRUMBLED_TILE(xx, yy, element))
2151 graphic = el_act2crm(element, ACTION_DEFAULT);
2153 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2155 MarkTileDirty(sxx, syy);
2158 /* crumble inner field corners of corner neighbour fields */
2159 for (i = 0; i < 4; i++)
2161 int dx = (i & 1 ? +1 : -1);
2162 int dy = (i & 2 ? +1 : -1);
2168 if (!IN_LEV_FIELD(xx, yy) ||
2169 !IN_SCR_FIELD(sxx, syy))
2172 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2175 element = TILE_GFX_ELEMENT(xx, yy);
2177 if (!IS_CRUMBLED_TILE(xx, yy, element))
2180 graphic = el_act2crm(element, ACTION_DEFAULT);
2182 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2183 graphic_info[graphic].anim_frames == 2)
2184 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2186 MarkTileDirty(sxx, syy);
2191 void DrawLevelFieldCrumbled(int x, int y)
2195 if (!IN_LEV_FIELD(x, y))
2198 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2199 GfxElement[x][y] != EL_UNDEFINED &&
2200 GFX_CRUMBLED(GfxElement[x][y]))
2202 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2207 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2209 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2212 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2215 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2216 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2217 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2218 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2219 int sx = SCREENX(x), sy = SCREENY(y);
2221 DrawGraphic(sx, sy, graphic1, frame1);
2222 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2225 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2227 int sx = SCREENX(x), sy = SCREENY(y);
2228 static int xy[4][2] =
2237 /* crumble direct neighbour fields (required for field borders) */
2238 for (i = 0; i < 4; i++)
2240 int xx = x + xy[i][0];
2241 int yy = y + xy[i][1];
2242 int sxx = sx + xy[i][0];
2243 int syy = sy + xy[i][1];
2245 if (!IN_LEV_FIELD(xx, yy) ||
2246 !IN_SCR_FIELD(sxx, syy) ||
2247 !GFX_CRUMBLED(Feld[xx][yy]) ||
2251 DrawLevelField(xx, yy);
2254 /* crumble corner neighbour fields (required for inner field corners) */
2255 for (i = 0; i < 4; i++)
2257 int dx = (i & 1 ? +1 : -1);
2258 int dy = (i & 2 ? +1 : -1);
2264 if (!IN_LEV_FIELD(xx, yy) ||
2265 !IN_SCR_FIELD(sxx, syy) ||
2266 !GFX_CRUMBLED(Feld[xx][yy]) ||
2270 int element = TILE_GFX_ELEMENT(xx, yy);
2271 int graphic = el_act2crm(element, ACTION_DEFAULT);
2273 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2274 graphic_info[graphic].anim_frames == 2)
2275 DrawLevelField(xx, yy);
2279 static int getBorderElement(int x, int y)
2283 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2284 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2285 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2286 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2287 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2288 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2289 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2291 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2292 int steel_position = (x == -1 && y == -1 ? 0 :
2293 x == lev_fieldx && y == -1 ? 1 :
2294 x == -1 && y == lev_fieldy ? 2 :
2295 x == lev_fieldx && y == lev_fieldy ? 3 :
2296 x == -1 || x == lev_fieldx ? 4 :
2297 y == -1 || y == lev_fieldy ? 5 : 6);
2299 return border[steel_position][steel_type];
2302 void DrawScreenElement(int x, int y, int element)
2304 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2305 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2308 void DrawLevelElement(int x, int y, int element)
2310 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2311 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2314 void DrawScreenField(int x, int y)
2316 int lx = LEVELX(x), ly = LEVELY(y);
2317 int element, content;
2319 if (!IN_LEV_FIELD(lx, ly))
2321 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2324 element = getBorderElement(lx, ly);
2326 DrawScreenElement(x, y, element);
2331 element = Feld[lx][ly];
2332 content = Store[lx][ly];
2334 if (IS_MOVING(lx, ly))
2336 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2337 boolean cut_mode = NO_CUTTING;
2339 if (element == EL_QUICKSAND_EMPTYING ||
2340 element == EL_QUICKSAND_FAST_EMPTYING ||
2341 element == EL_MAGIC_WALL_EMPTYING ||
2342 element == EL_BD_MAGIC_WALL_EMPTYING ||
2343 element == EL_DC_MAGIC_WALL_EMPTYING ||
2344 element == EL_AMOEBA_DROPPING)
2345 cut_mode = CUT_ABOVE;
2346 else if (element == EL_QUICKSAND_FILLING ||
2347 element == EL_QUICKSAND_FAST_FILLING ||
2348 element == EL_MAGIC_WALL_FILLING ||
2349 element == EL_BD_MAGIC_WALL_FILLING ||
2350 element == EL_DC_MAGIC_WALL_FILLING)
2351 cut_mode = CUT_BELOW;
2353 if (cut_mode == CUT_ABOVE)
2354 DrawScreenElement(x, y, element);
2356 DrawScreenElement(x, y, EL_EMPTY);
2359 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2360 else if (cut_mode == NO_CUTTING)
2361 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2364 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2366 if (cut_mode == CUT_BELOW &&
2367 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2368 DrawLevelElement(lx, ly + 1, element);
2371 if (content == EL_ACID)
2373 int dir = MovDir[lx][ly];
2374 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2375 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2377 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2379 // prevent target field from being drawn again (but without masking)
2380 // (this would happen if target field is scanned after moving element)
2381 Stop[newlx][newly] = TRUE;
2384 else if (IS_BLOCKED(lx, ly))
2389 boolean cut_mode = NO_CUTTING;
2390 int element_old, content_old;
2392 Blocked2Moving(lx, ly, &oldx, &oldy);
2395 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2396 MovDir[oldx][oldy] == MV_RIGHT);
2398 element_old = Feld[oldx][oldy];
2399 content_old = Store[oldx][oldy];
2401 if (element_old == EL_QUICKSAND_EMPTYING ||
2402 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2403 element_old == EL_MAGIC_WALL_EMPTYING ||
2404 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2405 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2406 element_old == EL_AMOEBA_DROPPING)
2407 cut_mode = CUT_ABOVE;
2409 DrawScreenElement(x, y, EL_EMPTY);
2412 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2414 else if (cut_mode == NO_CUTTING)
2415 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2418 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2421 else if (IS_DRAWABLE(element))
2422 DrawScreenElement(x, y, element);
2424 DrawScreenElement(x, y, EL_EMPTY);
2427 void DrawLevelField(int x, int y)
2429 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2430 DrawScreenField(SCREENX(x), SCREENY(y));
2431 else if (IS_MOVING(x, y))
2435 Moving2Blocked(x, y, &newx, &newy);
2436 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2437 DrawScreenField(SCREENX(newx), SCREENY(newy));
2439 else if (IS_BLOCKED(x, y))
2443 Blocked2Moving(x, y, &oldx, &oldy);
2444 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2445 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2449 void DrawSizedElement(int x, int y, int element, int tilesize)
2453 graphic = el2edimg(element);
2454 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2457 void DrawMiniElement(int x, int y, int element)
2461 graphic = el2edimg(element);
2462 DrawMiniGraphic(x, y, graphic);
2465 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2468 int x = sx + scroll_x, y = sy + scroll_y;
2470 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2471 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2472 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2473 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2475 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2478 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2480 int x = sx + scroll_x, y = sy + scroll_y;
2482 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2483 DrawMiniElement(sx, sy, EL_EMPTY);
2484 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2485 DrawMiniElement(sx, sy, Feld[x][y]);
2487 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2490 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2491 int x, int y, int xsize, int ysize,
2492 int tile_width, int tile_height)
2496 int dst_x = startx + x * tile_width;
2497 int dst_y = starty + y * tile_height;
2498 int width = graphic_info[graphic].width;
2499 int height = graphic_info[graphic].height;
2500 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2501 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2502 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2503 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2504 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2505 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2506 boolean draw_masked = graphic_info[graphic].draw_masked;
2508 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2510 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2512 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2516 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2517 inner_sx + (x - 1) * tile_width % inner_width);
2518 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2519 inner_sy + (y - 1) * tile_height % inner_height);
2522 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2525 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2529 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2530 int x, int y, int xsize, int ysize, int font_nr)
2532 int font_width = getFontWidth(font_nr);
2533 int font_height = getFontHeight(font_nr);
2535 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2536 font_width, font_height);
2539 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2541 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2542 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2543 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2544 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2545 boolean no_delay = (tape.warp_forward);
2546 unsigned int anim_delay = 0;
2547 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2548 int anim_delay_value = MAX(1, (no_delay ? 0 : frame_delay_value) / 2);
2549 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2550 int font_width = getFontWidth(font_nr);
2551 int font_height = getFontHeight(font_nr);
2552 int max_xsize = level.envelope[envelope_nr].xsize;
2553 int max_ysize = level.envelope[envelope_nr].ysize;
2554 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2555 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2556 int xend = max_xsize;
2557 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2558 int xstep = (xstart < xend ? 1 : 0);
2559 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2561 int end = MAX(xend - xstart, yend - ystart);
2564 for (i = start; i <= end; i++)
2566 int last_frame = end; // last frame of this "for" loop
2567 int x = xstart + i * xstep;
2568 int y = ystart + i * ystep;
2569 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2570 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2571 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2572 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2575 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2577 BlitScreenToBitmap(backbuffer);
2579 SetDrawtoField(DRAW_TO_BACKBUFFER);
2581 for (yy = 0; yy < ysize; yy++)
2582 for (xx = 0; xx < xsize; xx++)
2583 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2585 DrawTextBuffer(sx + font_width, sy + font_height,
2586 level.envelope[envelope_nr].text, font_nr, max_xsize,
2587 xsize - 2, ysize - 2, 0, mask_mode,
2588 level.envelope[envelope_nr].autowrap,
2589 level.envelope[envelope_nr].centered, FALSE);
2591 redraw_mask |= REDRAW_FIELD;
2594 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2598 void ShowEnvelope(int envelope_nr)
2600 int element = EL_ENVELOPE_1 + envelope_nr;
2601 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2602 int sound_opening = element_info[element].sound[ACTION_OPENING];
2603 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2604 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2605 boolean no_delay = (tape.warp_forward);
2606 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2607 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2608 int anim_mode = graphic_info[graphic].anim_mode;
2609 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2610 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2612 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2614 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2616 if (anim_mode == ANIM_DEFAULT)
2617 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2619 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2622 Delay(wait_delay_value);
2624 WaitForEventToContinue();
2626 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2628 if (anim_mode != ANIM_NONE)
2629 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2631 if (anim_mode == ANIM_DEFAULT)
2632 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2634 game.envelope_active = FALSE;
2636 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2638 redraw_mask |= REDRAW_FIELD;
2642 static void setRequestBasePosition(int *x, int *y)
2644 int sx_base, sy_base;
2646 if (request.x != -1)
2647 sx_base = request.x;
2648 else if (request.align == ALIGN_LEFT)
2650 else if (request.align == ALIGN_RIGHT)
2651 sx_base = SX + SXSIZE;
2653 sx_base = SX + SXSIZE / 2;
2655 if (request.y != -1)
2656 sy_base = request.y;
2657 else if (request.valign == VALIGN_TOP)
2659 else if (request.valign == VALIGN_BOTTOM)
2660 sy_base = SY + SYSIZE;
2662 sy_base = SY + SYSIZE / 2;
2668 static void setRequestPositionExt(int *x, int *y, int width, int height,
2669 boolean add_border_size)
2671 int border_size = request.border_size;
2672 int sx_base, sy_base;
2675 setRequestBasePosition(&sx_base, &sy_base);
2677 if (request.align == ALIGN_LEFT)
2679 else if (request.align == ALIGN_RIGHT)
2680 sx = sx_base - width;
2682 sx = sx_base - width / 2;
2684 if (request.valign == VALIGN_TOP)
2686 else if (request.valign == VALIGN_BOTTOM)
2687 sy = sy_base - height;
2689 sy = sy_base - height / 2;
2691 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2692 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2694 if (add_border_size)
2704 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2706 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2709 void DrawEnvelopeRequest(char *text)
2711 char *text_final = text;
2712 char *text_door_style = NULL;
2713 int graphic = IMG_BACKGROUND_REQUEST;
2714 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2715 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2716 int font_nr = FONT_REQUEST;
2717 int font_width = getFontWidth(font_nr);
2718 int font_height = getFontHeight(font_nr);
2719 int border_size = request.border_size;
2720 int line_spacing = request.line_spacing;
2721 int line_height = font_height + line_spacing;
2722 int max_text_width = request.width - 2 * border_size;
2723 int max_text_height = request.height - 2 * border_size;
2724 int line_length = max_text_width / font_width;
2725 int max_lines = max_text_height / line_height;
2726 int text_width = line_length * font_width;
2727 int width = request.width;
2728 int height = request.height;
2729 int tile_size = MAX(request.step_offset, 1);
2730 int x_steps = width / tile_size;
2731 int y_steps = height / tile_size;
2732 int sx_offset = border_size;
2733 int sy_offset = border_size;
2737 if (request.centered)
2738 sx_offset = (request.width - text_width) / 2;
2740 if (request.wrap_single_words && !request.autowrap)
2742 char *src_text_ptr, *dst_text_ptr;
2744 text_door_style = checked_malloc(2 * strlen(text) + 1);
2746 src_text_ptr = text;
2747 dst_text_ptr = text_door_style;
2749 while (*src_text_ptr)
2751 if (*src_text_ptr == ' ' ||
2752 *src_text_ptr == '?' ||
2753 *src_text_ptr == '!')
2754 *dst_text_ptr++ = '\n';
2756 if (*src_text_ptr != ' ')
2757 *dst_text_ptr++ = *src_text_ptr;
2762 *dst_text_ptr = '\0';
2764 text_final = text_door_style;
2767 setRequestPosition(&sx, &sy, FALSE);
2769 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2771 for (y = 0; y < y_steps; y++)
2772 for (x = 0; x < x_steps; x++)
2773 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2774 x, y, x_steps, y_steps,
2775 tile_size, tile_size);
2777 /* force DOOR font inside door area */
2778 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2780 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2781 line_length, -1, max_lines, line_spacing, mask_mode,
2782 request.autowrap, request.centered, FALSE);
2786 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2787 RedrawGadget(tool_gadget[i]);
2789 // store readily prepared envelope request for later use when animating
2790 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2792 if (text_door_style)
2793 free(text_door_style);
2796 void AnimateEnvelopeRequest(int anim_mode, int action)
2798 int graphic = IMG_BACKGROUND_REQUEST;
2799 boolean draw_masked = graphic_info[graphic].draw_masked;
2800 int delay_value_normal = request.step_delay;
2801 int delay_value_fast = delay_value_normal / 2;
2802 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2803 boolean no_delay = (tape.warp_forward);
2804 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2805 int anim_delay_value = MAX(1, (no_delay ? 0 : delay_value + 500 * 0) / 2);
2806 unsigned int anim_delay = 0;
2808 int tile_size = MAX(request.step_offset, 1);
2809 int max_xsize = request.width / tile_size;
2810 int max_ysize = request.height / tile_size;
2811 int max_xsize_inner = max_xsize - 2;
2812 int max_ysize_inner = max_ysize - 2;
2814 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2815 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2816 int xend = max_xsize_inner;
2817 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2818 int xstep = (xstart < xend ? 1 : 0);
2819 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2821 int end = MAX(xend - xstart, yend - ystart);
2824 if (setup.quick_doors)
2831 for (i = start; i <= end; i++)
2833 int last_frame = end; // last frame of this "for" loop
2834 int x = xstart + i * xstep;
2835 int y = ystart + i * ystep;
2836 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2837 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2838 int xsize_size_left = (xsize - 1) * tile_size;
2839 int ysize_size_top = (ysize - 1) * tile_size;
2840 int max_xsize_pos = (max_xsize - 1) * tile_size;
2841 int max_ysize_pos = (max_ysize - 1) * tile_size;
2842 int width = xsize * tile_size;
2843 int height = ysize * tile_size;
2848 setRequestPosition(&src_x, &src_y, FALSE);
2849 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2851 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2853 for (yy = 0; yy < 2; yy++)
2855 for (xx = 0; xx < 2; xx++)
2857 int src_xx = src_x + xx * max_xsize_pos;
2858 int src_yy = src_y + yy * max_ysize_pos;
2859 int dst_xx = dst_x + xx * xsize_size_left;
2860 int dst_yy = dst_y + yy * ysize_size_top;
2861 int xx_size = (xx ? tile_size : xsize_size_left);
2862 int yy_size = (yy ? tile_size : ysize_size_top);
2865 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
2866 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2868 BlitBitmap(bitmap_db_store_2, backbuffer,
2869 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2873 redraw_mask |= REDRAW_FIELD;
2877 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2881 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2883 int graphic = IMG_BACKGROUND_REQUEST;
2884 int sound_opening = SND_REQUEST_OPENING;
2885 int sound_closing = SND_REQUEST_CLOSING;
2886 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2887 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2888 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2889 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2890 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2892 if (game_status == GAME_MODE_PLAYING)
2893 BlitScreenToBitmap(backbuffer);
2895 SetDrawtoField(DRAW_TO_BACKBUFFER);
2897 // SetDrawBackgroundMask(REDRAW_NONE);
2899 if (action == ACTION_OPENING)
2901 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2903 if (req_state & REQ_ASK)
2905 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2906 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2908 else if (req_state & REQ_CONFIRM)
2910 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2912 else if (req_state & REQ_PLAYER)
2914 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2915 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2916 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2917 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2920 DrawEnvelopeRequest(text);
2923 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2925 if (action == ACTION_OPENING)
2927 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2929 if (anim_mode == ANIM_DEFAULT)
2930 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2932 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2936 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2938 if (anim_mode != ANIM_NONE)
2939 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2941 if (anim_mode == ANIM_DEFAULT)
2942 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2945 game.envelope_active = FALSE;
2947 if (action == ACTION_CLOSING)
2948 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2950 // SetDrawBackgroundMask(last_draw_background_mask);
2952 redraw_mask |= REDRAW_FIELD;
2956 if (action == ACTION_CLOSING &&
2957 game_status == GAME_MODE_PLAYING &&
2958 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2959 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2962 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2966 int graphic = el2preimg(element);
2968 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2969 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2972 void DrawLevel(int draw_background_mask)
2976 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2977 SetDrawBackgroundMask(draw_background_mask);
2981 for (x = BX1; x <= BX2; x++)
2982 for (y = BY1; y <= BY2; y++)
2983 DrawScreenField(x, y);
2985 redraw_mask |= REDRAW_FIELD;
2988 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2993 for (x = 0; x < size_x; x++)
2994 for (y = 0; y < size_y; y++)
2995 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2997 redraw_mask |= REDRAW_FIELD;
3000 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
3004 for (x = 0; x < size_x; x++)
3005 for (y = 0; y < size_y; y++)
3006 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
3008 redraw_mask |= REDRAW_FIELD;
3011 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
3013 boolean show_level_border = (BorderElement != EL_EMPTY);
3014 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3015 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3016 int tile_size = preview.tile_size;
3017 int preview_width = preview.xsize * tile_size;
3018 int preview_height = preview.ysize * tile_size;
3019 int real_preview_xsize = MIN(level_xsize, preview.xsize);
3020 int real_preview_ysize = MIN(level_ysize, preview.ysize);
3021 int real_preview_width = real_preview_xsize * tile_size;
3022 int real_preview_height = real_preview_ysize * tile_size;
3023 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
3024 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3027 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3030 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3032 dst_x += (preview_width - real_preview_width) / 2;
3033 dst_y += (preview_height - real_preview_height) / 2;
3035 for (x = 0; x < real_preview_xsize; x++)
3037 for (y = 0; y < real_preview_ysize; y++)
3039 int lx = from_x + x + (show_level_border ? -1 : 0);
3040 int ly = from_y + y + (show_level_border ? -1 : 0);
3041 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3042 getBorderElement(lx, ly));
3044 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3045 element, tile_size);
3049 redraw_mask |= REDRAW_FIELD;
3052 #define MICROLABEL_EMPTY 0
3053 #define MICROLABEL_LEVEL_NAME 1
3054 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3055 #define MICROLABEL_LEVEL_AUTHOR 3
3056 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3057 #define MICROLABEL_IMPORTED_FROM 5
3058 #define MICROLABEL_IMPORTED_BY_HEAD 6
3059 #define MICROLABEL_IMPORTED_BY 7
3061 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3063 int max_text_width = SXSIZE;
3064 int font_width = getFontWidth(font_nr);
3066 if (pos->align == ALIGN_CENTER)
3067 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3068 else if (pos->align == ALIGN_RIGHT)
3069 max_text_width = pos->x;
3071 max_text_width = SXSIZE - pos->x;
3073 return max_text_width / font_width;
3076 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3078 char label_text[MAX_OUTPUT_LINESIZE + 1];
3079 int max_len_label_text;
3080 int font_nr = pos->font;
3083 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3086 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3087 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3088 mode == MICROLABEL_IMPORTED_BY_HEAD)
3089 font_nr = pos->font_alt;
3091 max_len_label_text = getMaxTextLength(pos, font_nr);
3093 if (pos->size != -1)
3094 max_len_label_text = pos->size;
3096 for (i = 0; i < max_len_label_text; i++)
3097 label_text[i] = ' ';
3098 label_text[max_len_label_text] = '\0';
3100 if (strlen(label_text) > 0)
3101 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3104 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3105 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3106 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3107 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3108 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3109 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3110 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3111 max_len_label_text);
3112 label_text[max_len_label_text] = '\0';
3114 if (strlen(label_text) > 0)
3115 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3117 redraw_mask |= REDRAW_FIELD;
3120 static void DrawPreviewLevelLabel(int mode)
3122 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3125 static void DrawPreviewLevelInfo(int mode)
3127 if (mode == MICROLABEL_LEVEL_NAME)
3128 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3129 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3130 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3133 static void DrawPreviewLevelExt(boolean restart)
3135 static unsigned int scroll_delay = 0;
3136 static unsigned int label_delay = 0;
3137 static int from_x, from_y, scroll_direction;
3138 static int label_state, label_counter;
3139 unsigned int scroll_delay_value = preview.step_delay;
3140 boolean show_level_border = (BorderElement != EL_EMPTY);
3141 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3142 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3149 if (preview.anim_mode == ANIM_CENTERED)
3151 if (level_xsize > preview.xsize)
3152 from_x = (level_xsize - preview.xsize) / 2;
3153 if (level_ysize > preview.ysize)
3154 from_y = (level_ysize - preview.ysize) / 2;
3157 from_x += preview.xoffset;
3158 from_y += preview.yoffset;
3160 scroll_direction = MV_RIGHT;
3164 DrawPreviewLevelPlayfield(from_x, from_y);
3165 DrawPreviewLevelLabel(label_state);
3167 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3168 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3170 /* initialize delay counters */
3171 DelayReached(&scroll_delay, 0);
3172 DelayReached(&label_delay, 0);
3174 if (leveldir_current->name)
3176 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3177 char label_text[MAX_OUTPUT_LINESIZE + 1];
3178 int font_nr = pos->font;
3179 int max_len_label_text = getMaxTextLength(pos, font_nr);
3181 if (pos->size != -1)
3182 max_len_label_text = pos->size;
3184 strncpy(label_text, leveldir_current->name, max_len_label_text);
3185 label_text[max_len_label_text] = '\0';
3187 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3188 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3194 /* scroll preview level, if needed */
3195 if (preview.anim_mode != ANIM_NONE &&
3196 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3197 DelayReached(&scroll_delay, scroll_delay_value))
3199 switch (scroll_direction)
3204 from_x -= preview.step_offset;
3205 from_x = (from_x < 0 ? 0 : from_x);
3208 scroll_direction = MV_UP;
3212 if (from_x < level_xsize - preview.xsize)
3214 from_x += preview.step_offset;
3215 from_x = (from_x > level_xsize - preview.xsize ?
3216 level_xsize - preview.xsize : from_x);
3219 scroll_direction = MV_DOWN;
3225 from_y -= preview.step_offset;
3226 from_y = (from_y < 0 ? 0 : from_y);
3229 scroll_direction = MV_RIGHT;
3233 if (from_y < level_ysize - preview.ysize)
3235 from_y += preview.step_offset;
3236 from_y = (from_y > level_ysize - preview.ysize ?
3237 level_ysize - preview.ysize : from_y);
3240 scroll_direction = MV_LEFT;
3247 DrawPreviewLevelPlayfield(from_x, from_y);
3250 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
3251 /* redraw micro level label, if needed */
3252 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3253 !strEqual(level.author, ANONYMOUS_NAME) &&
3254 !strEqual(level.author, leveldir_current->name) &&
3255 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3257 int max_label_counter = 23;
3259 if (leveldir_current->imported_from != NULL &&
3260 strlen(leveldir_current->imported_from) > 0)
3261 max_label_counter += 14;
3262 if (leveldir_current->imported_by != NULL &&
3263 strlen(leveldir_current->imported_by) > 0)
3264 max_label_counter += 14;
3266 label_counter = (label_counter + 1) % max_label_counter;
3267 label_state = (label_counter >= 0 && label_counter <= 7 ?
3268 MICROLABEL_LEVEL_NAME :
3269 label_counter >= 9 && label_counter <= 12 ?
3270 MICROLABEL_LEVEL_AUTHOR_HEAD :
3271 label_counter >= 14 && label_counter <= 21 ?
3272 MICROLABEL_LEVEL_AUTHOR :
3273 label_counter >= 23 && label_counter <= 26 ?
3274 MICROLABEL_IMPORTED_FROM_HEAD :
3275 label_counter >= 28 && label_counter <= 35 ?
3276 MICROLABEL_IMPORTED_FROM :
3277 label_counter >= 37 && label_counter <= 40 ?
3278 MICROLABEL_IMPORTED_BY_HEAD :
3279 label_counter >= 42 && label_counter <= 49 ?
3280 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3282 if (leveldir_current->imported_from == NULL &&
3283 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3284 label_state == MICROLABEL_IMPORTED_FROM))
3285 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3286 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3288 DrawPreviewLevelLabel(label_state);
3292 void DrawPreviewLevelInitial()
3294 DrawPreviewLevelExt(TRUE);
3297 void DrawPreviewLevelAnimation()
3299 DrawPreviewLevelExt(FALSE);
3302 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3303 int graphic, int sync_frame,
3306 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3308 if (mask_mode == USE_MASKING)
3309 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3311 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3314 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3315 int graphic, int sync_frame, int mask_mode)
3317 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3319 if (mask_mode == USE_MASKING)
3320 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3322 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3325 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3327 int lx = LEVELX(x), ly = LEVELY(y);
3329 if (!IN_SCR_FIELD(x, y))
3332 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3333 graphic, GfxFrame[lx][ly], NO_MASKING);
3335 MarkTileDirty(x, y);
3338 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3340 int lx = LEVELX(x), ly = LEVELY(y);
3342 if (!IN_SCR_FIELD(x, y))
3345 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3346 graphic, GfxFrame[lx][ly], NO_MASKING);
3347 MarkTileDirty(x, y);
3350 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3352 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3355 void DrawLevelElementAnimation(int x, int y, int element)
3357 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3359 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3362 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3364 int sx = SCREENX(x), sy = SCREENY(y);
3366 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3369 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3372 DrawGraphicAnimation(sx, sy, graphic);
3375 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3376 DrawLevelFieldCrumbled(x, y);
3378 if (GFX_CRUMBLED(Feld[x][y]))
3379 DrawLevelFieldCrumbled(x, y);
3383 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3385 int sx = SCREENX(x), sy = SCREENY(y);
3388 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3391 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3393 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3396 DrawGraphicAnimation(sx, sy, graphic);
3398 if (GFX_CRUMBLED(element))
3399 DrawLevelFieldCrumbled(x, y);
3402 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3404 if (player->use_murphy)
3406 /* this works only because currently only one player can be "murphy" ... */
3407 static int last_horizontal_dir = MV_LEFT;
3408 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3410 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3411 last_horizontal_dir = move_dir;
3413 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3415 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3417 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3423 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3426 static boolean equalGraphics(int graphic1, int graphic2)
3428 struct GraphicInfo *g1 = &graphic_info[graphic1];
3429 struct GraphicInfo *g2 = &graphic_info[graphic2];
3431 return (g1->bitmap == g2->bitmap &&
3432 g1->src_x == g2->src_x &&
3433 g1->src_y == g2->src_y &&
3434 g1->anim_frames == g2->anim_frames &&
3435 g1->anim_delay == g2->anim_delay &&
3436 g1->anim_mode == g2->anim_mode);
3439 void DrawAllPlayers()
3443 for (i = 0; i < MAX_PLAYERS; i++)
3444 if (stored_player[i].active)
3445 DrawPlayer(&stored_player[i]);
3448 void DrawPlayerField(int x, int y)
3450 if (!IS_PLAYER(x, y))
3453 DrawPlayer(PLAYERINFO(x, y));
3456 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3458 void DrawPlayer(struct PlayerInfo *player)
3460 int jx = player->jx;
3461 int jy = player->jy;
3462 int move_dir = player->MovDir;
3463 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3464 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3465 int last_jx = (player->is_moving ? jx - dx : jx);
3466 int last_jy = (player->is_moving ? jy - dy : jy);
3467 int next_jx = jx + dx;
3468 int next_jy = jy + dy;
3469 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3470 boolean player_is_opaque = FALSE;
3471 int sx = SCREENX(jx), sy = SCREENY(jy);
3472 int sxx = 0, syy = 0;
3473 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3475 int action = ACTION_DEFAULT;
3476 int last_player_graphic = getPlayerGraphic(player, move_dir);
3477 int last_player_frame = player->Frame;
3480 /* GfxElement[][] is set to the element the player is digging or collecting;
3481 remove also for off-screen player if the player is not moving anymore */
3482 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3483 GfxElement[jx][jy] = EL_UNDEFINED;
3485 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3489 if (!IN_LEV_FIELD(jx, jy))
3491 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3492 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3493 printf("DrawPlayerField(): This should never happen!\n");
3498 if (element == EL_EXPLOSION)
3501 action = (player->is_pushing ? ACTION_PUSHING :
3502 player->is_digging ? ACTION_DIGGING :
3503 player->is_collecting ? ACTION_COLLECTING :
3504 player->is_moving ? ACTION_MOVING :
3505 player->is_snapping ? ACTION_SNAPPING :
3506 player->is_dropping ? ACTION_DROPPING :
3507 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3509 if (player->is_waiting)
3510 move_dir = player->dir_waiting;
3512 InitPlayerGfxAnimation(player, action, move_dir);
3514 /* ----------------------------------------------------------------------- */
3515 /* draw things in the field the player is leaving, if needed */
3516 /* ----------------------------------------------------------------------- */
3518 if (player->is_moving)
3520 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3522 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3524 if (last_element == EL_DYNAMITE_ACTIVE ||
3525 last_element == EL_EM_DYNAMITE_ACTIVE ||
3526 last_element == EL_SP_DISK_RED_ACTIVE)
3527 DrawDynamite(last_jx, last_jy);
3529 DrawLevelFieldThruMask(last_jx, last_jy);
3531 else if (last_element == EL_DYNAMITE_ACTIVE ||
3532 last_element == EL_EM_DYNAMITE_ACTIVE ||
3533 last_element == EL_SP_DISK_RED_ACTIVE)
3534 DrawDynamite(last_jx, last_jy);
3536 /* !!! this is not enough to prevent flickering of players which are
3537 moving next to each others without a free tile between them -- this
3538 can only be solved by drawing all players layer by layer (first the
3539 background, then the foreground etc.) !!! => TODO */
3540 else if (!IS_PLAYER(last_jx, last_jy))
3541 DrawLevelField(last_jx, last_jy);
3544 DrawLevelField(last_jx, last_jy);
3547 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3548 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3551 if (!IN_SCR_FIELD(sx, sy))
3554 /* ----------------------------------------------------------------------- */
3555 /* draw things behind the player, if needed */
3556 /* ----------------------------------------------------------------------- */
3559 DrawLevelElement(jx, jy, Back[jx][jy]);
3560 else if (IS_ACTIVE_BOMB(element))
3561 DrawLevelElement(jx, jy, EL_EMPTY);
3564 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3566 int old_element = GfxElement[jx][jy];
3567 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3568 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3570 if (GFX_CRUMBLED(old_element))
3571 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3573 DrawGraphic(sx, sy, old_graphic, frame);
3575 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3576 player_is_opaque = TRUE;
3580 GfxElement[jx][jy] = EL_UNDEFINED;
3582 /* make sure that pushed elements are drawn with correct frame rate */
3583 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3585 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3586 GfxFrame[jx][jy] = player->StepFrame;
3588 DrawLevelField(jx, jy);
3592 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3593 /* ----------------------------------------------------------------------- */
3594 /* draw player himself */
3595 /* ----------------------------------------------------------------------- */
3597 graphic = getPlayerGraphic(player, move_dir);
3599 /* in the case of changed player action or direction, prevent the current
3600 animation frame from being restarted for identical animations */
3601 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3602 player->Frame = last_player_frame;
3604 frame = getGraphicAnimationFrame(graphic, player->Frame);
3608 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3609 sxx = player->GfxPos;
3611 syy = player->GfxPos;
3614 if (player_is_opaque)
3615 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3617 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3619 if (SHIELD_ON(player))
3621 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3622 IMG_SHIELD_NORMAL_ACTIVE);
3623 int frame = getGraphicAnimationFrame(graphic, -1);
3625 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3629 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3632 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3633 sxx = player->GfxPos;
3635 syy = player->GfxPos;
3639 /* ----------------------------------------------------------------------- */
3640 /* draw things the player is pushing, if needed */
3641 /* ----------------------------------------------------------------------- */
3643 if (player->is_pushing && player->is_moving)
3645 int px = SCREENX(jx), py = SCREENY(jy);
3646 int pxx = (TILEX - ABS(sxx)) * dx;
3647 int pyy = (TILEY - ABS(syy)) * dy;
3648 int gfx_frame = GfxFrame[jx][jy];
3654 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3656 element = Feld[next_jx][next_jy];
3657 gfx_frame = GfxFrame[next_jx][next_jy];
3660 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3662 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3663 frame = getGraphicAnimationFrame(graphic, sync_frame);
3665 /* draw background element under pushed element (like the Sokoban field) */
3666 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3668 /* this allows transparent pushing animation over non-black background */
3671 DrawLevelElement(jx, jy, Back[jx][jy]);
3673 DrawLevelElement(jx, jy, EL_EMPTY);
3675 if (Back[next_jx][next_jy])
3676 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3678 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3680 else if (Back[next_jx][next_jy])
3681 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3684 /* do not draw (EM style) pushing animation when pushing is finished */
3685 /* (two-tile animations usually do not contain start and end frame) */
3686 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3687 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3689 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3691 /* masked drawing is needed for EMC style (double) movement graphics */
3692 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3693 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3697 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3698 /* ----------------------------------------------------------------------- */
3699 /* draw player himself */
3700 /* ----------------------------------------------------------------------- */
3702 graphic = getPlayerGraphic(player, move_dir);
3704 /* in the case of changed player action or direction, prevent the current
3705 animation frame from being restarted for identical animations */
3706 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3707 player->Frame = last_player_frame;
3709 frame = getGraphicAnimationFrame(graphic, player->Frame);
3713 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3714 sxx = player->GfxPos;
3716 syy = player->GfxPos;
3719 if (player_is_opaque)
3720 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3722 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3724 if (SHIELD_ON(player))
3726 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3727 IMG_SHIELD_NORMAL_ACTIVE);
3728 int frame = getGraphicAnimationFrame(graphic, -1);
3730 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3734 /* ----------------------------------------------------------------------- */
3735 /* draw things in front of player (active dynamite or dynabombs) */
3736 /* ----------------------------------------------------------------------- */
3738 if (IS_ACTIVE_BOMB(element))
3740 graphic = el2img(element);
3741 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3743 if (game.emulation == EMU_SUPAPLEX)
3744 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3746 DrawGraphicThruMask(sx, sy, graphic, frame);
3749 if (player_is_moving && last_element == EL_EXPLOSION)
3751 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3752 GfxElement[last_jx][last_jy] : EL_EMPTY);
3753 int graphic = el_act2img(element, ACTION_EXPLODING);
3754 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3755 int phase = ExplodePhase[last_jx][last_jy] - 1;
3756 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3759 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3762 /* ----------------------------------------------------------------------- */
3763 /* draw elements the player is just walking/passing through/under */
3764 /* ----------------------------------------------------------------------- */
3766 if (player_is_moving)
3768 /* handle the field the player is leaving ... */
3769 if (IS_ACCESSIBLE_INSIDE(last_element))
3770 DrawLevelField(last_jx, last_jy);
3771 else if (IS_ACCESSIBLE_UNDER(last_element))
3772 DrawLevelFieldThruMask(last_jx, last_jy);
3775 /* do not redraw accessible elements if the player is just pushing them */
3776 if (!player_is_moving || !player->is_pushing)
3778 /* ... and the field the player is entering */
3779 if (IS_ACCESSIBLE_INSIDE(element))
3780 DrawLevelField(jx, jy);
3781 else if (IS_ACCESSIBLE_UNDER(element))
3782 DrawLevelFieldThruMask(jx, jy);
3785 MarkTileDirty(sx, sy);
3788 /* ------------------------------------------------------------------------- */
3790 void WaitForEventToContinue()
3792 boolean still_wait = TRUE;
3794 if (program.headless)
3797 /* simulate releasing mouse button over last gadget, if still pressed */
3799 HandleGadgets(-1, -1, 0);
3801 button_status = MB_RELEASED;
3809 if (NextValidEvent(&event))
3813 case EVENT_BUTTONPRESS:
3814 case EVENT_KEYPRESS:
3815 #if defined(TARGET_SDL2)
3816 case SDL_CONTROLLERBUTTONDOWN:
3818 case SDL_JOYBUTTONDOWN:
3822 case EVENT_KEYRELEASE:
3823 ClearPlayerAction();
3827 HandleOtherEvents(&event);
3831 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3840 #define MAX_REQUEST_LINES 13
3841 #define MAX_REQUEST_LINE_FONT1_LEN 7
3842 #define MAX_REQUEST_LINE_FONT2_LEN 10
3844 static int RequestHandleEvents(unsigned int req_state)
3846 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3847 local_player->LevelSolved_GameEnd);
3848 int width = request.width;
3849 int height = request.height;
3853 setRequestPosition(&sx, &sy, FALSE);
3855 button_status = MB_RELEASED;
3857 request_gadget_id = -1;
3864 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3866 HandleGameActions();
3868 SetDrawtoField(DRAW_TO_BACKBUFFER);
3870 if (global.use_envelope_request)
3872 /* copy current state of request area to middle of playfield area */
3873 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
3881 while (NextValidEvent(&event))
3885 case EVENT_BUTTONPRESS:
3886 case EVENT_BUTTONRELEASE:
3887 case EVENT_MOTIONNOTIFY:
3891 if (event.type == EVENT_MOTIONNOTIFY)
3896 motion_status = TRUE;
3897 mx = ((MotionEvent *) &event)->x;
3898 my = ((MotionEvent *) &event)->y;
3902 motion_status = FALSE;
3903 mx = ((ButtonEvent *) &event)->x;
3904 my = ((ButtonEvent *) &event)->y;
3905 if (event.type == EVENT_BUTTONPRESS)
3906 button_status = ((ButtonEvent *) &event)->button;
3908 button_status = MB_RELEASED;
3911 /* this sets 'request_gadget_id' */
3912 HandleGadgets(mx, my, button_status);
3914 switch (request_gadget_id)
3916 case TOOL_CTRL_ID_YES:
3919 case TOOL_CTRL_ID_NO:
3922 case TOOL_CTRL_ID_CONFIRM:
3923 result = TRUE | FALSE;
3926 case TOOL_CTRL_ID_PLAYER_1:
3929 case TOOL_CTRL_ID_PLAYER_2:
3932 case TOOL_CTRL_ID_PLAYER_3:
3935 case TOOL_CTRL_ID_PLAYER_4:
3946 #if defined(TARGET_SDL2)
3947 case SDL_WINDOWEVENT:
3948 HandleWindowEvent((WindowEvent *) &event);
3951 case SDL_APP_WILLENTERBACKGROUND:
3952 case SDL_APP_DIDENTERBACKGROUND:
3953 case SDL_APP_WILLENTERFOREGROUND:
3954 case SDL_APP_DIDENTERFOREGROUND:
3955 HandlePauseResumeEvent((PauseResumeEvent *) &event);
3959 case EVENT_KEYPRESS:
3961 Key key = GetEventKey((KeyEvent *)&event, TRUE);
3966 if (req_state & REQ_CONFIRM)
3971 #if defined(TARGET_SDL2)
3974 #if defined(KSYM_Rewind)
3975 case KSYM_Rewind: /* for Amazon Fire TV remote */
3982 #if defined(TARGET_SDL2)
3984 #if defined(KSYM_FastForward)
3985 case KSYM_FastForward: /* for Amazon Fire TV remote */
3992 HandleKeysDebug(key);
3996 if (req_state & REQ_PLAYER)
4002 case EVENT_KEYRELEASE:
4003 ClearPlayerAction();
4006 #if defined(TARGET_SDL2)
4007 case SDL_CONTROLLERBUTTONDOWN:
4008 switch (event.cbutton.button)
4010 case SDL_CONTROLLER_BUTTON_A:
4011 case SDL_CONTROLLER_BUTTON_X:
4012 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
4016 case SDL_CONTROLLER_BUTTON_B:
4017 case SDL_CONTROLLER_BUTTON_Y:
4018 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
4019 case SDL_CONTROLLER_BUTTON_BACK:
4024 if (req_state & REQ_PLAYER)
4029 case SDL_CONTROLLERBUTTONUP:
4030 HandleJoystickEvent(&event);
4031 ClearPlayerAction();
4036 HandleOtherEvents(&event);
4041 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4043 int joy = AnyJoystick();
4045 if (joy & JOY_BUTTON_1)
4047 else if (joy & JOY_BUTTON_2)
4053 if (global.use_envelope_request)
4055 /* copy back current state of pressed buttons inside request area */
4056 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4066 static boolean RequestDoor(char *text, unsigned int req_state)
4068 unsigned int old_door_state;
4069 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4070 int font_nr = FONT_TEXT_2;
4075 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4077 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4078 font_nr = FONT_TEXT_1;
4081 if (game_status == GAME_MODE_PLAYING)
4082 BlitScreenToBitmap(backbuffer);
4084 /* disable deactivated drawing when quick-loading level tape recording */
4085 if (tape.playing && tape.deactivate_display)
4086 TapeDeactivateDisplayOff(TRUE);
4088 SetMouseCursor(CURSOR_DEFAULT);
4090 #if defined(NETWORK_AVALIABLE)
4091 /* pause network game while waiting for request to answer */
4092 if (options.network &&
4093 game_status == GAME_MODE_PLAYING &&
4094 req_state & REQUEST_WAIT_FOR_INPUT)
4095 SendToServer_PausePlaying();
4098 old_door_state = GetDoorState();
4100 /* simulate releasing mouse button over last gadget, if still pressed */
4102 HandleGadgets(-1, -1, 0);
4106 /* draw released gadget before proceeding */
4109 if (old_door_state & DOOR_OPEN_1)
4111 CloseDoor(DOOR_CLOSE_1);
4113 /* save old door content */
4114 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4115 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4118 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4119 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4121 /* clear door drawing field */
4122 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4124 /* force DOOR font inside door area */
4125 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4127 /* write text for request */
4128 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4130 char text_line[max_request_line_len + 1];
4136 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4138 tc = *(text_ptr + tx);
4139 // if (!tc || tc == ' ')
4140 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4144 if ((tc == '?' || tc == '!') && tl == 0)
4154 strncpy(text_line, text_ptr, tl);
4157 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4158 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4159 text_line, font_nr);
4161 text_ptr += tl + (tc == ' ' ? 1 : 0);
4162 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4167 if (req_state & REQ_ASK)
4169 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4170 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4172 else if (req_state & REQ_CONFIRM)
4174 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4176 else if (req_state & REQ_PLAYER)
4178 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4179 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4180 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4181 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4184 /* copy request gadgets to door backbuffer */
4185 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4187 OpenDoor(DOOR_OPEN_1);
4189 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4191 if (game_status == GAME_MODE_PLAYING)
4193 SetPanelBackground();
4194 SetDrawBackgroundMask(REDRAW_DOOR_1);
4198 SetDrawBackgroundMask(REDRAW_FIELD);
4204 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4206 // ---------- handle request buttons ----------
4207 result = RequestHandleEvents(req_state);
4211 if (!(req_state & REQ_STAY_OPEN))
4213 CloseDoor(DOOR_CLOSE_1);
4215 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4216 (req_state & REQ_REOPEN))
4217 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4222 if (game_status == GAME_MODE_PLAYING)
4224 SetPanelBackground();
4225 SetDrawBackgroundMask(REDRAW_DOOR_1);
4229 SetDrawBackgroundMask(REDRAW_FIELD);
4232 #if defined(NETWORK_AVALIABLE)
4233 /* continue network game after request */
4234 if (options.network &&
4235 game_status == GAME_MODE_PLAYING &&
4236 req_state & REQUEST_WAIT_FOR_INPUT)
4237 SendToServer_ContinuePlaying();
4240 /* restore deactivated drawing when quick-loading level tape recording */
4241 if (tape.playing && tape.deactivate_display)
4242 TapeDeactivateDisplayOn();
4247 static boolean RequestEnvelope(char *text, unsigned int req_state)
4251 if (game_status == GAME_MODE_PLAYING)
4252 BlitScreenToBitmap(backbuffer);
4254 /* disable deactivated drawing when quick-loading level tape recording */
4255 if (tape.playing && tape.deactivate_display)
4256 TapeDeactivateDisplayOff(TRUE);
4258 SetMouseCursor(CURSOR_DEFAULT);
4260 #if defined(NETWORK_AVALIABLE)
4261 /* pause network game while waiting for request to answer */
4262 if (options.network &&
4263 game_status == GAME_MODE_PLAYING &&
4264 req_state & REQUEST_WAIT_FOR_INPUT)
4265 SendToServer_PausePlaying();
4268 /* simulate releasing mouse button over last gadget, if still pressed */
4270 HandleGadgets(-1, -1, 0);
4274 // (replace with setting corresponding request background)
4275 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4276 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4278 /* clear door drawing field */
4279 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4281 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4283 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4285 if (game_status == GAME_MODE_PLAYING)
4287 SetPanelBackground();
4288 SetDrawBackgroundMask(REDRAW_DOOR_1);
4292 SetDrawBackgroundMask(REDRAW_FIELD);
4298 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4300 // ---------- handle request buttons ----------
4301 result = RequestHandleEvents(req_state);
4305 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4309 if (game_status == GAME_MODE_PLAYING)
4311 SetPanelBackground();
4312 SetDrawBackgroundMask(REDRAW_DOOR_1);
4316 SetDrawBackgroundMask(REDRAW_FIELD);
4319 #if defined(NETWORK_AVALIABLE)
4320 /* continue network game after request */
4321 if (options.network &&
4322 game_status == GAME_MODE_PLAYING &&
4323 req_state & REQUEST_WAIT_FOR_INPUT)
4324 SendToServer_ContinuePlaying();
4327 /* restore deactivated drawing when quick-loading level tape recording */
4328 if (tape.playing && tape.deactivate_display)
4329 TapeDeactivateDisplayOn();
4334 boolean Request(char *text, unsigned int req_state)
4336 boolean overlay_active = GetOverlayActive();
4339 SetOverlayActive(FALSE);
4341 if (global.use_envelope_request)
4342 result = RequestEnvelope(text, req_state);
4344 result = RequestDoor(text, req_state);
4346 SetOverlayActive(overlay_active);
4351 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4353 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4354 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4357 if (dpo1->sort_priority != dpo2->sort_priority)
4358 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4360 compare_result = dpo1->nr - dpo2->nr;
4362 return compare_result;
4365 void InitGraphicCompatibilityInfo_Doors()
4371 struct DoorInfo *door;
4375 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4376 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4378 { -1, -1, -1, NULL }
4380 struct Rect door_rect_list[] =
4382 { DX, DY, DXSIZE, DYSIZE },
4383 { VX, VY, VXSIZE, VYSIZE }
4387 for (i = 0; doors[i].door_token != -1; i++)
4389 int door_token = doors[i].door_token;
4390 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4391 int part_1 = doors[i].part_1;
4392 int part_8 = doors[i].part_8;
4393 int part_2 = part_1 + 1;
4394 int part_3 = part_1 + 2;
4395 struct DoorInfo *door = doors[i].door;
4396 struct Rect *door_rect = &door_rect_list[door_index];
4397 boolean door_gfx_redefined = FALSE;
4399 /* check if any door part graphic definitions have been redefined */
4401 for (j = 0; door_part_controls[j].door_token != -1; j++)
4403 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4404 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4406 if (dpc->door_token == door_token && fi->redefined)
4407 door_gfx_redefined = TRUE;
4410 /* check for old-style door graphic/animation modifications */
4412 if (!door_gfx_redefined)
4414 if (door->anim_mode & ANIM_STATIC_PANEL)
4416 door->panel.step_xoffset = 0;
4417 door->panel.step_yoffset = 0;
4420 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4422 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4423 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4424 int num_door_steps, num_panel_steps;
4426 /* remove door part graphics other than the two default wings */
4428 for (j = 0; door_part_controls[j].door_token != -1; j++)
4430 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4431 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4433 if (dpc->graphic >= part_3 &&
4434 dpc->graphic <= part_8)
4438 /* set graphics and screen positions of the default wings */
4440 g_part_1->width = door_rect->width;
4441 g_part_1->height = door_rect->height;
4442 g_part_2->width = door_rect->width;
4443 g_part_2->height = door_rect->height;
4444 g_part_2->src_x = door_rect->width;
4445 g_part_2->src_y = g_part_1->src_y;
4447 door->part_2.x = door->part_1.x;
4448 door->part_2.y = door->part_1.y;
4450 if (door->width != -1)
4452 g_part_1->width = door->width;
4453 g_part_2->width = door->width;
4455 // special treatment for graphics and screen position of right wing
4456 g_part_2->src_x += door_rect->width - door->width;
4457 door->part_2.x += door_rect->width - door->width;
4460 if (door->height != -1)
4462 g_part_1->height = door->height;
4463 g_part_2->height = door->height;
4465 // special treatment for graphics and screen position of bottom wing
4466 g_part_2->src_y += door_rect->height - door->height;
4467 door->part_2.y += door_rect->height - door->height;
4470 /* set animation delays for the default wings and panels */
4472 door->part_1.step_delay = door->step_delay;
4473 door->part_2.step_delay = door->step_delay;
4474 door->panel.step_delay = door->step_delay;
4476 /* set animation draw order for the default wings */
4478 door->part_1.sort_priority = 2; /* draw left wing over ... */
4479 door->part_2.sort_priority = 1; /* ... right wing */
4481 /* set animation draw offset for the default wings */
4483 if (door->anim_mode & ANIM_HORIZONTAL)
4485 door->part_1.step_xoffset = door->step_offset;
4486 door->part_1.step_yoffset = 0;
4487 door->part_2.step_xoffset = door->step_offset * -1;
4488 door->part_2.step_yoffset = 0;
4490 num_door_steps = g_part_1->width / door->step_offset;
4492 else // ANIM_VERTICAL
4494 door->part_1.step_xoffset = 0;
4495 door->part_1.step_yoffset = door->step_offset;
4496 door->part_2.step_xoffset = 0;
4497 door->part_2.step_yoffset = door->step_offset * -1;
4499 num_door_steps = g_part_1->height / door->step_offset;
4502 /* set animation draw offset for the default panels */
4504 if (door->step_offset > 1)
4506 num_panel_steps = 2 * door_rect->height / door->step_offset;
4507 door->panel.start_step = num_panel_steps - num_door_steps;
4508 door->panel.start_step_closing = door->panel.start_step;
4512 num_panel_steps = door_rect->height / door->step_offset;
4513 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4514 door->panel.start_step_closing = door->panel.start_step;
4515 door->panel.step_delay *= 2;
4526 for (i = 0; door_part_controls[i].door_token != -1; i++)
4528 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4529 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4531 /* initialize "start_step_opening" and "start_step_closing", if needed */
4532 if (dpc->pos->start_step_opening == 0 &&
4533 dpc->pos->start_step_closing == 0)
4535 // dpc->pos->start_step_opening = dpc->pos->start_step;
4536 dpc->pos->start_step_closing = dpc->pos->start_step;
4539 /* fill structure for door part draw order (sorted below) */
4541 dpo->sort_priority = dpc->pos->sort_priority;
4544 /* sort door part controls according to sort_priority and graphic number */
4545 qsort(door_part_order, MAX_DOOR_PARTS,
4546 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4549 unsigned int OpenDoor(unsigned int door_state)
4551 if (door_state & DOOR_COPY_BACK)
4553 if (door_state & DOOR_OPEN_1)
4554 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4555 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4557 if (door_state & DOOR_OPEN_2)
4558 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4559 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4561 door_state &= ~DOOR_COPY_BACK;
4564 return MoveDoor(door_state);
4567 unsigned int CloseDoor(unsigned int door_state)
4569 unsigned int old_door_state = GetDoorState();
4571 if (!(door_state & DOOR_NO_COPY_BACK))
4573 if (old_door_state & DOOR_OPEN_1)
4574 BlitBitmap(backbuffer, bitmap_db_door_1,
4575 DX, DY, DXSIZE, DYSIZE, 0, 0);
4577 if (old_door_state & DOOR_OPEN_2)
4578 BlitBitmap(backbuffer, bitmap_db_door_2,
4579 VX, VY, VXSIZE, VYSIZE, 0, 0);
4581 door_state &= ~DOOR_NO_COPY_BACK;
4584 return MoveDoor(door_state);
4587 unsigned int GetDoorState()
4589 return MoveDoor(DOOR_GET_STATE);
4592 unsigned int SetDoorState(unsigned int door_state)
4594 return MoveDoor(door_state | DOOR_SET_STATE);
4597 int euclid(int a, int b)
4599 return (b ? euclid(b, a % b) : a);
4602 unsigned int MoveDoor(unsigned int door_state)
4604 struct Rect door_rect_list[] =
4606 { DX, DY, DXSIZE, DYSIZE },
4607 { VX, VY, VXSIZE, VYSIZE }
4609 static int door1 = DOOR_CLOSE_1;
4610 static int door2 = DOOR_CLOSE_2;
4611 unsigned int door_delay = 0;
4612 unsigned int door_delay_value;
4615 if (door_state == DOOR_GET_STATE)
4616 return (door1 | door2);
4618 if (door_state & DOOR_SET_STATE)
4620 if (door_state & DOOR_ACTION_1)
4621 door1 = door_state & DOOR_ACTION_1;
4622 if (door_state & DOOR_ACTION_2)
4623 door2 = door_state & DOOR_ACTION_2;
4625 return (door1 | door2);
4628 if (!(door_state & DOOR_FORCE_REDRAW))
4630 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4631 door_state &= ~DOOR_OPEN_1;
4632 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4633 door_state &= ~DOOR_CLOSE_1;
4634 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4635 door_state &= ~DOOR_OPEN_2;
4636 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4637 door_state &= ~DOOR_CLOSE_2;
4640 if (global.autoplay_leveldir)
4642 door_state |= DOOR_NO_DELAY;
4643 door_state &= ~DOOR_CLOSE_ALL;
4646 if (game_status == GAME_MODE_EDITOR)
4647 door_state |= DOOR_NO_DELAY;
4649 if (door_state & DOOR_ACTION)
4651 boolean door_panel_drawn[NUM_DOORS];
4652 boolean panel_has_doors[NUM_DOORS];
4653 boolean door_part_skip[MAX_DOOR_PARTS];
4654 boolean door_part_done[MAX_DOOR_PARTS];
4655 boolean door_part_done_all;
4656 int num_steps[MAX_DOOR_PARTS];
4657 int max_move_delay = 0; // delay for complete animations of all doors
4658 int max_step_delay = 0; // delay (ms) between two animation frames
4659 int num_move_steps = 0; // number of animation steps for all doors
4660 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4661 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4662 int current_move_delay = 0;
4666 for (i = 0; i < NUM_DOORS; i++)
4667 panel_has_doors[i] = FALSE;
4669 for (i = 0; i < MAX_DOOR_PARTS; i++)
4671 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4672 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4673 int door_token = dpc->door_token;
4675 door_part_done[i] = FALSE;
4676 door_part_skip[i] = (!(door_state & door_token) ||
4680 for (i = 0; i < MAX_DOOR_PARTS; i++)
4682 int nr = door_part_order[i].nr;
4683 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4684 struct DoorPartPosInfo *pos = dpc->pos;
4685 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4686 int door_token = dpc->door_token;
4687 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4688 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4689 int step_xoffset = ABS(pos->step_xoffset);
4690 int step_yoffset = ABS(pos->step_yoffset);
4691 int step_delay = pos->step_delay;
4692 int current_door_state = door_state & door_token;
4693 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4694 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4695 boolean part_opening = (is_panel ? door_closing : door_opening);
4696 int start_step = (part_opening ? pos->start_step_opening :
4697 pos->start_step_closing);
4698 float move_xsize = (step_xoffset ? g->width : 0);
4699 float move_ysize = (step_yoffset ? g->height : 0);
4700 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4701 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4702 int move_steps = (move_xsteps && move_ysteps ?
4703 MIN(move_xsteps, move_ysteps) :
4704 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4705 int move_delay = move_steps * step_delay;
4707 if (door_part_skip[nr])
4710 max_move_delay = MAX(max_move_delay, move_delay);
4711 max_step_delay = (max_step_delay == 0 ? step_delay :
4712 euclid(max_step_delay, step_delay));
4713 num_steps[nr] = move_steps;
4717 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4719 panel_has_doors[door_index] = TRUE;
4723 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4725 num_move_steps = max_move_delay / max_step_delay;
4726 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4728 door_delay_value = max_step_delay;
4730 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4732 start = num_move_steps - 1;
4736 /* opening door sound has priority over simultaneously closing door */
4737 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4738 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4739 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4740 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4743 for (k = start; k < num_move_steps; k++)
4745 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4747 door_part_done_all = TRUE;
4749 for (i = 0; i < NUM_DOORS; i++)
4750 door_panel_drawn[i] = FALSE;
4752 for (i = 0; i < MAX_DOOR_PARTS; i++)
4754 int nr = door_part_order[i].nr;
4755 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4756 struct DoorPartPosInfo *pos = dpc->pos;
4757 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4758 int door_token = dpc->door_token;
4759 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4760 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4761 boolean is_panel_and_door_has_closed = FALSE;
4762 struct Rect *door_rect = &door_rect_list[door_index];
4763 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4765 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4766 int current_door_state = door_state & door_token;
4767 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4768 boolean door_closing = !door_opening;
4769 boolean part_opening = (is_panel ? door_closing : door_opening);
4770 boolean part_closing = !part_opening;
4771 int start_step = (part_opening ? pos->start_step_opening :
4772 pos->start_step_closing);
4773 int step_delay = pos->step_delay;
4774 int step_factor = step_delay / max_step_delay;
4775 int k1 = (step_factor ? k / step_factor + 1 : k);
4776 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4777 int kk = MAX(0, k2);
4780 int src_x, src_y, src_xx, src_yy;
4781 int dst_x, dst_y, dst_xx, dst_yy;
4784 if (door_part_skip[nr])
4787 if (!(door_state & door_token))
4795 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4796 int kk_door = MAX(0, k2_door);
4797 int sync_frame = kk_door * door_delay_value;
4798 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4800 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
4801 &g_src_x, &g_src_y);
4806 if (!door_panel_drawn[door_index])
4808 ClearRectangle(drawto, door_rect->x, door_rect->y,
4809 door_rect->width, door_rect->height);
4811 door_panel_drawn[door_index] = TRUE;
4814 // draw opening or closing door parts
4816 if (pos->step_xoffset < 0) // door part on right side
4819 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4822 if (dst_xx + width > door_rect->width)
4823 width = door_rect->width - dst_xx;
4825 else // door part on left side
4828 dst_xx = pos->x - kk * pos->step_xoffset;
4832 src_xx = ABS(dst_xx);
4836 width = g->width - src_xx;
4838 if (width > door_rect->width)
4839 width = door_rect->width;
4841 // printf("::: k == %d [%d] \n", k, start_step);
4844 if (pos->step_yoffset < 0) // door part on bottom side
4847 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4850 if (dst_yy + height > door_rect->height)
4851 height = door_rect->height - dst_yy;
4853 else // door part on top side
4856 dst_yy = pos->y - kk * pos->step_yoffset;
4860 src_yy = ABS(dst_yy);
4864 height = g->height - src_yy;
4867 src_x = g_src_x + src_xx;
4868 src_y = g_src_y + src_yy;
4870 dst_x = door_rect->x + dst_xx;
4871 dst_y = door_rect->y + dst_yy;
4873 is_panel_and_door_has_closed =
4876 panel_has_doors[door_index] &&
4877 k >= num_move_steps_doors_only - 1);
4879 if (width >= 0 && width <= g->width &&
4880 height >= 0 && height <= g->height &&
4881 !is_panel_and_door_has_closed)
4883 if (is_panel || !pos->draw_masked)
4884 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4887 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4891 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4893 if ((part_opening && (width < 0 || height < 0)) ||
4894 (part_closing && (width >= g->width && height >= g->height)))
4895 door_part_done[nr] = TRUE;
4897 // continue door part animations, but not panel after door has closed
4898 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4899 door_part_done_all = FALSE;
4902 if (!(door_state & DOOR_NO_DELAY))
4906 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4908 current_move_delay += max_step_delay;
4910 /* prevent OS (Windows) from complaining about program not responding */
4914 if (door_part_done_all)
4919 if (door_state & DOOR_ACTION_1)
4920 door1 = door_state & DOOR_ACTION_1;
4921 if (door_state & DOOR_ACTION_2)
4922 door2 = door_state & DOOR_ACTION_2;
4924 // draw masked border over door area
4925 DrawMaskedBorder(REDRAW_DOOR_1);
4926 DrawMaskedBorder(REDRAW_DOOR_2);
4928 return (door1 | door2);
4931 static boolean useSpecialEditorDoor()
4933 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4934 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4936 // do not draw special editor door if editor border defined or redefined
4937 if (graphic_info[graphic].bitmap != NULL || redefined)
4940 // do not draw special editor door if global border defined to be empty
4941 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4944 // do not draw special editor door if viewport definitions do not match
4948 EY + EYSIZE != VY + VYSIZE)
4954 void DrawSpecialEditorDoor()
4956 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4957 int top_border_width = gfx1->width;
4958 int top_border_height = gfx1->height;
4959 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4960 int ex = EX - outer_border;
4961 int ey = EY - outer_border;
4962 int vy = VY - outer_border;
4963 int exsize = EXSIZE + 2 * outer_border;
4965 if (!useSpecialEditorDoor())
4968 /* draw bigger level editor toolbox window */
4969 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4970 top_border_width, top_border_height, ex, ey - top_border_height);
4971 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4972 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4974 redraw_mask |= REDRAW_ALL;
4977 void UndrawSpecialEditorDoor()
4979 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4980 int top_border_width = gfx1->width;
4981 int top_border_height = gfx1->height;
4982 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4983 int ex = EX - outer_border;
4984 int ey = EY - outer_border;
4985 int ey_top = ey - top_border_height;
4986 int exsize = EXSIZE + 2 * outer_border;
4987 int eysize = EYSIZE + 2 * outer_border;
4989 if (!useSpecialEditorDoor())
4992 /* draw normal tape recorder window */
4993 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4995 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4996 ex, ey_top, top_border_width, top_border_height,
4998 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4999 ex, ey, exsize, eysize, ex, ey);
5003 // if screen background is set to "[NONE]", clear editor toolbox window
5004 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
5005 ClearRectangle(drawto, ex, ey, exsize, eysize);
5008 redraw_mask |= REDRAW_ALL;
5012 /* ---------- new tool button stuff ---------------------------------------- */
5017 struct TextPosInfo *pos;
5020 } toolbutton_info[NUM_TOOL_BUTTONS] =
5023 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
5024 TOOL_CTRL_ID_YES, "yes"
5027 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5028 TOOL_CTRL_ID_NO, "no"
5031 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5032 TOOL_CTRL_ID_CONFIRM, "confirm"
5035 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5036 TOOL_CTRL_ID_PLAYER_1, "player 1"
5039 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5040 TOOL_CTRL_ID_PLAYER_2, "player 2"
5043 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5044 TOOL_CTRL_ID_PLAYER_3, "player 3"
5047 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5048 TOOL_CTRL_ID_PLAYER_4, "player 4"
5052 void CreateToolButtons()
5056 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5058 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
5059 struct TextPosInfo *pos = toolbutton_info[i].pos;
5060 struct GadgetInfo *gi;
5061 Bitmap *deco_bitmap = None;
5062 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5063 unsigned int event_mask = GD_EVENT_RELEASED;
5066 int gd_x = gfx->src_x;
5067 int gd_y = gfx->src_y;
5068 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5069 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5072 if (global.use_envelope_request)
5073 setRequestPosition(&dx, &dy, TRUE);
5075 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5077 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5079 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5080 pos->size, &deco_bitmap, &deco_x, &deco_y);
5081 deco_xpos = (gfx->width - pos->size) / 2;
5082 deco_ypos = (gfx->height - pos->size) / 2;
5085 gi = CreateGadget(GDI_CUSTOM_ID, id,
5086 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5087 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
5088 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
5089 GDI_WIDTH, gfx->width,
5090 GDI_HEIGHT, gfx->height,
5091 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5092 GDI_STATE, GD_BUTTON_UNPRESSED,
5093 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5094 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5095 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5096 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5097 GDI_DECORATION_SIZE, pos->size, pos->size,
5098 GDI_DECORATION_SHIFTING, 1, 1,
5099 GDI_DIRECT_DRAW, FALSE,
5100 GDI_EVENT_MASK, event_mask,
5101 GDI_CALLBACK_ACTION, HandleToolButtons,
5105 Error(ERR_EXIT, "cannot create gadget");
5107 tool_gadget[id] = gi;
5111 void FreeToolButtons()
5115 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5116 FreeGadget(tool_gadget[i]);
5119 static void UnmapToolButtons()
5123 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5124 UnmapGadget(tool_gadget[i]);
5127 static void HandleToolButtons(struct GadgetInfo *gi)
5129 request_gadget_id = gi->custom_id;
5132 static struct Mapping_EM_to_RND_object
5135 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
5136 boolean is_backside; /* backside of moving element */
5142 em_object_mapping_list[] =
5145 Xblank, TRUE, FALSE,
5149 Yacid_splash_eB, FALSE, FALSE,
5150 EL_ACID_SPLASH_RIGHT, -1, -1
5153 Yacid_splash_wB, FALSE, FALSE,
5154 EL_ACID_SPLASH_LEFT, -1, -1
5157 #ifdef EM_ENGINE_BAD_ROLL
5159 Xstone_force_e, FALSE, FALSE,
5160 EL_ROCK, -1, MV_BIT_RIGHT
5163 Xstone_force_w, FALSE, FALSE,
5164 EL_ROCK, -1, MV_BIT_LEFT
5167 Xnut_force_e, FALSE, FALSE,
5168 EL_NUT, -1, MV_BIT_RIGHT
5171 Xnut_force_w, FALSE, FALSE,
5172 EL_NUT, -1, MV_BIT_LEFT
5175 Xspring_force_e, FALSE, FALSE,
5176 EL_SPRING, -1, MV_BIT_RIGHT
5179 Xspring_force_w, FALSE, FALSE,
5180 EL_SPRING, -1, MV_BIT_LEFT
5183 Xemerald_force_e, FALSE, FALSE,
5184 EL_EMERALD, -1, MV_BIT_RIGHT
5187 Xemerald_force_w, FALSE, FALSE,
5188 EL_EMERALD, -1, MV_BIT_LEFT
5191 Xdiamond_force_e, FALSE, FALSE,
5192 EL_DIAMOND, -1, MV_BIT_RIGHT
5195 Xdiamond_force_w, FALSE, FALSE,
5196 EL_DIAMOND, -1, MV_BIT_LEFT
5199 Xbomb_force_e, FALSE, FALSE,
5200 EL_BOMB, -1, MV_BIT_RIGHT
5203 Xbomb_force_w, FALSE, FALSE,
5204 EL_BOMB, -1, MV_BIT_LEFT
5206 #endif /* EM_ENGINE_BAD_ROLL */
5209 Xstone, TRUE, FALSE,
5213 Xstone_pause, FALSE, FALSE,
5217 Xstone_fall, FALSE, FALSE,
5221 Ystone_s, FALSE, FALSE,
5222 EL_ROCK, ACTION_FALLING, -1
5225 Ystone_sB, FALSE, TRUE,
5226 EL_ROCK, ACTION_FALLING, -1
5229 Ystone_e, FALSE, FALSE,
5230 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5233 Ystone_eB, FALSE, TRUE,
5234 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5237 Ystone_w, FALSE, FALSE,
5238 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5241 Ystone_wB, FALSE, TRUE,
5242 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5249 Xnut_pause, FALSE, FALSE,
5253 Xnut_fall, FALSE, FALSE,
5257 Ynut_s, FALSE, FALSE,
5258 EL_NUT, ACTION_FALLING, -1
5261 Ynut_sB, FALSE, TRUE,
5262 EL_NUT, ACTION_FALLING, -1
5265 Ynut_e, FALSE, FALSE,
5266 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5269 Ynut_eB, FALSE, TRUE,
5270 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5273 Ynut_w, FALSE, FALSE,
5274 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5277 Ynut_wB, FALSE, TRUE,
5278 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5281 Xbug_n, TRUE, FALSE,
5285 Xbug_e, TRUE, FALSE,
5286 EL_BUG_RIGHT, -1, -1
5289 Xbug_s, TRUE, FALSE,
5293 Xbug_w, TRUE, FALSE,
5297 Xbug_gon, FALSE, FALSE,
5301 Xbug_goe, FALSE, FALSE,
5302 EL_BUG_RIGHT, -1, -1
5305 Xbug_gos, FALSE, FALSE,
5309 Xbug_gow, FALSE, FALSE,
5313 Ybug_n, FALSE, FALSE,
5314 EL_BUG, ACTION_MOVING, MV_BIT_UP
5317 Ybug_nB, FALSE, TRUE,
5318 EL_BUG, ACTION_MOVING, MV_BIT_UP
5321 Ybug_e, FALSE, FALSE,
5322 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5325 Ybug_eB, FALSE, TRUE,
5326 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5329 Ybug_s, FALSE, FALSE,
5330 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5333 Ybug_sB, FALSE, TRUE,
5334 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5337 Ybug_w, FALSE, FALSE,
5338 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5341 Ybug_wB, FALSE, TRUE,
5342 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5345 Ybug_w_n, FALSE, FALSE,
5346 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5349 Ybug_n_e, FALSE, FALSE,
5350 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5353 Ybug_e_s, FALSE, FALSE,
5354 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5357 Ybug_s_w, FALSE, FALSE,
5358 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5361 Ybug_e_n, FALSE, FALSE,
5362 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5365 Ybug_s_e, FALSE, FALSE,
5366 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5369 Ybug_w_s, FALSE, FALSE,
5370 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5373 Ybug_n_w, FALSE, FALSE,
5374 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5377 Ybug_stone, FALSE, FALSE,
5378 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5381 Ybug_spring, FALSE, FALSE,
5382 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5385 Xtank_n, TRUE, FALSE,
5386 EL_SPACESHIP_UP, -1, -1
5389 Xtank_e, TRUE, FALSE,
5390 EL_SPACESHIP_RIGHT, -1, -1
5393 Xtank_s, TRUE, FALSE,
5394 EL_SPACESHIP_DOWN, -1, -1
5397 Xtank_w, TRUE, FALSE,
5398 EL_SPACESHIP_LEFT, -1, -1
5401 Xtank_gon, FALSE, FALSE,
5402 EL_SPACESHIP_UP, -1, -1
5405 Xtank_goe, FALSE, FALSE,
5406 EL_SPACESHIP_RIGHT, -1, -1
5409 Xtank_gos, FALSE, FALSE,
5410 EL_SPACESHIP_DOWN, -1, -1
5413 Xtank_gow, FALSE, FALSE,
5414 EL_SPACESHIP_LEFT, -1, -1
5417 Ytank_n, FALSE, FALSE,
5418 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5421 Ytank_nB, FALSE, TRUE,
5422 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5425 Ytank_e, FALSE, FALSE,
5426 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5429 Ytank_eB, FALSE, TRUE,
5430 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5433 Ytank_s, FALSE, FALSE,
5434 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5437 Ytank_sB, FALSE, TRUE,
5438 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5441 Ytank_w, FALSE, FALSE,
5442 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5445 Ytank_wB, FALSE, TRUE,
5446 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5449 Ytank_w_n, FALSE, FALSE,
5450 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5453 Ytank_n_e, FALSE, FALSE,
5454 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5457 Ytank_e_s, FALSE, FALSE,
5458 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5461 Ytank_s_w, FALSE, FALSE,
5462 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5465 Ytank_e_n, FALSE, FALSE,
5466 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5469 Ytank_s_e, FALSE, FALSE,
5470 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5473 Ytank_w_s, FALSE, FALSE,
5474 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5477 Ytank_n_w, FALSE, FALSE,
5478 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5481 Ytank_stone, FALSE, FALSE,
5482 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5485 Ytank_spring, FALSE, FALSE,
5486 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5489 Xandroid, TRUE, FALSE,
5490 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5493 Xandroid_1_n, FALSE, FALSE,
5494 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5497 Xandroid_2_n, FALSE, FALSE,
5498 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5501 Xandroid_1_e, FALSE, FALSE,
5502 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5505 Xandroid_2_e, FALSE, FALSE,
5506 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5509 Xandroid_1_w, FALSE, FALSE,
5510 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5513 Xandroid_2_w, FALSE, FALSE,
5514 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5517 Xandroid_1_s, FALSE, FALSE,
5518 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5521 Xandroid_2_s, FALSE, FALSE,
5522 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5525 Yandroid_n, FALSE, FALSE,
5526 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5529 Yandroid_nB, FALSE, TRUE,
5530 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5533 Yandroid_ne, FALSE, FALSE,
5534 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5537 Yandroid_neB, FALSE, TRUE,
5538 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5541 Yandroid_e, FALSE, FALSE,
5542 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5545 Yandroid_eB, FALSE, TRUE,
5546 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5549 Yandroid_se, FALSE, FALSE,
5550 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5553 Yandroid_seB, FALSE, TRUE,
5554 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5557 Yandroid_s, FALSE, FALSE,
5558 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5561 Yandroid_sB, FALSE, TRUE,
5562 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5565 Yandroid_sw, FALSE, FALSE,
5566 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5569 Yandroid_swB, FALSE, TRUE,
5570 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5573 Yandroid_w, FALSE, FALSE,
5574 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5577 Yandroid_wB, FALSE, TRUE,
5578 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5581 Yandroid_nw, FALSE, FALSE,
5582 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5585 Yandroid_nwB, FALSE, TRUE,
5586 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5589 Xspring, TRUE, FALSE,
5593 Xspring_pause, FALSE, FALSE,
5597 Xspring_e, FALSE, FALSE,
5601 Xspring_w, FALSE, FALSE,
5605 Xspring_fall, FALSE, FALSE,
5609 Yspring_s, FALSE, FALSE,
5610 EL_SPRING, ACTION_FALLING, -1
5613 Yspring_sB, FALSE, TRUE,
5614 EL_SPRING, ACTION_FALLING, -1
5617 Yspring_e, FALSE, FALSE,
5618 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5621 Yspring_eB, FALSE, TRUE,
5622 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5625 Yspring_w, FALSE, FALSE,
5626 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5629 Yspring_wB, FALSE, TRUE,
5630 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5633 Yspring_kill_e, FALSE, FALSE,
5634 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5637 Yspring_kill_eB, FALSE, TRUE,
5638 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5641 Yspring_kill_w, FALSE, FALSE,
5642 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5645 Yspring_kill_wB, FALSE, TRUE,
5646 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5649 Xeater_n, TRUE, FALSE,
5650 EL_YAMYAM_UP, -1, -1
5653 Xeater_e, TRUE, FALSE,
5654 EL_YAMYAM_RIGHT, -1, -1
5657 Xeater_w, TRUE, FALSE,
5658 EL_YAMYAM_LEFT, -1, -1
5661 Xeater_s, TRUE, FALSE,
5662 EL_YAMYAM_DOWN, -1, -1
5665 Yeater_n, FALSE, FALSE,
5666 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5669 Yeater_nB, FALSE, TRUE,
5670 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5673 Yeater_e, FALSE, FALSE,
5674 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5677 Yeater_eB, FALSE, TRUE,
5678 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5681 Yeater_s, FALSE, FALSE,
5682 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5685 Yeater_sB, FALSE, TRUE,
5686 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5689 Yeater_w, FALSE, FALSE,
5690 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5693 Yeater_wB, FALSE, TRUE,
5694 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5697 Yeater_stone, FALSE, FALSE,
5698 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5701 Yeater_spring, FALSE, FALSE,
5702 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5705 Xalien, TRUE, FALSE,
5709 Xalien_pause, FALSE, FALSE,
5713 Yalien_n, FALSE, FALSE,
5714 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5717 Yalien_nB, FALSE, TRUE,
5718 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5721 Yalien_e, FALSE, FALSE,
5722 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5725 Yalien_eB, FALSE, TRUE,
5726 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5729 Yalien_s, FALSE, FALSE,
5730 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5733 Yalien_sB, FALSE, TRUE,
5734 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5737 Yalien_w, FALSE, FALSE,
5738 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5741 Yalien_wB, FALSE, TRUE,
5742 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5745 Yalien_stone, FALSE, FALSE,
5746 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5749 Yalien_spring, FALSE, FALSE,
5750 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5753 Xemerald, TRUE, FALSE,
5757 Xemerald_pause, FALSE, FALSE,
5761 Xemerald_fall, FALSE, FALSE,
5765 Xemerald_shine, FALSE, FALSE,
5766 EL_EMERALD, ACTION_TWINKLING, -1
5769 Yemerald_s, FALSE, FALSE,
5770 EL_EMERALD, ACTION_FALLING, -1
5773 Yemerald_sB, FALSE, TRUE,
5774 EL_EMERALD, ACTION_FALLING, -1
5777 Yemerald_e, FALSE, FALSE,
5778 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5781 Yemerald_eB, FALSE, TRUE,
5782 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5785 Yemerald_w, FALSE, FALSE,
5786 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5789 Yemerald_wB, FALSE, TRUE,
5790 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5793 Yemerald_eat, FALSE, FALSE,
5794 EL_EMERALD, ACTION_COLLECTING, -1
5797 Yemerald_stone, FALSE, FALSE,
5798 EL_NUT, ACTION_BREAKING, -1
5801 Xdiamond, TRUE, FALSE,
5805 Xdiamond_pause, FALSE, FALSE,
5809 Xdiamond_fall, FALSE, FALSE,
5813 Xdiamond_shine, FALSE, FALSE,
5814 EL_DIAMOND, ACTION_TWINKLING, -1
5817 Ydiamond_s, FALSE, FALSE,
5818 EL_DIAMOND, ACTION_FALLING, -1
5821 Ydiamond_sB, FALSE, TRUE,
5822 EL_DIAMOND, ACTION_FALLING, -1
5825 Ydiamond_e, FALSE, FALSE,
5826 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5829 Ydiamond_eB, FALSE, TRUE,
5830 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5833 Ydiamond_w, FALSE, FALSE,
5834 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5837 Ydiamond_wB, FALSE, TRUE,
5838 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5841 Ydiamond_eat, FALSE, FALSE,
5842 EL_DIAMOND, ACTION_COLLECTING, -1
5845 Ydiamond_stone, FALSE, FALSE,
5846 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5849 Xdrip_fall, TRUE, FALSE,
5850 EL_AMOEBA_DROP, -1, -1
5853 Xdrip_stretch, FALSE, FALSE,
5854 EL_AMOEBA_DROP, ACTION_FALLING, -1
5857 Xdrip_stretchB, FALSE, TRUE,
5858 EL_AMOEBA_DROP, ACTION_FALLING, -1
5861 Xdrip_eat, FALSE, FALSE,
5862 EL_AMOEBA_DROP, ACTION_GROWING, -1
5865 Ydrip_s1, FALSE, FALSE,
5866 EL_AMOEBA_DROP, ACTION_FALLING, -1
5869 Ydrip_s1B, FALSE, TRUE,
5870 EL_AMOEBA_DROP, ACTION_FALLING, -1
5873 Ydrip_s2, FALSE, FALSE,
5874 EL_AMOEBA_DROP, ACTION_FALLING, -1
5877 Ydrip_s2B, FALSE, TRUE,
5878 EL_AMOEBA_DROP, ACTION_FALLING, -1
5885 Xbomb_pause, FALSE, FALSE,
5889 Xbomb_fall, FALSE, FALSE,
5893 Ybomb_s, FALSE, FALSE,
5894 EL_BOMB, ACTION_FALLING, -1
5897 Ybomb_sB, FALSE, TRUE,
5898 EL_BOMB, ACTION_FALLING, -1
5901 Ybomb_e, FALSE, FALSE,
5902 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5905 Ybomb_eB, FALSE, TRUE,
5906 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5909 Ybomb_w, FALSE, FALSE,
5910 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5913 Ybomb_wB, FALSE, TRUE,
5914 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5917 Ybomb_eat, FALSE, FALSE,
5918 EL_BOMB, ACTION_ACTIVATING, -1
5921 Xballoon, TRUE, FALSE,
5925 Yballoon_n, FALSE, FALSE,
5926 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5929 Yballoon_nB, FALSE, TRUE,
5930 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5933 Yballoon_e, FALSE, FALSE,
5934 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5937 Yballoon_eB, FALSE, TRUE,
5938 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5941 Yballoon_s, FALSE, FALSE,
5942 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5945 Yballoon_sB, FALSE, TRUE,
5946 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5949 Yballoon_w, FALSE, FALSE,
5950 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5953 Yballoon_wB, FALSE, TRUE,
5954 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5957 Xgrass, TRUE, FALSE,
5958 EL_EMC_GRASS, -1, -1
5961 Ygrass_nB, FALSE, FALSE,
5962 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5965 Ygrass_eB, FALSE, FALSE,
5966 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5969 Ygrass_sB, FALSE, FALSE,
5970 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5973 Ygrass_wB, FALSE, FALSE,
5974 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5981 Ydirt_nB, FALSE, FALSE,
5982 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5985 Ydirt_eB, FALSE, FALSE,
5986 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5989 Ydirt_sB, FALSE, FALSE,
5990 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5993 Ydirt_wB, FALSE, FALSE,
5994 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5997 Xacid_ne, TRUE, FALSE,
5998 EL_ACID_POOL_TOPRIGHT, -1, -1
6001 Xacid_se, TRUE, FALSE,
6002 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
6005 Xacid_s, TRUE, FALSE,
6006 EL_ACID_POOL_BOTTOM, -1, -1
6009 Xacid_sw, TRUE, FALSE,
6010 EL_ACID_POOL_BOTTOMLEFT, -1, -1
6013 Xacid_nw, TRUE, FALSE,
6014 EL_ACID_POOL_TOPLEFT, -1, -1
6017 Xacid_1, TRUE, FALSE,
6021 Xacid_2, FALSE, FALSE,
6025 Xacid_3, FALSE, FALSE,
6029 Xacid_4, FALSE, FALSE,
6033 Xacid_5, FALSE, FALSE,
6037 Xacid_6, FALSE, FALSE,
6041 Xacid_7, FALSE, FALSE,
6045 Xacid_8, FALSE, FALSE,
6049 Xball_1, TRUE, FALSE,
6050 EL_EMC_MAGIC_BALL, -1, -1
6053 Xball_1B, FALSE, FALSE,
6054 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6057 Xball_2, FALSE, FALSE,
6058 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6061 Xball_2B, FALSE, FALSE,
6062 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6065 Yball_eat, FALSE, FALSE,
6066 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6069 Ykey_1_eat, FALSE, FALSE,
6070 EL_EM_KEY_1, ACTION_COLLECTING, -1
6073 Ykey_2_eat, FALSE, FALSE,
6074 EL_EM_KEY_2, ACTION_COLLECTING, -1
6077 Ykey_3_eat, FALSE, FALSE,
6078 EL_EM_KEY_3, ACTION_COLLECTING, -1
6081 Ykey_4_eat, FALSE, FALSE,
6082 EL_EM_KEY_4, ACTION_COLLECTING, -1
6085 Ykey_5_eat, FALSE, FALSE,
6086 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6089 Ykey_6_eat, FALSE, FALSE,
6090 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6093 Ykey_7_eat, FALSE, FALSE,
6094 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6097 Ykey_8_eat, FALSE, FALSE,
6098 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6101 Ylenses_eat, FALSE, FALSE,
6102 EL_EMC_LENSES, ACTION_COLLECTING, -1
6105 Ymagnify_eat, FALSE, FALSE,
6106 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6109 Ygrass_eat, FALSE, FALSE,
6110 EL_EMC_GRASS, ACTION_SNAPPING, -1
6113 Ydirt_eat, FALSE, FALSE,
6114 EL_SAND, ACTION_SNAPPING, -1
6117 Xgrow_ns, TRUE, FALSE,
6118 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6121 Ygrow_ns_eat, FALSE, FALSE,
6122 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6125 Xgrow_ew, TRUE, FALSE,
6126 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6129 Ygrow_ew_eat, FALSE, FALSE,
6130 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6133 Xwonderwall, TRUE, FALSE,
6134 EL_MAGIC_WALL, -1, -1
6137 XwonderwallB, FALSE, FALSE,
6138 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6141 Xamoeba_1, TRUE, FALSE,
6142 EL_AMOEBA_DRY, ACTION_OTHER, -1
6145 Xamoeba_2, FALSE, FALSE,
6146 EL_AMOEBA_DRY, ACTION_OTHER, -1
6149 Xamoeba_3, FALSE, FALSE,
6150 EL_AMOEBA_DRY, ACTION_OTHER, -1
6153 Xamoeba_4, FALSE, FALSE,
6154 EL_AMOEBA_DRY, ACTION_OTHER, -1
6157 Xamoeba_5, TRUE, FALSE,
6158 EL_AMOEBA_WET, ACTION_OTHER, -1
6161 Xamoeba_6, FALSE, FALSE,
6162 EL_AMOEBA_WET, ACTION_OTHER, -1
6165 Xamoeba_7, FALSE, FALSE,
6166 EL_AMOEBA_WET, ACTION_OTHER, -1
6169 Xamoeba_8, FALSE, FALSE,
6170 EL_AMOEBA_WET, ACTION_OTHER, -1
6173 Xdoor_1, TRUE, FALSE,
6174 EL_EM_GATE_1, -1, -1
6177 Xdoor_2, TRUE, FALSE,
6178 EL_EM_GATE_2, -1, -1
6181 Xdoor_3, TRUE, FALSE,
6182 EL_EM_GATE_3, -1, -1
6185 Xdoor_4, TRUE, FALSE,
6186 EL_EM_GATE_4, -1, -1
6189 Xdoor_5, TRUE, FALSE,
6190 EL_EMC_GATE_5, -1, -1
6193 Xdoor_6, TRUE, FALSE,
6194 EL_EMC_GATE_6, -1, -1
6197 Xdoor_7, TRUE, FALSE,
6198 EL_EMC_GATE_7, -1, -1
6201 Xdoor_8, TRUE, FALSE,
6202 EL_EMC_GATE_8, -1, -1
6205 Xkey_1, TRUE, FALSE,
6209 Xkey_2, TRUE, FALSE,
6213 Xkey_3, TRUE, FALSE,
6217 Xkey_4, TRUE, FALSE,
6221 Xkey_5, TRUE, FALSE,
6222 EL_EMC_KEY_5, -1, -1
6225 Xkey_6, TRUE, FALSE,
6226 EL_EMC_KEY_6, -1, -1
6229 Xkey_7, TRUE, FALSE,
6230 EL_EMC_KEY_7, -1, -1
6233 Xkey_8, TRUE, FALSE,
6234 EL_EMC_KEY_8, -1, -1
6237 Xwind_n, TRUE, FALSE,
6238 EL_BALLOON_SWITCH_UP, -1, -1
6241 Xwind_e, TRUE, FALSE,
6242 EL_BALLOON_SWITCH_RIGHT, -1, -1
6245 Xwind_s, TRUE, FALSE,
6246 EL_BALLOON_SWITCH_DOWN, -1, -1
6249 Xwind_w, TRUE, FALSE,
6250 EL_BALLOON_SWITCH_LEFT, -1, -1
6253 Xwind_nesw, TRUE, FALSE,
6254 EL_BALLOON_SWITCH_ANY, -1, -1
6257 Xwind_stop, TRUE, FALSE,
6258 EL_BALLOON_SWITCH_NONE, -1, -1
6262 EL_EM_EXIT_CLOSED, -1, -1
6265 Xexit_1, TRUE, FALSE,
6266 EL_EM_EXIT_OPEN, -1, -1
6269 Xexit_2, FALSE, FALSE,
6270 EL_EM_EXIT_OPEN, -1, -1
6273 Xexit_3, FALSE, FALSE,
6274 EL_EM_EXIT_OPEN, -1, -1
6277 Xdynamite, TRUE, FALSE,
6278 EL_EM_DYNAMITE, -1, -1
6281 Ydynamite_eat, FALSE, FALSE,
6282 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6285 Xdynamite_1, TRUE, FALSE,
6286 EL_EM_DYNAMITE_ACTIVE, -1, -1
6289 Xdynamite_2, FALSE, FALSE,
6290 EL_EM_DYNAMITE_ACTIVE, -1, -1
6293 Xdynamite_3, FALSE, FALSE,
6294 EL_EM_DYNAMITE_ACTIVE, -1, -1
6297 Xdynamite_4, FALSE, FALSE,
6298 EL_EM_DYNAMITE_ACTIVE, -1, -1
6301 Xbumper, TRUE, FALSE,
6302 EL_EMC_SPRING_BUMPER, -1, -1
6305 XbumperB, FALSE, FALSE,
6306 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6309 Xwheel, TRUE, FALSE,
6310 EL_ROBOT_WHEEL, -1, -1
6313 XwheelB, FALSE, FALSE,
6314 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6317 Xswitch, TRUE, FALSE,
6318 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6321 XswitchB, FALSE, FALSE,
6322 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6326 EL_QUICKSAND_EMPTY, -1, -1
6329 Xsand_stone, TRUE, FALSE,
6330 EL_QUICKSAND_FULL, -1, -1
6333 Xsand_stonein_1, FALSE, TRUE,
6334 EL_ROCK, ACTION_FILLING, -1
6337 Xsand_stonein_2, FALSE, TRUE,
6338 EL_ROCK, ACTION_FILLING, -1
6341 Xsand_stonein_3, FALSE, TRUE,
6342 EL_ROCK, ACTION_FILLING, -1
6345 Xsand_stonein_4, FALSE, TRUE,
6346 EL_ROCK, ACTION_FILLING, -1
6349 Xsand_stonesand_1, FALSE, FALSE,
6350 EL_QUICKSAND_EMPTYING, -1, -1
6353 Xsand_stonesand_2, FALSE, FALSE,
6354 EL_QUICKSAND_EMPTYING, -1, -1
6357 Xsand_stonesand_3, FALSE, FALSE,
6358 EL_QUICKSAND_EMPTYING, -1, -1
6361 Xsand_stonesand_4, FALSE, FALSE,
6362 EL_QUICKSAND_EMPTYING, -1, -1
6365 Xsand_stonesand_quickout_1, FALSE, FALSE,
6366 EL_QUICKSAND_EMPTYING, -1, -1
6369 Xsand_stonesand_quickout_2, FALSE, FALSE,
6370 EL_QUICKSAND_EMPTYING, -1, -1
6373 Xsand_stoneout_1, FALSE, FALSE,
6374 EL_ROCK, ACTION_EMPTYING, -1
6377 Xsand_stoneout_2, FALSE, FALSE,
6378 EL_ROCK, ACTION_EMPTYING, -1
6381 Xsand_sandstone_1, FALSE, FALSE,
6382 EL_QUICKSAND_FILLING, -1, -1
6385 Xsand_sandstone_2, FALSE, FALSE,
6386 EL_QUICKSAND_FILLING, -1, -1
6389 Xsand_sandstone_3, FALSE, FALSE,
6390 EL_QUICKSAND_FILLING, -1, -1
6393 Xsand_sandstone_4, FALSE, FALSE,
6394 EL_QUICKSAND_FILLING, -1, -1
6397 Xplant, TRUE, FALSE,
6398 EL_EMC_PLANT, -1, -1
6401 Yplant, FALSE, FALSE,
6402 EL_EMC_PLANT, -1, -1
6405 Xlenses, TRUE, FALSE,
6406 EL_EMC_LENSES, -1, -1
6409 Xmagnify, TRUE, FALSE,
6410 EL_EMC_MAGNIFIER, -1, -1
6413 Xdripper, TRUE, FALSE,
6414 EL_EMC_DRIPPER, -1, -1
6417 XdripperB, FALSE, FALSE,
6418 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6421 Xfake_blank, TRUE, FALSE,
6422 EL_INVISIBLE_WALL, -1, -1
6425 Xfake_blankB, FALSE, FALSE,
6426 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6429 Xfake_grass, TRUE, FALSE,
6430 EL_EMC_FAKE_GRASS, -1, -1
6433 Xfake_grassB, FALSE, FALSE,
6434 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6437 Xfake_door_1, TRUE, FALSE,
6438 EL_EM_GATE_1_GRAY, -1, -1
6441 Xfake_door_2, TRUE, FALSE,
6442 EL_EM_GATE_2_GRAY, -1, -1
6445 Xfake_door_3, TRUE, FALSE,
6446 EL_EM_GATE_3_GRAY, -1, -1
6449 Xfake_door_4, TRUE, FALSE,
6450 EL_EM_GATE_4_GRAY, -1, -1
6453 Xfake_door_5, TRUE, FALSE,
6454 EL_EMC_GATE_5_GRAY, -1, -1
6457 Xfake_door_6, TRUE, FALSE,
6458 EL_EMC_GATE_6_GRAY, -1, -1
6461 Xfake_door_7, TRUE, FALSE,
6462 EL_EMC_GATE_7_GRAY, -1, -1
6465 Xfake_door_8, TRUE, FALSE,
6466 EL_EMC_GATE_8_GRAY, -1, -1
6469 Xfake_acid_1, TRUE, FALSE,
6470 EL_EMC_FAKE_ACID, -1, -1
6473 Xfake_acid_2, FALSE, FALSE,
6474 EL_EMC_FAKE_ACID, -1, -1
6477 Xfake_acid_3, FALSE, FALSE,
6478 EL_EMC_FAKE_ACID, -1, -1
6481 Xfake_acid_4, FALSE, FALSE,
6482 EL_EMC_FAKE_ACID, -1, -1
6485 Xfake_acid_5, FALSE, FALSE,
6486 EL_EMC_FAKE_ACID, -1, -1
6489 Xfake_acid_6, FALSE, FALSE,
6490 EL_EMC_FAKE_ACID, -1, -1
6493 Xfake_acid_7, FALSE, FALSE,
6494 EL_EMC_FAKE_ACID, -1, -1
6497 Xfake_acid_8, FALSE, FALSE,
6498 EL_EMC_FAKE_ACID, -1, -1
6501 Xsteel_1, TRUE, FALSE,
6502 EL_STEELWALL, -1, -1
6505 Xsteel_2, TRUE, FALSE,
6506 EL_EMC_STEELWALL_2, -1, -1
6509 Xsteel_3, TRUE, FALSE,
6510 EL_EMC_STEELWALL_3, -1, -1
6513 Xsteel_4, TRUE, FALSE,
6514 EL_EMC_STEELWALL_4, -1, -1
6517 Xwall_1, TRUE, FALSE,
6521 Xwall_2, TRUE, FALSE,
6522 EL_EMC_WALL_14, -1, -1
6525 Xwall_3, TRUE, FALSE,
6526 EL_EMC_WALL_15, -1, -1
6529 Xwall_4, TRUE, FALSE,
6530 EL_EMC_WALL_16, -1, -1
6533 Xround_wall_1, TRUE, FALSE,
6534 EL_WALL_SLIPPERY, -1, -1
6537 Xround_wall_2, TRUE, FALSE,
6538 EL_EMC_WALL_SLIPPERY_2, -1, -1
6541 Xround_wall_3, TRUE, FALSE,
6542 EL_EMC_WALL_SLIPPERY_3, -1, -1
6545 Xround_wall_4, TRUE, FALSE,
6546 EL_EMC_WALL_SLIPPERY_4, -1, -1
6549 Xdecor_1, TRUE, FALSE,
6550 EL_EMC_WALL_8, -1, -1
6553 Xdecor_2, TRUE, FALSE,
6554 EL_EMC_WALL_6, -1, -1
6557 Xdecor_3, TRUE, FALSE,
6558 EL_EMC_WALL_4, -1, -1
6561 Xdecor_4, TRUE, FALSE,
6562 EL_EMC_WALL_7, -1, -1
6565 Xdecor_5, TRUE, FALSE,
6566 EL_EMC_WALL_5, -1, -1
6569 Xdecor_6, TRUE, FALSE,
6570 EL_EMC_WALL_9, -1, -1
6573 Xdecor_7, TRUE, FALSE,
6574 EL_EMC_WALL_10, -1, -1
6577 Xdecor_8, TRUE, FALSE,
6578 EL_EMC_WALL_1, -1, -1
6581 Xdecor_9, TRUE, FALSE,
6582 EL_EMC_WALL_2, -1, -1
6585 Xdecor_10, TRUE, FALSE,
6586 EL_EMC_WALL_3, -1, -1
6589 Xdecor_11, TRUE, FALSE,
6590 EL_EMC_WALL_11, -1, -1
6593 Xdecor_12, TRUE, FALSE,
6594 EL_EMC_WALL_12, -1, -1
6597 Xalpha_0, TRUE, FALSE,
6598 EL_CHAR('0'), -1, -1
6601 Xalpha_1, TRUE, FALSE,
6602 EL_CHAR('1'), -1, -1
6605 Xalpha_2, TRUE, FALSE,
6606 EL_CHAR('2'), -1, -1
6609 Xalpha_3, TRUE, FALSE,
6610 EL_CHAR('3'), -1, -1
6613 Xalpha_4, TRUE, FALSE,
6614 EL_CHAR('4'), -1, -1
6617 Xalpha_5, TRUE, FALSE,
6618 EL_CHAR('5'), -1, -1
6621 Xalpha_6, TRUE, FALSE,
6622 EL_CHAR('6'), -1, -1
6625 Xalpha_7, TRUE, FALSE,
6626 EL_CHAR('7'), -1, -1
6629 Xalpha_8, TRUE, FALSE,
6630 EL_CHAR('8'), -1, -1
6633 Xalpha_9, TRUE, FALSE,
6634 EL_CHAR('9'), -1, -1
6637 Xalpha_excla, TRUE, FALSE,
6638 EL_CHAR('!'), -1, -1
6641 Xalpha_quote, TRUE, FALSE,
6642 EL_CHAR('"'), -1, -1
6645 Xalpha_comma, TRUE, FALSE,
6646 EL_CHAR(','), -1, -1
6649 Xalpha_minus, TRUE, FALSE,
6650 EL_CHAR('-'), -1, -1
6653 Xalpha_perio, TRUE, FALSE,
6654 EL_CHAR('.'), -1, -1
6657 Xalpha_colon, TRUE, FALSE,
6658 EL_CHAR(':'), -1, -1
6661 Xalpha_quest, TRUE, FALSE,
6662 EL_CHAR('?'), -1, -1
6665 Xalpha_a, TRUE, FALSE,
6666 EL_CHAR('A'), -1, -1
6669 Xalpha_b, TRUE, FALSE,
6670 EL_CHAR('B'), -1, -1
6673 Xalpha_c, TRUE, FALSE,
6674 EL_CHAR('C'), -1, -1
6677 Xalpha_d, TRUE, FALSE,
6678 EL_CHAR('D'), -1, -1
6681 Xalpha_e, TRUE, FALSE,
6682 EL_CHAR('E'), -1, -1
6685 Xalpha_f, TRUE, FALSE,
6686 EL_CHAR('F'), -1, -1
6689 Xalpha_g, TRUE, FALSE,
6690 EL_CHAR('G'), -1, -1
6693 Xalpha_h, TRUE, FALSE,
6694 EL_CHAR('H'), -1, -1
6697 Xalpha_i, TRUE, FALSE,
6698 EL_CHAR('I'), -1, -1
6701 Xalpha_j, TRUE, FALSE,
6702 EL_CHAR('J'), -1, -1
6705 Xalpha_k, TRUE, FALSE,
6706 EL_CHAR('K'), -1, -1
6709 Xalpha_l, TRUE, FALSE,
6710 EL_CHAR('L'), -1, -1
6713 Xalpha_m, TRUE, FALSE,
6714 EL_CHAR('M'), -1, -1
6717 Xalpha_n, TRUE, FALSE,
6718 EL_CHAR('N'), -1, -1
6721 Xalpha_o, TRUE, FALSE,
6722 EL_CHAR('O'), -1, -1
6725 Xalpha_p, TRUE, FALSE,
6726 EL_CHAR('P'), -1, -1
6729 Xalpha_q, TRUE, FALSE,
6730 EL_CHAR('Q'), -1, -1
6733 Xalpha_r, TRUE, FALSE,
6734 EL_CHAR('R'), -1, -1
6737 Xalpha_s, TRUE, FALSE,
6738 EL_CHAR('S'), -1, -1
6741 Xalpha_t, TRUE, FALSE,
6742 EL_CHAR('T'), -1, -1
6745 Xalpha_u, TRUE, FALSE,
6746 EL_CHAR('U'), -1, -1
6749 Xalpha_v, TRUE, FALSE,
6750 EL_CHAR('V'), -1, -1
6753 Xalpha_w, TRUE, FALSE,
6754 EL_CHAR('W'), -1, -1
6757 Xalpha_x, TRUE, FALSE,
6758 EL_CHAR('X'), -1, -1
6761 Xalpha_y, TRUE, FALSE,
6762 EL_CHAR('Y'), -1, -1
6765 Xalpha_z, TRUE, FALSE,
6766 EL_CHAR('Z'), -1, -1
6769 Xalpha_arrow_e, TRUE, FALSE,
6770 EL_CHAR('>'), -1, -1
6773 Xalpha_arrow_w, TRUE, FALSE,
6774 EL_CHAR('<'), -1, -1
6777 Xalpha_copyr, TRUE, FALSE,
6778 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6782 Xboom_bug, FALSE, FALSE,
6783 EL_BUG, ACTION_EXPLODING, -1
6786 Xboom_bomb, FALSE, FALSE,
6787 EL_BOMB, ACTION_EXPLODING, -1
6790 Xboom_android, FALSE, FALSE,
6791 EL_EMC_ANDROID, ACTION_OTHER, -1
6794 Xboom_1, FALSE, FALSE,
6795 EL_DEFAULT, ACTION_EXPLODING, -1
6798 Xboom_2, FALSE, FALSE,
6799 EL_DEFAULT, ACTION_EXPLODING, -1
6802 Znormal, FALSE, FALSE,
6806 Zdynamite, FALSE, FALSE,
6810 Zplayer, FALSE, FALSE,
6814 ZBORDER, FALSE, FALSE,
6824 static struct Mapping_EM_to_RND_player
6833 em_player_mapping_list[] =
6837 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6841 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6845 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6849 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6853 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6857 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6861 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6865 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6869 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6873 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6877 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6881 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6885 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6889 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6893 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6897 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6901 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6905 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6909 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6913 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6917 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6921 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6925 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6929 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6933 EL_PLAYER_1, ACTION_DEFAULT, -1,
6937 EL_PLAYER_2, ACTION_DEFAULT, -1,
6941 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6945 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6949 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6953 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6957 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6961 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6965 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6969 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6973 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6977 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6981 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6985 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6989 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6993 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6997 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
7001 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
7005 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
7009 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
7013 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
7017 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
7021 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7025 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7029 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7033 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7037 EL_PLAYER_3, ACTION_DEFAULT, -1,
7041 EL_PLAYER_4, ACTION_DEFAULT, -1,
7050 int map_element_RND_to_EM(int element_rnd)
7052 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7053 static boolean mapping_initialized = FALSE;
7055 if (!mapping_initialized)
7059 /* return "Xalpha_quest" for all undefined elements in mapping array */
7060 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7061 mapping_RND_to_EM[i] = Xalpha_quest;
7063 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7064 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7065 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7066 em_object_mapping_list[i].element_em;
7068 mapping_initialized = TRUE;
7071 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7072 return mapping_RND_to_EM[element_rnd];
7074 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7079 int map_element_EM_to_RND(int element_em)
7081 static unsigned short mapping_EM_to_RND[TILE_MAX];
7082 static boolean mapping_initialized = FALSE;
7084 if (!mapping_initialized)
7088 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
7089 for (i = 0; i < TILE_MAX; i++)
7090 mapping_EM_to_RND[i] = EL_UNKNOWN;
7092 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7093 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7094 em_object_mapping_list[i].element_rnd;
7096 mapping_initialized = TRUE;
7099 if (element_em >= 0 && element_em < TILE_MAX)
7100 return mapping_EM_to_RND[element_em];
7102 Error(ERR_WARN, "invalid EM level element %d", element_em);
7107 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7109 struct LevelInfo_EM *level_em = level->native_em_level;
7110 struct LEVEL *lev = level_em->lev;
7113 for (i = 0; i < TILE_MAX; i++)
7114 lev->android_array[i] = Xblank;
7116 for (i = 0; i < level->num_android_clone_elements; i++)
7118 int element_rnd = level->android_clone_element[i];
7119 int element_em = map_element_RND_to_EM(element_rnd);
7121 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7122 if (em_object_mapping_list[j].element_rnd == element_rnd)
7123 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7127 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7129 struct LevelInfo_EM *level_em = level->native_em_level;
7130 struct LEVEL *lev = level_em->lev;
7133 level->num_android_clone_elements = 0;
7135 for (i = 0; i < TILE_MAX; i++)
7137 int element_em = lev->android_array[i];
7139 boolean element_found = FALSE;
7141 if (element_em == Xblank)
7144 element_rnd = map_element_EM_to_RND(element_em);
7146 for (j = 0; j < level->num_android_clone_elements; j++)
7147 if (level->android_clone_element[j] == element_rnd)
7148 element_found = TRUE;
7152 level->android_clone_element[level->num_android_clone_elements++] =
7155 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7160 if (level->num_android_clone_elements == 0)
7162 level->num_android_clone_elements = 1;
7163 level->android_clone_element[0] = EL_EMPTY;
7167 int map_direction_RND_to_EM(int direction)
7169 return (direction == MV_UP ? 0 :
7170 direction == MV_RIGHT ? 1 :
7171 direction == MV_DOWN ? 2 :
7172 direction == MV_LEFT ? 3 :
7176 int map_direction_EM_to_RND(int direction)
7178 return (direction == 0 ? MV_UP :
7179 direction == 1 ? MV_RIGHT :
7180 direction == 2 ? MV_DOWN :
7181 direction == 3 ? MV_LEFT :
7185 int map_element_RND_to_SP(int element_rnd)
7187 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
7189 if (element_rnd >= EL_SP_START &&
7190 element_rnd <= EL_SP_END)
7191 element_sp = element_rnd - EL_SP_START;
7192 else if (element_rnd == EL_EMPTY_SPACE)
7194 else if (element_rnd == EL_INVISIBLE_WALL)
7200 int map_element_SP_to_RND(int element_sp)
7202 int element_rnd = EL_UNKNOWN;
7204 if (element_sp >= 0x00 &&
7206 element_rnd = EL_SP_START + element_sp;
7207 else if (element_sp == 0x28)
7208 element_rnd = EL_INVISIBLE_WALL;
7213 int map_action_SP_to_RND(int action_sp)
7217 case actActive: return ACTION_ACTIVE;
7218 case actImpact: return ACTION_IMPACT;
7219 case actExploding: return ACTION_EXPLODING;
7220 case actDigging: return ACTION_DIGGING;
7221 case actSnapping: return ACTION_SNAPPING;
7222 case actCollecting: return ACTION_COLLECTING;
7223 case actPassing: return ACTION_PASSING;
7224 case actPushing: return ACTION_PUSHING;
7225 case actDropping: return ACTION_DROPPING;
7227 default: return ACTION_DEFAULT;
7231 int map_element_RND_to_MM(int element_rnd)
7233 return (element_rnd >= EL_CHAR_START &&
7234 element_rnd <= EL_CHAR_END ?
7235 EL_CHAR_START_NATIVE_MM + element_rnd - EL_CHAR_START :
7237 element_rnd >= EL_DF_START &&
7238 element_rnd <= EL_DF_END ?
7239 EL_DF_START_NATIVE_MM + element_rnd - EL_DF_START :
7241 element_rnd >= EL_MM_START &&
7242 element_rnd <= EL_MM_END ?
7243 EL_MM_START_NATIVE_MM + element_rnd - EL_MM_START :
7245 element_rnd >= EL_MM_RUNTIME_START &&
7246 element_rnd <= EL_MM_RUNTIME_END ?
7247 EL_MM_RUNTIME_START_NATIVE_MM + element_rnd - EL_MM_RUNTIME_START :
7249 element_rnd >= EL_MM_DUMMY_START &&
7250 element_rnd <= EL_MM_DUMMY_END ?
7251 EL_MM_DUMMY_START_NATIVE_MM + element_rnd - EL_MM_DUMMY_START :
7253 EL_EMPTY_NATIVE_MM);
7256 int map_element_MM_to_RND(int element_mm)
7258 return (element_mm == EL_EMPTY_NATIVE_MM ||
7259 element_mm == EL_DF_EMPTY_NATIVE_MM ?
7262 element_mm >= EL_CHAR_START_NATIVE_MM &&
7263 element_mm <= EL_CHAR_END_NATIVE_MM ?
7264 EL_CHAR_START + element_mm - EL_CHAR_START_NATIVE_MM :
7266 element_mm >= EL_DF_START_NATIVE_MM &&
7267 element_mm <= EL_DF_END_NATIVE_MM ?
7268 EL_DF_START + element_mm - EL_DF_START_NATIVE_MM :
7270 element_mm >= EL_MM_START_NATIVE_MM &&
7271 element_mm <= EL_MM_END_NATIVE_MM ?
7272 EL_MM_START + element_mm - EL_MM_START_NATIVE_MM :
7274 element_mm >= EL_MM_RUNTIME_START_NATIVE_MM &&
7275 element_mm <= EL_MM_RUNTIME_END_NATIVE_MM ?
7276 EL_MM_RUNTIME_START + element_mm - EL_MM_RUNTIME_START_NATIVE_MM :
7278 element_mm >= EL_MM_DUMMY_START_NATIVE_MM &&
7279 element_mm <= EL_MM_DUMMY_END_NATIVE_MM ?
7280 EL_MM_DUMMY_START + element_mm - EL_MM_DUMMY_START_NATIVE_MM :
7285 int get_next_element(int element)
7289 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7290 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7291 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7292 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7293 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7294 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7295 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7296 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7297 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7298 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7299 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7301 default: return element;
7305 int el_act_dir2img(int element, int action, int direction)
7307 element = GFX_ELEMENT(element);
7308 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7310 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7311 return element_info[element].direction_graphic[action][direction];
7314 static int el_act_dir2crm(int element, int action, int direction)
7316 element = GFX_ELEMENT(element);
7317 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7319 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7320 return element_info[element].direction_crumbled[action][direction];
7323 int el_act2img(int element, int action)
7325 element = GFX_ELEMENT(element);
7327 return element_info[element].graphic[action];
7330 int el_act2crm(int element, int action)
7332 element = GFX_ELEMENT(element);
7334 return element_info[element].crumbled[action];
7337 int el_dir2img(int element, int direction)
7339 element = GFX_ELEMENT(element);
7341 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7344 int el2baseimg(int element)
7346 return element_info[element].graphic[ACTION_DEFAULT];
7349 int el2img(int element)
7351 element = GFX_ELEMENT(element);
7353 return element_info[element].graphic[ACTION_DEFAULT];
7356 int el2edimg(int element)
7358 element = GFX_ELEMENT(element);
7360 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7363 int el2preimg(int element)
7365 element = GFX_ELEMENT(element);
7367 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7370 int el2panelimg(int element)
7372 element = GFX_ELEMENT(element);
7374 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7377 int font2baseimg(int font_nr)
7379 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7382 int getBeltNrFromBeltElement(int element)
7384 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7385 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7386 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
7389 int getBeltNrFromBeltActiveElement(int element)
7391 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
7392 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
7393 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
7396 int getBeltNrFromBeltSwitchElement(int element)
7398 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
7399 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
7400 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
7403 int getBeltDirNrFromBeltElement(int element)
7405 static int belt_base_element[4] =
7407 EL_CONVEYOR_BELT_1_LEFT,
7408 EL_CONVEYOR_BELT_2_LEFT,
7409 EL_CONVEYOR_BELT_3_LEFT,
7410 EL_CONVEYOR_BELT_4_LEFT
7413 int belt_nr = getBeltNrFromBeltElement(element);
7414 int belt_dir_nr = element - belt_base_element[belt_nr];
7416 return (belt_dir_nr % 3);
7419 int getBeltDirNrFromBeltSwitchElement(int element)
7421 static int belt_base_element[4] =
7423 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7424 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7425 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7426 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7429 int belt_nr = getBeltNrFromBeltSwitchElement(element);
7430 int belt_dir_nr = element - belt_base_element[belt_nr];
7432 return (belt_dir_nr % 3);
7435 int getBeltDirFromBeltElement(int element)
7437 static int belt_move_dir[3] =
7444 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7446 return belt_move_dir[belt_dir_nr];
7449 int getBeltDirFromBeltSwitchElement(int element)
7451 static int belt_move_dir[3] =
7458 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7460 return belt_move_dir[belt_dir_nr];
7463 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7465 static int belt_base_element[4] =
7467 EL_CONVEYOR_BELT_1_LEFT,
7468 EL_CONVEYOR_BELT_2_LEFT,
7469 EL_CONVEYOR_BELT_3_LEFT,
7470 EL_CONVEYOR_BELT_4_LEFT
7473 return belt_base_element[belt_nr] + belt_dir_nr;
7476 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7478 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7480 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7483 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7485 static int belt_base_element[4] =
7487 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7488 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7489 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7490 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7493 return belt_base_element[belt_nr] + belt_dir_nr;
7496 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7498 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7500 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7503 boolean getTeamMode_EM()
7505 return game.team_mode;
7508 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7510 int game_frame_delay_value;
7512 game_frame_delay_value =
7513 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7514 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7517 if (tape.playing && tape.warp_forward && !tape.pausing)
7518 game_frame_delay_value = 0;
7520 return game_frame_delay_value;
7523 unsigned int InitRND(int seed)
7525 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7526 return InitEngineRandom_EM(seed);
7527 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7528 return InitEngineRandom_SP(seed);
7529 else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
7530 return InitEngineRandom_MM(seed);
7532 return InitEngineRandom_RND(seed);
7535 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7536 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7538 inline static int get_effective_element_EM(int tile, int frame_em)
7540 int element = object_mapping[tile].element_rnd;
7541 int action = object_mapping[tile].action;
7542 boolean is_backside = object_mapping[tile].is_backside;
7543 boolean action_removing = (action == ACTION_DIGGING ||
7544 action == ACTION_SNAPPING ||
7545 action == ACTION_COLLECTING);
7551 case Yacid_splash_eB:
7552 case Yacid_splash_wB:
7553 return (frame_em > 5 ? EL_EMPTY : element);
7559 else /* frame_em == 7 */
7563 case Yacid_splash_eB:
7564 case Yacid_splash_wB:
7567 case Yemerald_stone:
7570 case Ydiamond_stone:
7574 case Xdrip_stretchB:
7593 case Xsand_stonein_1:
7594 case Xsand_stonein_2:
7595 case Xsand_stonein_3:
7596 case Xsand_stonein_4:
7600 return (is_backside || action_removing ? EL_EMPTY : element);
7605 inline static boolean check_linear_animation_EM(int tile)
7609 case Xsand_stonesand_1:
7610 case Xsand_stonesand_quickout_1:
7611 case Xsand_sandstone_1:
7612 case Xsand_stonein_1:
7613 case Xsand_stoneout_1:
7632 case Yacid_splash_eB:
7633 case Yacid_splash_wB:
7634 case Yemerald_stone:
7641 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7642 boolean has_crumbled_graphics,
7643 int crumbled, int sync_frame)
7645 /* if element can be crumbled, but certain action graphics are just empty
7646 space (like instantly snapping sand to empty space in 1 frame), do not
7647 treat these empty space graphics as crumbled graphics in EMC engine */
7648 if (crumbled == IMG_EMPTY_SPACE)
7649 has_crumbled_graphics = FALSE;
7651 if (has_crumbled_graphics)
7653 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7654 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7655 g_crumbled->anim_delay,
7656 g_crumbled->anim_mode,
7657 g_crumbled->anim_start_frame,
7660 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7661 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7663 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7664 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
7666 g_em->has_crumbled_graphics = TRUE;
7670 g_em->crumbled_bitmap = NULL;
7671 g_em->crumbled_src_x = 0;
7672 g_em->crumbled_src_y = 0;
7673 g_em->crumbled_border_size = 0;
7674 g_em->crumbled_tile_size = 0;
7676 g_em->has_crumbled_graphics = FALSE;
7680 void ResetGfxAnimation_EM(int x, int y, int tile)
7685 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7686 int tile, int frame_em, int x, int y)
7688 int action = object_mapping[tile].action;
7689 int direction = object_mapping[tile].direction;
7690 int effective_element = get_effective_element_EM(tile, frame_em);
7691 int graphic = (direction == MV_NONE ?
7692 el_act2img(effective_element, action) :
7693 el_act_dir2img(effective_element, action, direction));
7694 struct GraphicInfo *g = &graphic_info[graphic];
7696 boolean action_removing = (action == ACTION_DIGGING ||
7697 action == ACTION_SNAPPING ||
7698 action == ACTION_COLLECTING);
7699 boolean action_moving = (action == ACTION_FALLING ||
7700 action == ACTION_MOVING ||
7701 action == ACTION_PUSHING ||
7702 action == ACTION_EATING ||
7703 action == ACTION_FILLING ||
7704 action == ACTION_EMPTYING);
7705 boolean action_falling = (action == ACTION_FALLING ||
7706 action == ACTION_FILLING ||
7707 action == ACTION_EMPTYING);
7709 /* special case: graphic uses "2nd movement tile" and has defined
7710 7 frames for movement animation (or less) => use default graphic
7711 for last (8th) frame which ends the movement animation */
7712 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7714 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7715 graphic = (direction == MV_NONE ?
7716 el_act2img(effective_element, action) :
7717 el_act_dir2img(effective_element, action, direction));
7719 g = &graphic_info[graphic];
7722 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7726 else if (action_moving)
7728 boolean is_backside = object_mapping[tile].is_backside;
7732 int direction = object_mapping[tile].direction;
7733 int move_dir = (action_falling ? MV_DOWN : direction);
7738 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7739 if (g->double_movement && frame_em == 0)
7743 if (move_dir == MV_LEFT)
7744 GfxFrame[x - 1][y] = GfxFrame[x][y];
7745 else if (move_dir == MV_RIGHT)
7746 GfxFrame[x + 1][y] = GfxFrame[x][y];
7747 else if (move_dir == MV_UP)
7748 GfxFrame[x][y - 1] = GfxFrame[x][y];
7749 else if (move_dir == MV_DOWN)
7750 GfxFrame[x][y + 1] = GfxFrame[x][y];
7757 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7758 if (tile == Xsand_stonesand_quickout_1 ||
7759 tile == Xsand_stonesand_quickout_2)
7763 if (graphic_info[graphic].anim_global_sync)
7764 sync_frame = FrameCounter;
7765 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7766 sync_frame = GfxFrame[x][y];
7768 sync_frame = 0; /* playfield border (pseudo steel) */
7770 SetRandomAnimationValue(x, y);
7772 int frame = getAnimationFrame(g->anim_frames,
7775 g->anim_start_frame,
7778 g_em->unique_identifier =
7779 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7782 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7783 int tile, int frame_em, int x, int y)
7785 int action = object_mapping[tile].action;
7786 int direction = object_mapping[tile].direction;
7787 boolean is_backside = object_mapping[tile].is_backside;
7788 int effective_element = get_effective_element_EM(tile, frame_em);
7789 int effective_action = action;
7790 int graphic = (direction == MV_NONE ?
7791 el_act2img(effective_element, effective_action) :
7792 el_act_dir2img(effective_element, effective_action,
7794 int crumbled = (direction == MV_NONE ?
7795 el_act2crm(effective_element, effective_action) :
7796 el_act_dir2crm(effective_element, effective_action,
7798 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7799 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7800 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7801 struct GraphicInfo *g = &graphic_info[graphic];
7804 /* special case: graphic uses "2nd movement tile" and has defined
7805 7 frames for movement animation (or less) => use default graphic
7806 for last (8th) frame which ends the movement animation */
7807 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7809 effective_action = ACTION_DEFAULT;
7810 graphic = (direction == MV_NONE ?
7811 el_act2img(effective_element, effective_action) :
7812 el_act_dir2img(effective_element, effective_action,
7814 crumbled = (direction == MV_NONE ?
7815 el_act2crm(effective_element, effective_action) :
7816 el_act_dir2crm(effective_element, effective_action,
7819 g = &graphic_info[graphic];
7822 if (graphic_info[graphic].anim_global_sync)
7823 sync_frame = FrameCounter;
7824 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7825 sync_frame = GfxFrame[x][y];
7827 sync_frame = 0; /* playfield border (pseudo steel) */
7829 SetRandomAnimationValue(x, y);
7831 int frame = getAnimationFrame(g->anim_frames,
7834 g->anim_start_frame,
7837 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7838 g->double_movement && is_backside);
7840 /* (updating the "crumbled" graphic definitions is probably not really needed,
7841 as animations for crumbled graphics can't be longer than one EMC cycle) */
7842 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7846 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7847 int player_nr, int anim, int frame_em)
7849 int element = player_mapping[player_nr][anim].element_rnd;
7850 int action = player_mapping[player_nr][anim].action;
7851 int direction = player_mapping[player_nr][anim].direction;
7852 int graphic = (direction == MV_NONE ?
7853 el_act2img(element, action) :
7854 el_act_dir2img(element, action, direction));
7855 struct GraphicInfo *g = &graphic_info[graphic];
7858 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7860 stored_player[player_nr].StepFrame = frame_em;
7862 sync_frame = stored_player[player_nr].Frame;
7864 int frame = getAnimationFrame(g->anim_frames,
7867 g->anim_start_frame,
7870 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7871 &g_em->src_x, &g_em->src_y, FALSE);
7874 void InitGraphicInfo_EM(void)
7879 int num_em_gfx_errors = 0;
7881 if (graphic_info_em_object[0][0].bitmap == NULL)
7883 /* EM graphics not yet initialized in em_open_all() */
7888 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7891 /* always start with reliable default values */
7892 for (i = 0; i < TILE_MAX; i++)
7894 object_mapping[i].element_rnd = EL_UNKNOWN;
7895 object_mapping[i].is_backside = FALSE;
7896 object_mapping[i].action = ACTION_DEFAULT;
7897 object_mapping[i].direction = MV_NONE;
7900 /* always start with reliable default values */
7901 for (p = 0; p < MAX_PLAYERS; p++)
7903 for (i = 0; i < SPR_MAX; i++)
7905 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7906 player_mapping[p][i].action = ACTION_DEFAULT;
7907 player_mapping[p][i].direction = MV_NONE;
7911 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7913 int e = em_object_mapping_list[i].element_em;
7915 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7916 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7918 if (em_object_mapping_list[i].action != -1)
7919 object_mapping[e].action = em_object_mapping_list[i].action;
7921 if (em_object_mapping_list[i].direction != -1)
7922 object_mapping[e].direction =
7923 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7926 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7928 int a = em_player_mapping_list[i].action_em;
7929 int p = em_player_mapping_list[i].player_nr;
7931 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7933 if (em_player_mapping_list[i].action != -1)
7934 player_mapping[p][a].action = em_player_mapping_list[i].action;
7936 if (em_player_mapping_list[i].direction != -1)
7937 player_mapping[p][a].direction =
7938 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7941 for (i = 0; i < TILE_MAX; i++)
7943 int element = object_mapping[i].element_rnd;
7944 int action = object_mapping[i].action;
7945 int direction = object_mapping[i].direction;
7946 boolean is_backside = object_mapping[i].is_backside;
7947 boolean action_exploding = ((action == ACTION_EXPLODING ||
7948 action == ACTION_SMASHED_BY_ROCK ||
7949 action == ACTION_SMASHED_BY_SPRING) &&
7950 element != EL_DIAMOND);
7951 boolean action_active = (action == ACTION_ACTIVE);
7952 boolean action_other = (action == ACTION_OTHER);
7954 for (j = 0; j < 8; j++)
7956 int effective_element = get_effective_element_EM(i, j);
7957 int effective_action = (j < 7 ? action :
7958 i == Xdrip_stretch ? action :
7959 i == Xdrip_stretchB ? action :
7960 i == Ydrip_s1 ? action :
7961 i == Ydrip_s1B ? action :
7962 i == Xball_1B ? action :
7963 i == Xball_2 ? action :
7964 i == Xball_2B ? action :
7965 i == Yball_eat ? action :
7966 i == Ykey_1_eat ? action :
7967 i == Ykey_2_eat ? action :
7968 i == Ykey_3_eat ? action :
7969 i == Ykey_4_eat ? action :
7970 i == Ykey_5_eat ? action :
7971 i == Ykey_6_eat ? action :
7972 i == Ykey_7_eat ? action :
7973 i == Ykey_8_eat ? action :
7974 i == Ylenses_eat ? action :
7975 i == Ymagnify_eat ? action :
7976 i == Ygrass_eat ? action :
7977 i == Ydirt_eat ? action :
7978 i == Xsand_stonein_1 ? action :
7979 i == Xsand_stonein_2 ? action :
7980 i == Xsand_stonein_3 ? action :
7981 i == Xsand_stonein_4 ? action :
7982 i == Xsand_stoneout_1 ? action :
7983 i == Xsand_stoneout_2 ? action :
7984 i == Xboom_android ? ACTION_EXPLODING :
7985 action_exploding ? ACTION_EXPLODING :
7986 action_active ? action :
7987 action_other ? action :
7989 int graphic = (el_act_dir2img(effective_element, effective_action,
7991 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7993 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7994 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7995 boolean has_action_graphics = (graphic != base_graphic);
7996 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7997 struct GraphicInfo *g = &graphic_info[graphic];
7998 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8001 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
8002 boolean special_animation = (action != ACTION_DEFAULT &&
8003 g->anim_frames == 3 &&
8004 g->anim_delay == 2 &&
8005 g->anim_mode & ANIM_LINEAR);
8006 int sync_frame = (i == Xdrip_stretch ? 7 :
8007 i == Xdrip_stretchB ? 7 :
8008 i == Ydrip_s2 ? j + 8 :
8009 i == Ydrip_s2B ? j + 8 :
8018 i == Xfake_acid_1 ? 0 :
8019 i == Xfake_acid_2 ? 10 :
8020 i == Xfake_acid_3 ? 20 :
8021 i == Xfake_acid_4 ? 30 :
8022 i == Xfake_acid_5 ? 40 :
8023 i == Xfake_acid_6 ? 50 :
8024 i == Xfake_acid_7 ? 60 :
8025 i == Xfake_acid_8 ? 70 :
8027 i == Xball_2B ? j + 8 :
8028 i == Yball_eat ? j + 1 :
8029 i == Ykey_1_eat ? j + 1 :
8030 i == Ykey_2_eat ? j + 1 :
8031 i == Ykey_3_eat ? j + 1 :
8032 i == Ykey_4_eat ? j + 1 :
8033 i == Ykey_5_eat ? j + 1 :
8034 i == Ykey_6_eat ? j + 1 :
8035 i == Ykey_7_eat ? j + 1 :
8036 i == Ykey_8_eat ? j + 1 :
8037 i == Ylenses_eat ? j + 1 :
8038 i == Ymagnify_eat ? j + 1 :
8039 i == Ygrass_eat ? j + 1 :
8040 i == Ydirt_eat ? j + 1 :
8041 i == Xamoeba_1 ? 0 :
8042 i == Xamoeba_2 ? 1 :
8043 i == Xamoeba_3 ? 2 :
8044 i == Xamoeba_4 ? 3 :
8045 i == Xamoeba_5 ? 0 :
8046 i == Xamoeba_6 ? 1 :
8047 i == Xamoeba_7 ? 2 :
8048 i == Xamoeba_8 ? 3 :
8049 i == Xexit_2 ? j + 8 :
8050 i == Xexit_3 ? j + 16 :
8051 i == Xdynamite_1 ? 0 :
8052 i == Xdynamite_2 ? 8 :
8053 i == Xdynamite_3 ? 16 :
8054 i == Xdynamite_4 ? 24 :
8055 i == Xsand_stonein_1 ? j + 1 :
8056 i == Xsand_stonein_2 ? j + 9 :
8057 i == Xsand_stonein_3 ? j + 17 :
8058 i == Xsand_stonein_4 ? j + 25 :
8059 i == Xsand_stoneout_1 && j == 0 ? 0 :
8060 i == Xsand_stoneout_1 && j == 1 ? 0 :
8061 i == Xsand_stoneout_1 && j == 2 ? 1 :
8062 i == Xsand_stoneout_1 && j == 3 ? 2 :
8063 i == Xsand_stoneout_1 && j == 4 ? 2 :
8064 i == Xsand_stoneout_1 && j == 5 ? 3 :
8065 i == Xsand_stoneout_1 && j == 6 ? 4 :
8066 i == Xsand_stoneout_1 && j == 7 ? 4 :
8067 i == Xsand_stoneout_2 && j == 0 ? 5 :
8068 i == Xsand_stoneout_2 && j == 1 ? 6 :
8069 i == Xsand_stoneout_2 && j == 2 ? 7 :
8070 i == Xsand_stoneout_2 && j == 3 ? 8 :
8071 i == Xsand_stoneout_2 && j == 4 ? 9 :
8072 i == Xsand_stoneout_2 && j == 5 ? 11 :
8073 i == Xsand_stoneout_2 && j == 6 ? 13 :
8074 i == Xsand_stoneout_2 && j == 7 ? 15 :
8075 i == Xboom_bug && j == 1 ? 2 :
8076 i == Xboom_bug && j == 2 ? 2 :
8077 i == Xboom_bug && j == 3 ? 4 :
8078 i == Xboom_bug && j == 4 ? 4 :
8079 i == Xboom_bug && j == 5 ? 2 :
8080 i == Xboom_bug && j == 6 ? 2 :
8081 i == Xboom_bug && j == 7 ? 0 :
8082 i == Xboom_bomb && j == 1 ? 2 :
8083 i == Xboom_bomb && j == 2 ? 2 :
8084 i == Xboom_bomb && j == 3 ? 4 :
8085 i == Xboom_bomb && j == 4 ? 4 :
8086 i == Xboom_bomb && j == 5 ? 2 :
8087 i == Xboom_bomb && j == 6 ? 2 :
8088 i == Xboom_bomb && j == 7 ? 0 :
8089 i == Xboom_android && j == 7 ? 6 :
8090 i == Xboom_1 && j == 1 ? 2 :
8091 i == Xboom_1 && j == 2 ? 2 :
8092 i == Xboom_1 && j == 3 ? 4 :
8093 i == Xboom_1 && j == 4 ? 4 :
8094 i == Xboom_1 && j == 5 ? 6 :
8095 i == Xboom_1 && j == 6 ? 6 :
8096 i == Xboom_1 && j == 7 ? 8 :
8097 i == Xboom_2 && j == 0 ? 8 :
8098 i == Xboom_2 && j == 1 ? 8 :
8099 i == Xboom_2 && j == 2 ? 10 :
8100 i == Xboom_2 && j == 3 ? 10 :
8101 i == Xboom_2 && j == 4 ? 10 :
8102 i == Xboom_2 && j == 5 ? 12 :
8103 i == Xboom_2 && j == 6 ? 12 :
8104 i == Xboom_2 && j == 7 ? 12 :
8105 special_animation && j == 4 ? 3 :
8106 effective_action != action ? 0 :
8110 Bitmap *debug_bitmap = g_em->bitmap;
8111 int debug_src_x = g_em->src_x;
8112 int debug_src_y = g_em->src_y;
8115 int frame = getAnimationFrame(g->anim_frames,
8118 g->anim_start_frame,
8121 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8122 g->double_movement && is_backside);
8124 g_em->bitmap = src_bitmap;
8125 g_em->src_x = src_x;
8126 g_em->src_y = src_y;
8127 g_em->src_offset_x = 0;
8128 g_em->src_offset_y = 0;
8129 g_em->dst_offset_x = 0;
8130 g_em->dst_offset_y = 0;
8131 g_em->width = TILEX;
8132 g_em->height = TILEY;
8134 g_em->preserve_background = FALSE;
8136 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8139 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8140 effective_action == ACTION_MOVING ||
8141 effective_action == ACTION_PUSHING ||
8142 effective_action == ACTION_EATING)) ||
8143 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8144 effective_action == ACTION_EMPTYING)))
8147 (effective_action == ACTION_FALLING ||
8148 effective_action == ACTION_FILLING ||
8149 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8150 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8151 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8152 int num_steps = (i == Ydrip_s1 ? 16 :
8153 i == Ydrip_s1B ? 16 :
8154 i == Ydrip_s2 ? 16 :
8155 i == Ydrip_s2B ? 16 :
8156 i == Xsand_stonein_1 ? 32 :
8157 i == Xsand_stonein_2 ? 32 :
8158 i == Xsand_stonein_3 ? 32 :
8159 i == Xsand_stonein_4 ? 32 :
8160 i == Xsand_stoneout_1 ? 16 :
8161 i == Xsand_stoneout_2 ? 16 : 8);
8162 int cx = ABS(dx) * (TILEX / num_steps);
8163 int cy = ABS(dy) * (TILEY / num_steps);
8164 int step_frame = (i == Ydrip_s2 ? j + 8 :
8165 i == Ydrip_s2B ? j + 8 :
8166 i == Xsand_stonein_2 ? j + 8 :
8167 i == Xsand_stonein_3 ? j + 16 :
8168 i == Xsand_stonein_4 ? j + 24 :
8169 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8170 int step = (is_backside ? step_frame : num_steps - step_frame);
8172 if (is_backside) /* tile where movement starts */
8174 if (dx < 0 || dy < 0)
8176 g_em->src_offset_x = cx * step;
8177 g_em->src_offset_y = cy * step;
8181 g_em->dst_offset_x = cx * step;
8182 g_em->dst_offset_y = cy * step;
8185 else /* tile where movement ends */
8187 if (dx < 0 || dy < 0)
8189 g_em->dst_offset_x = cx * step;
8190 g_em->dst_offset_y = cy * step;
8194 g_em->src_offset_x = cx * step;
8195 g_em->src_offset_y = cy * step;
8199 g_em->width = TILEX - cx * step;
8200 g_em->height = TILEY - cy * step;
8203 /* create unique graphic identifier to decide if tile must be redrawn */
8204 /* bit 31 - 16 (16 bit): EM style graphic
8205 bit 15 - 12 ( 4 bit): EM style frame
8206 bit 11 - 6 ( 6 bit): graphic width
8207 bit 5 - 0 ( 6 bit): graphic height */
8208 g_em->unique_identifier =
8209 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8213 /* skip check for EMC elements not contained in original EMC artwork */
8214 if (element == EL_EMC_FAKE_ACID)
8217 if (g_em->bitmap != debug_bitmap ||
8218 g_em->src_x != debug_src_x ||
8219 g_em->src_y != debug_src_y ||
8220 g_em->src_offset_x != 0 ||
8221 g_em->src_offset_y != 0 ||
8222 g_em->dst_offset_x != 0 ||
8223 g_em->dst_offset_y != 0 ||
8224 g_em->width != TILEX ||
8225 g_em->height != TILEY)
8227 static int last_i = -1;
8235 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8236 i, element, element_info[element].token_name,
8237 element_action_info[effective_action].suffix, direction);
8239 if (element != effective_element)
8240 printf(" [%d ('%s')]",
8242 element_info[effective_element].token_name);
8246 if (g_em->bitmap != debug_bitmap)
8247 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8248 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8250 if (g_em->src_x != debug_src_x ||
8251 g_em->src_y != debug_src_y)
8252 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8253 j, (is_backside ? 'B' : 'F'),
8254 g_em->src_x, g_em->src_y,
8255 g_em->src_x / 32, g_em->src_y / 32,
8256 debug_src_x, debug_src_y,
8257 debug_src_x / 32, debug_src_y / 32);
8259 if (g_em->src_offset_x != 0 ||
8260 g_em->src_offset_y != 0 ||
8261 g_em->dst_offset_x != 0 ||
8262 g_em->dst_offset_y != 0)
8263 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8265 g_em->src_offset_x, g_em->src_offset_y,
8266 g_em->dst_offset_x, g_em->dst_offset_y);
8268 if (g_em->width != TILEX ||
8269 g_em->height != TILEY)
8270 printf(" %d (%d): size %d,%d should be %d,%d\n",
8272 g_em->width, g_em->height, TILEX, TILEY);
8274 num_em_gfx_errors++;
8281 for (i = 0; i < TILE_MAX; i++)
8283 for (j = 0; j < 8; j++)
8285 int element = object_mapping[i].element_rnd;
8286 int action = object_mapping[i].action;
8287 int direction = object_mapping[i].direction;
8288 boolean is_backside = object_mapping[i].is_backside;
8289 int graphic_action = el_act_dir2img(element, action, direction);
8290 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8292 if ((action == ACTION_SMASHED_BY_ROCK ||
8293 action == ACTION_SMASHED_BY_SPRING ||
8294 action == ACTION_EATING) &&
8295 graphic_action == graphic_default)
8297 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8298 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8299 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8300 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8303 /* no separate animation for "smashed by rock" -- use rock instead */
8304 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8305 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8307 g_em->bitmap = g_xx->bitmap;
8308 g_em->src_x = g_xx->src_x;
8309 g_em->src_y = g_xx->src_y;
8310 g_em->src_offset_x = g_xx->src_offset_x;
8311 g_em->src_offset_y = g_xx->src_offset_y;
8312 g_em->dst_offset_x = g_xx->dst_offset_x;
8313 g_em->dst_offset_y = g_xx->dst_offset_y;
8314 g_em->width = g_xx->width;
8315 g_em->height = g_xx->height;
8316 g_em->unique_identifier = g_xx->unique_identifier;
8319 g_em->preserve_background = TRUE;
8324 for (p = 0; p < MAX_PLAYERS; p++)
8326 for (i = 0; i < SPR_MAX; i++)
8328 int element = player_mapping[p][i].element_rnd;
8329 int action = player_mapping[p][i].action;
8330 int direction = player_mapping[p][i].direction;
8332 for (j = 0; j < 8; j++)
8334 int effective_element = element;
8335 int effective_action = action;
8336 int graphic = (direction == MV_NONE ?
8337 el_act2img(effective_element, effective_action) :
8338 el_act_dir2img(effective_element, effective_action,
8340 struct GraphicInfo *g = &graphic_info[graphic];
8341 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8347 Bitmap *debug_bitmap = g_em->bitmap;
8348 int debug_src_x = g_em->src_x;
8349 int debug_src_y = g_em->src_y;
8352 int frame = getAnimationFrame(g->anim_frames,
8355 g->anim_start_frame,
8358 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8360 g_em->bitmap = src_bitmap;
8361 g_em->src_x = src_x;
8362 g_em->src_y = src_y;
8363 g_em->src_offset_x = 0;
8364 g_em->src_offset_y = 0;
8365 g_em->dst_offset_x = 0;
8366 g_em->dst_offset_y = 0;
8367 g_em->width = TILEX;
8368 g_em->height = TILEY;
8372 /* skip check for EMC elements not contained in original EMC artwork */
8373 if (element == EL_PLAYER_3 ||
8374 element == EL_PLAYER_4)
8377 if (g_em->bitmap != debug_bitmap ||
8378 g_em->src_x != debug_src_x ||
8379 g_em->src_y != debug_src_y)
8381 static int last_i = -1;
8389 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
8390 p, i, element, element_info[element].token_name,
8391 element_action_info[effective_action].suffix, direction);
8393 if (element != effective_element)
8394 printf(" [%d ('%s')]",
8396 element_info[effective_element].token_name);
8400 if (g_em->bitmap != debug_bitmap)
8401 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
8402 j, (int)(g_em->bitmap), (int)(debug_bitmap));
8404 if (g_em->src_x != debug_src_x ||
8405 g_em->src_y != debug_src_y)
8406 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8408 g_em->src_x, g_em->src_y,
8409 g_em->src_x / 32, g_em->src_y / 32,
8410 debug_src_x, debug_src_y,
8411 debug_src_x / 32, debug_src_y / 32);
8413 num_em_gfx_errors++;
8423 printf("::: [%d errors found]\n", num_em_gfx_errors);
8429 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
8430 boolean any_player_moving,
8431 boolean any_player_snapping,
8432 boolean any_player_dropping)
8434 if (frame == 0 && !any_player_dropping)
8436 if (!local_player->was_waiting)
8438 if (!CheckSaveEngineSnapshotToList())
8441 local_player->was_waiting = TRUE;
8444 else if (any_player_moving || any_player_snapping || any_player_dropping)
8446 local_player->was_waiting = FALSE;
8450 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8451 boolean murphy_is_dropping)
8453 if (murphy_is_waiting)
8455 if (!local_player->was_waiting)
8457 if (!CheckSaveEngineSnapshotToList())
8460 local_player->was_waiting = TRUE;
8465 local_player->was_waiting = FALSE;
8469 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8470 boolean any_player_moving,
8471 boolean any_player_snapping,
8472 boolean any_player_dropping)
8474 if (tape.single_step && tape.recording && !tape.pausing)
8475 if (frame == 0 && !any_player_dropping)
8476 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8478 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8479 any_player_snapping, any_player_dropping);
8482 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8483 boolean murphy_is_dropping)
8485 boolean murphy_starts_dropping = FALSE;
8488 for (i = 0; i < MAX_PLAYERS; i++)
8489 if (stored_player[i].force_dropping)
8490 murphy_starts_dropping = TRUE;
8492 if (tape.single_step && tape.recording && !tape.pausing)
8493 if (murphy_is_waiting && !murphy_starts_dropping)
8494 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8496 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8499 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8500 int graphic, int sync_frame, int x, int y)
8502 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8504 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8507 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8509 return (IS_NEXT_FRAME(sync_frame, graphic));
8512 int getGraphicInfo_Delay(int graphic)
8514 return graphic_info[graphic].anim_delay;
8517 void PlayMenuSoundExt(int sound)
8519 if (sound == SND_UNDEFINED)
8522 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8523 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8526 if (IS_LOOP_SOUND(sound))
8527 PlaySoundLoop(sound);
8532 void PlayMenuSound()
8534 PlayMenuSoundExt(menu.sound[game_status]);
8537 void PlayMenuSoundStereo(int sound, int stereo_position)
8539 if (sound == SND_UNDEFINED)
8542 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8543 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8546 if (IS_LOOP_SOUND(sound))
8547 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8549 PlaySoundStereo(sound, stereo_position);
8552 void PlayMenuSoundIfLoopExt(int sound)
8554 if (sound == SND_UNDEFINED)
8557 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8558 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8561 if (IS_LOOP_SOUND(sound))
8562 PlaySoundLoop(sound);
8565 void PlayMenuSoundIfLoop()
8567 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8570 void PlayMenuMusicExt(int music)
8572 if (music == MUS_UNDEFINED)
8575 if (!setup.sound_music)
8581 void PlayMenuMusic()
8583 char *curr_music = getCurrentlyPlayingMusicFilename();
8584 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8586 if (!strEqual(curr_music, next_music))
8587 PlayMenuMusicExt(menu.music[game_status]);
8590 void PlayMenuSoundsAndMusic()
8596 static void FadeMenuSounds()
8601 static void FadeMenuMusic()
8603 char *curr_music = getCurrentlyPlayingMusicFilename();
8604 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8606 if (!strEqual(curr_music, next_music))
8610 void FadeMenuSoundsAndMusic()
8616 void PlaySoundActivating()
8619 PlaySound(SND_MENU_ITEM_ACTIVATING);
8623 void PlaySoundSelecting()
8626 PlaySound(SND_MENU_ITEM_SELECTING);
8630 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8632 boolean change_fullscreen = (setup.fullscreen !=
8633 video.fullscreen_enabled);
8634 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8635 setup.window_scaling_percent !=
8636 video.window_scaling_percent);
8638 if (change_window_scaling_percent && video.fullscreen_enabled)
8641 if (!change_window_scaling_percent && !video.fullscreen_available)
8644 #if defined(TARGET_SDL2)
8645 if (change_window_scaling_percent)
8647 SDLSetWindowScaling(setup.window_scaling_percent);
8651 else if (change_fullscreen)
8653 SDLSetWindowFullscreen(setup.fullscreen);
8655 /* set setup value according to successfully changed fullscreen mode */
8656 setup.fullscreen = video.fullscreen_enabled;
8662 if (change_fullscreen ||
8663 change_window_scaling_percent)
8665 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8667 /* save backbuffer content which gets lost when toggling fullscreen mode */
8668 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8670 if (change_window_scaling_percent)
8672 /* keep window mode, but change window scaling */
8673 video.fullscreen_enabled = TRUE; /* force new window scaling */
8676 /* toggle fullscreen */
8677 ChangeVideoModeIfNeeded(setup.fullscreen);
8679 /* set setup value according to successfully changed fullscreen mode */
8680 setup.fullscreen = video.fullscreen_enabled;
8682 /* restore backbuffer content from temporary backbuffer backup bitmap */
8683 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8685 FreeBitmap(tmp_backbuffer);
8687 /* update visible window/screen */
8688 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8692 void JoinRectangles(int *x, int *y, int *width, int *height,
8693 int x2, int y2, int width2, int height2)
8695 // do not join with "off-screen" rectangle
8696 if (x2 == -1 || y2 == -1)
8701 *width = MAX(*width, width2);
8702 *height = MAX(*height, height2);
8705 void SetAnimStatus(int anim_status_new)
8707 if (anim_status_new == GAME_MODE_MAIN)
8708 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
8709 else if (anim_status_new == GAME_MODE_SCORES)
8710 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
8712 global.anim_status_next = anim_status_new;
8714 // directly set screen modes that are entered without fading
8715 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
8716 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
8717 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
8718 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
8719 global.anim_status = global.anim_status_next;
8722 void SetGameStatus(int game_status_new)
8724 if (game_status_new != game_status)
8725 game_status_last_screen = game_status;
8727 game_status = game_status_new;
8729 SetAnimStatus(game_status_new);
8732 void SetFontStatus(int game_status_new)
8734 static int last_game_status = -1;
8736 if (game_status_new != -1)
8738 // set game status for font use after storing last game status
8739 last_game_status = game_status;
8740 game_status = game_status_new;
8744 // reset game status after font use from last stored game status
8745 game_status = last_game_status;
8749 void ResetFontStatus()
8754 void ChangeViewportPropertiesIfNeeded()
8756 int gfx_game_mode = game_status;
8757 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8759 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
8760 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8761 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8762 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8763 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8764 int new_win_xsize = vp_window->width;
8765 int new_win_ysize = vp_window->height;
8766 int border_size = vp_playfield->border_size;
8767 int new_sx = vp_playfield->x + border_size;
8768 int new_sy = vp_playfield->y + border_size;
8769 int new_sxsize = vp_playfield->width - 2 * border_size;
8770 int new_sysize = vp_playfield->height - 2 * border_size;
8771 int new_real_sx = vp_playfield->x;
8772 int new_real_sy = vp_playfield->y;
8773 int new_full_sxsize = vp_playfield->width;
8774 int new_full_sysize = vp_playfield->height;
8775 int new_dx = vp_door_1->x;
8776 int new_dy = vp_door_1->y;
8777 int new_dxsize = vp_door_1->width;
8778 int new_dysize = vp_door_1->height;
8779 int new_vx = vp_door_2->x;
8780 int new_vy = vp_door_2->y;
8781 int new_vxsize = vp_door_2->width;
8782 int new_vysize = vp_door_2->height;
8783 int new_ex = vp_door_3->x;
8784 int new_ey = vp_door_3->y;
8785 int new_exsize = vp_door_3->width;
8786 int new_eysize = vp_door_3->height;
8787 int new_tilesize_var =
8788 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8790 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8791 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8792 int new_scr_fieldx = new_sxsize / tilesize;
8793 int new_scr_fieldy = new_sysize / tilesize;
8794 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8795 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8796 boolean init_gfx_buffers = FALSE;
8797 boolean init_video_buffer = FALSE;
8798 boolean init_gadgets_and_anims = FALSE;
8799 boolean init_em_graphics = FALSE;
8801 if (new_win_xsize != WIN_XSIZE ||
8802 new_win_ysize != WIN_YSIZE)
8804 WIN_XSIZE = new_win_xsize;
8805 WIN_YSIZE = new_win_ysize;
8807 init_video_buffer = TRUE;
8808 init_gfx_buffers = TRUE;
8809 init_gadgets_and_anims = TRUE;
8811 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8814 if (new_scr_fieldx != SCR_FIELDX ||
8815 new_scr_fieldy != SCR_FIELDY)
8817 /* this always toggles between MAIN and GAME when using small tile size */
8819 SCR_FIELDX = new_scr_fieldx;
8820 SCR_FIELDY = new_scr_fieldy;
8822 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8833 new_sxsize != SXSIZE ||
8834 new_sysize != SYSIZE ||
8835 new_dxsize != DXSIZE ||
8836 new_dysize != DYSIZE ||
8837 new_vxsize != VXSIZE ||
8838 new_vysize != VYSIZE ||
8839 new_exsize != EXSIZE ||
8840 new_eysize != EYSIZE ||
8841 new_real_sx != REAL_SX ||
8842 new_real_sy != REAL_SY ||
8843 new_full_sxsize != FULL_SXSIZE ||
8844 new_full_sysize != FULL_SYSIZE ||
8845 new_tilesize_var != TILESIZE_VAR
8848 // ------------------------------------------------------------------------
8849 // determine next fading area for changed viewport definitions
8850 // ------------------------------------------------------------------------
8852 // start with current playfield area (default fading area)
8855 FADE_SXSIZE = FULL_SXSIZE;
8856 FADE_SYSIZE = FULL_SYSIZE;
8858 // add new playfield area if position or size has changed
8859 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8860 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8862 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8863 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8866 // add current and new door 1 area if position or size has changed
8867 if (new_dx != DX || new_dy != DY ||
8868 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8870 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8871 DX, DY, DXSIZE, DYSIZE);
8872 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8873 new_dx, new_dy, new_dxsize, new_dysize);
8876 // add current and new door 2 area if position or size has changed
8877 if (new_dx != VX || new_dy != VY ||
8878 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8880 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8881 VX, VY, VXSIZE, VYSIZE);
8882 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8883 new_vx, new_vy, new_vxsize, new_vysize);
8886 // ------------------------------------------------------------------------
8887 // handle changed tile size
8888 // ------------------------------------------------------------------------
8890 if (new_tilesize_var != TILESIZE_VAR)
8892 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8894 // changing tile size invalidates scroll values of engine snapshots
8895 FreeEngineSnapshotSingle();
8897 // changing tile size requires update of graphic mapping for EM engine
8898 init_em_graphics = TRUE;
8909 SXSIZE = new_sxsize;
8910 SYSIZE = new_sysize;
8911 DXSIZE = new_dxsize;
8912 DYSIZE = new_dysize;
8913 VXSIZE = new_vxsize;
8914 VYSIZE = new_vysize;
8915 EXSIZE = new_exsize;
8916 EYSIZE = new_eysize;
8917 REAL_SX = new_real_sx;
8918 REAL_SY = new_real_sy;
8919 FULL_SXSIZE = new_full_sxsize;
8920 FULL_SYSIZE = new_full_sysize;
8921 TILESIZE_VAR = new_tilesize_var;
8923 init_gfx_buffers = TRUE;
8924 init_gadgets_and_anims = TRUE;
8926 // printf("::: viewports: init_gfx_buffers\n");
8927 // printf("::: viewports: init_gadgets_and_anims\n");
8930 if (init_gfx_buffers)
8932 // printf("::: init_gfx_buffers\n");
8934 SCR_FIELDX = new_scr_fieldx_buffers;
8935 SCR_FIELDY = new_scr_fieldy_buffers;
8939 SCR_FIELDX = new_scr_fieldx;
8940 SCR_FIELDY = new_scr_fieldy;
8942 SetDrawDeactivationMask(REDRAW_NONE);
8943 SetDrawBackgroundMask(REDRAW_FIELD);
8946 if (init_video_buffer)
8948 // printf("::: init_video_buffer\n");
8950 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8951 InitImageTextures();
8954 if (init_gadgets_and_anims)
8956 // printf("::: init_gadgets_and_anims\n");
8959 InitGlobalAnimations();
8962 if (init_em_graphics)
8964 InitGraphicInfo_EM();