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 int getLevelFromScreenX(int x)
363 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
364 return getLevelFromScreenX_EM(x);
365 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
366 return getLevelFromScreenX_SP(x);
368 return getLevelFromScreenX_RND(x);
371 int getLevelFromScreenY(int y)
373 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
374 return getLevelFromScreenY_EM(y);
375 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
376 return getLevelFromScreenY_SP(y);
378 return getLevelFromScreenY_RND(y);
381 void DumpTile(int x, int y)
386 printf_line("-", 79);
387 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
388 printf_line("-", 79);
390 if (!IN_LEV_FIELD(x, y))
392 printf("(not in level field)\n");
398 printf(" Feld: %d\t['%s']\n", Feld[x][y],
399 element_info[Feld[x][y]].token_name);
400 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
401 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
402 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
403 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
404 printf(" MovPos: %d\n", MovPos[x][y]);
405 printf(" MovDir: %d\n", MovDir[x][y]);
406 printf(" MovDelay: %d\n", MovDelay[x][y]);
407 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
408 printf(" CustomValue: %d\n", CustomValue[x][y]);
409 printf(" GfxElement: %d\n", GfxElement[x][y]);
410 printf(" GfxAction: %d\n", GfxAction[x][y]);
411 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
412 printf(" Player x/y: %d, %d\n", local_player->jx, local_player->jy);
416 void DumpTileFromScreen(int sx, int sy)
418 int lx = getLevelFromScreenX(sx);
419 int ly = getLevelFromScreenY(sy);
424 void SetDrawtoField(int mode)
426 if (mode == DRAW_TO_FIELDBUFFER)
432 BX2 = SCR_FIELDX + 1;
433 BY2 = SCR_FIELDY + 1;
435 drawto_field = fieldbuffer;
437 else /* DRAW_TO_BACKBUFFER */
443 BX2 = SCR_FIELDX - 1;
444 BY2 = SCR_FIELDY - 1;
446 drawto_field = backbuffer;
450 static void RedrawPlayfield_RND()
452 if (game.envelope_active)
455 DrawLevel(REDRAW_ALL);
459 void RedrawPlayfield()
461 if (game_status != GAME_MODE_PLAYING)
464 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
465 RedrawPlayfield_EM(TRUE);
466 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
467 RedrawPlayfield_SP(TRUE);
468 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
469 RedrawPlayfield_RND();
471 BlitScreenToBitmap(backbuffer);
473 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
477 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
480 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
481 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
483 if (x == -1 && y == -1)
486 if (draw_target == DRAW_TO_SCREEN)
487 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
489 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
492 static void DrawMaskedBorderExt_FIELD(int draw_target)
494 if (global.border_status >= GAME_MODE_MAIN &&
495 global.border_status <= GAME_MODE_PLAYING &&
496 border.draw_masked[global.border_status])
497 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
501 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
503 // when drawing to backbuffer, never draw border over open doors
504 if (draw_target == DRAW_TO_BACKBUFFER &&
505 (GetDoorState() & DOOR_OPEN_1))
508 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
509 (global.border_status != GAME_MODE_EDITOR ||
510 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
511 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
514 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
516 // when drawing to backbuffer, never draw border over open doors
517 if (draw_target == DRAW_TO_BACKBUFFER &&
518 (GetDoorState() & DOOR_OPEN_2))
521 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
522 global.border_status != GAME_MODE_EDITOR)
523 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
526 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
528 /* currently not available */
531 static void DrawMaskedBorderExt_ALL(int draw_target)
533 DrawMaskedBorderExt_FIELD(draw_target);
534 DrawMaskedBorderExt_DOOR_1(draw_target);
535 DrawMaskedBorderExt_DOOR_2(draw_target);
536 DrawMaskedBorderExt_DOOR_3(draw_target);
539 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
541 /* never draw masked screen borders on borderless screens */
542 if (global.border_status == GAME_MODE_LOADING ||
543 global.border_status == GAME_MODE_TITLE)
546 if (redraw_mask & REDRAW_ALL)
547 DrawMaskedBorderExt_ALL(draw_target);
550 if (redraw_mask & REDRAW_FIELD)
551 DrawMaskedBorderExt_FIELD(draw_target);
552 if (redraw_mask & REDRAW_DOOR_1)
553 DrawMaskedBorderExt_DOOR_1(draw_target);
554 if (redraw_mask & REDRAW_DOOR_2)
555 DrawMaskedBorderExt_DOOR_2(draw_target);
556 if (redraw_mask & REDRAW_DOOR_3)
557 DrawMaskedBorderExt_DOOR_3(draw_target);
561 void DrawMaskedBorder_FIELD()
563 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
566 void DrawMaskedBorder(int redraw_mask)
568 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
571 void DrawMaskedBorderToTarget(int draw_target)
573 if (draw_target == DRAW_TO_BACKBUFFER ||
574 draw_target == DRAW_TO_SCREEN)
576 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
580 int last_border_status = global.border_status;
582 if (draw_target == DRAW_TO_FADE_SOURCE)
584 global.border_status = gfx.fade_border_source_status;
585 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
587 else if (draw_target == DRAW_TO_FADE_TARGET)
589 global.border_status = gfx.fade_border_target_status;
590 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
593 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
595 global.border_status = last_border_status;
596 gfx.masked_border_bitmap_ptr = backbuffer;
600 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
602 int fx = getFieldbufferOffsetX_RND();
603 int fy = getFieldbufferOffsetY_RND();
605 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
608 void BlitScreenToBitmap(Bitmap *target_bitmap)
610 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
611 BlitScreenToBitmap_EM(target_bitmap);
612 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
613 BlitScreenToBitmap_SP(target_bitmap);
614 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
615 BlitScreenToBitmap_RND(target_bitmap);
617 redraw_mask |= REDRAW_FIELD;
620 void DrawFramesPerSecond()
623 int font_nr = FONT_TEXT_2;
624 int font_width = getFontWidth(font_nr);
625 int draw_deactivation_mask = GetDrawDeactivationMask();
626 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
628 /* draw FPS with leading space (needed if field buffer deactivated) */
629 sprintf(text, " %04.1f fps", global.frames_per_second);
631 /* override draw deactivation mask (required for invisible warp mode) */
632 SetDrawDeactivationMask(REDRAW_NONE);
634 /* draw opaque FPS if field buffer deactivated, else draw masked FPS */
635 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
636 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
638 /* set draw deactivation mask to previous value */
639 SetDrawDeactivationMask(draw_deactivation_mask);
641 /* force full-screen redraw in this frame */
642 redraw_mask = REDRAW_ALL;
646 static void PrintFrameTimeDebugging()
648 static unsigned int last_counter = 0;
649 unsigned int counter = Counter();
650 int diff_1 = counter - last_counter;
651 int diff_2 = diff_1 - GAME_FRAME_DELAY;
653 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
654 char diff_bar[2 * diff_2_max + 5];
658 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
660 for (i = 0; i < diff_2_max; i++)
661 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
662 i >= diff_2_max - diff_2_cut ? '-' : ' ');
664 diff_bar[pos++] = '|';
666 for (i = 0; i < diff_2_max; i++)
667 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
669 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
671 diff_bar[pos++] = '\0';
673 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
676 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
679 last_counter = counter;
683 static int unifiedRedrawMask(int mask)
685 if (mask & REDRAW_ALL)
688 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
694 static boolean equalRedrawMasks(int mask_1, int mask_2)
696 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
701 static int last_redraw_mask = REDRAW_NONE;
703 // force screen redraw in every frame to continue drawing global animations
704 // (but always use the last redraw mask to prevent unwanted side effects)
705 if (redraw_mask == REDRAW_NONE)
706 redraw_mask = last_redraw_mask;
708 last_redraw_mask = redraw_mask;
711 // masked border now drawn immediately when blitting backbuffer to window
713 // draw masked border to all viewports, if defined
714 DrawMaskedBorder(redraw_mask);
717 // draw frames per second (only if debug mode is enabled)
718 if (redraw_mask & REDRAW_FPS)
719 DrawFramesPerSecond();
721 // remove playfield redraw before potentially merging with doors redraw
722 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
723 redraw_mask &= ~REDRAW_FIELD;
725 // redraw complete window if both playfield and (some) doors need redraw
726 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
727 redraw_mask = REDRAW_ALL;
729 /* although redrawing the whole window would be fine for normal gameplay,
730 being able to only redraw the playfield is required for deactivating
731 certain drawing areas (mainly playfield) to work, which is needed for
732 warp-forward to be fast enough (by skipping redraw of most frames) */
734 if (redraw_mask & REDRAW_ALL)
736 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
738 else if (redraw_mask & REDRAW_FIELD)
740 BlitBitmap(backbuffer, window,
741 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
743 else if (redraw_mask & REDRAW_DOORS)
745 // merge door areas to prevent calling screen redraw more than once
751 if (redraw_mask & REDRAW_DOOR_1)
755 x2 = MAX(x2, DX + DXSIZE);
756 y2 = MAX(y2, DY + DYSIZE);
759 if (redraw_mask & REDRAW_DOOR_2)
763 x2 = MAX(x2, VX + VXSIZE);
764 y2 = MAX(y2, VY + VYSIZE);
767 if (redraw_mask & REDRAW_DOOR_3)
771 x2 = MAX(x2, EX + EXSIZE);
772 y2 = MAX(y2, EY + EYSIZE);
775 // make sure that at least one pixel is blitted, and inside the screen
776 // (else nothing is blitted, causing the animations not to be updated)
777 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
778 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
779 x2 = MIN(MAX(1, x2), WIN_XSIZE);
780 y2 = MIN(MAX(1, y2), WIN_YSIZE);
782 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
785 redraw_mask = REDRAW_NONE;
788 PrintFrameTimeDebugging();
792 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
794 unsigned int frame_delay_value_old = GetVideoFrameDelay();
796 SetVideoFrameDelay(frame_delay_value);
800 SetVideoFrameDelay(frame_delay_value_old);
803 static int fade_type_skip = FADE_TYPE_NONE;
805 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
807 void (*draw_border_function)(void) = NULL;
808 int x, y, width, height;
809 int fade_delay, post_delay;
811 if (fade_type == FADE_TYPE_FADE_OUT)
813 if (fade_type_skip != FADE_TYPE_NONE)
815 /* skip all fade operations until specified fade operation */
816 if (fade_type & fade_type_skip)
817 fade_type_skip = FADE_TYPE_NONE;
822 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
826 redraw_mask |= fade_mask;
828 if (fade_type == FADE_TYPE_SKIP)
830 fade_type_skip = fade_mode;
835 fade_delay = fading.fade_delay;
836 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
838 if (fade_type_skip != FADE_TYPE_NONE)
840 /* skip all fade operations until specified fade operation */
841 if (fade_type & fade_type_skip)
842 fade_type_skip = FADE_TYPE_NONE;
847 if (global.autoplay_leveldir)
852 if (fade_mask == REDRAW_FIELD)
857 height = FADE_SYSIZE;
859 if (border.draw_masked_when_fading)
860 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
862 DrawMaskedBorder_FIELD(); /* draw once */
864 else /* REDRAW_ALL */
872 if (!setup.fade_screens ||
874 fading.fade_mode == FADE_MODE_NONE)
876 if (fade_mode == FADE_MODE_FADE_OUT)
879 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
881 redraw_mask &= ~fade_mask;
886 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
887 draw_border_function);
889 redraw_mask &= ~fade_mask;
892 static void SetScreenStates_BeforeFadingIn()
894 // temporarily set screen mode for animations to screen after fading in
895 global.anim_status = global.anim_status_next;
897 // store backbuffer with all animations that will be started after fading in
898 if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
899 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
901 // set screen mode for animations back to fading
902 global.anim_status = GAME_MODE_PSEUDO_FADING;
905 static void SetScreenStates_AfterFadingIn()
907 // store new source screen (to use correct masked border for fading)
908 gfx.fade_border_source_status = global.border_status;
910 global.anim_status = global.anim_status_next;
913 static void SetScreenStates_BeforeFadingOut()
915 // store new target screen (to use correct masked border for fading)
916 gfx.fade_border_target_status = game_status;
918 // set screen mode for animations to fading
919 global.anim_status = GAME_MODE_PSEUDO_FADING;
921 // store backbuffer with all animations that will be stopped for fading out
922 if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
923 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
926 static void SetScreenStates_AfterFadingOut()
928 global.border_status = game_status;
931 void FadeIn(int fade_mask)
933 SetScreenStates_BeforeFadingIn();
936 DrawMaskedBorder(REDRAW_ALL);
939 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
940 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
942 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
946 FADE_SXSIZE = FULL_SXSIZE;
947 FADE_SYSIZE = FULL_SYSIZE;
949 if (game_status == GAME_MODE_PLAYING &&
950 strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
951 SetOverlayActive(TRUE);
953 SetScreenStates_AfterFadingIn();
955 // force update of global animation status in case of rapid screen changes
956 redraw_mask = REDRAW_ALL;
960 void FadeOut(int fade_mask)
962 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
963 if (!equalRedrawMasks(fade_mask, redraw_mask))
966 SetScreenStates_BeforeFadingOut();
968 SetOverlayActive(FALSE);
971 DrawMaskedBorder(REDRAW_ALL);
974 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
975 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
977 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
979 SetScreenStates_AfterFadingOut();
982 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
984 static struct TitleFadingInfo fading_leave_stored;
987 fading_leave_stored = fading_leave;
989 fading = fading_leave_stored;
992 void FadeSetEnterMenu()
994 fading = menu.enter_menu;
996 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
999 void FadeSetLeaveMenu()
1001 fading = menu.leave_menu;
1003 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1006 void FadeSetEnterScreen()
1008 fading = menu.enter_screen[game_status];
1010 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
1013 void FadeSetNextScreen()
1015 fading = menu.next_screen[game_status];
1017 // (do not overwrite fade mode set by FadeSetEnterScreen)
1018 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1021 void FadeSetLeaveScreen()
1023 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
1026 void FadeSetFromType(int type)
1028 if (type & TYPE_ENTER_SCREEN)
1029 FadeSetEnterScreen();
1030 else if (type & TYPE_ENTER)
1032 else if (type & TYPE_LEAVE)
1036 void FadeSetDisabled()
1038 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1040 fading = fading_none;
1043 void FadeSkipNextFadeIn()
1045 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1048 void FadeSkipNextFadeOut()
1050 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1053 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1055 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1057 return (graphic == IMG_UNDEFINED ? NULL :
1058 graphic_info[graphic].bitmap != NULL || redefined ?
1059 graphic_info[graphic].bitmap :
1060 graphic_info[default_graphic].bitmap);
1063 Bitmap *getBackgroundBitmap(int graphic)
1065 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1068 Bitmap *getGlobalBorderBitmap(int graphic)
1070 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1073 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1076 (status == GAME_MODE_MAIN ||
1077 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1078 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1079 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1080 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1083 return getGlobalBorderBitmap(graphic);
1086 void SetWindowBackgroundImageIfDefined(int graphic)
1088 if (graphic_info[graphic].bitmap)
1089 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1092 void SetMainBackgroundImageIfDefined(int graphic)
1094 if (graphic_info[graphic].bitmap)
1095 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1098 void SetDoorBackgroundImageIfDefined(int graphic)
1100 if (graphic_info[graphic].bitmap)
1101 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1104 void SetWindowBackgroundImage(int graphic)
1106 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1109 void SetMainBackgroundImage(int graphic)
1111 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1114 void SetDoorBackgroundImage(int graphic)
1116 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1119 void SetPanelBackground()
1121 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1123 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1124 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1126 SetDoorBackgroundBitmap(bitmap_db_panel);
1129 void DrawBackground(int x, int y, int width, int height)
1131 /* "drawto" might still point to playfield buffer here (hall of fame) */
1132 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1134 if (IN_GFX_FIELD_FULL(x, y))
1135 redraw_mask |= REDRAW_FIELD;
1136 else if (IN_GFX_DOOR_1(x, y))
1137 redraw_mask |= REDRAW_DOOR_1;
1138 else if (IN_GFX_DOOR_2(x, y))
1139 redraw_mask |= REDRAW_DOOR_2;
1140 else if (IN_GFX_DOOR_3(x, y))
1141 redraw_mask |= REDRAW_DOOR_3;
1144 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1146 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1148 if (font->bitmap == NULL)
1151 DrawBackground(x, y, width, height);
1154 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1156 struct GraphicInfo *g = &graphic_info[graphic];
1158 if (g->bitmap == NULL)
1161 DrawBackground(x, y, width, height);
1164 static int game_status_last = -1;
1165 static Bitmap *global_border_bitmap_last = NULL;
1166 static Bitmap *global_border_bitmap = NULL;
1167 static int real_sx_last = -1, real_sy_last = -1;
1168 static int full_sxsize_last = -1, full_sysize_last = -1;
1169 static int dx_last = -1, dy_last = -1;
1170 static int dxsize_last = -1, dysize_last = -1;
1171 static int vx_last = -1, vy_last = -1;
1172 static int vxsize_last = -1, vysize_last = -1;
1174 boolean CheckIfGlobalBorderHasChanged()
1176 // if game status has not changed, global border has not changed either
1177 if (game_status == game_status_last)
1180 // determine and store new global border bitmap for current game status
1181 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1183 return (global_border_bitmap_last != global_border_bitmap);
1186 boolean CheckIfGlobalBorderRedrawIsNeeded()
1188 // if game status has not changed, nothing has to be redrawn
1189 if (game_status == game_status_last)
1192 // redraw if last screen was title screen
1193 if (game_status_last == GAME_MODE_TITLE)
1196 // redraw if global screen border has changed
1197 if (CheckIfGlobalBorderHasChanged())
1200 // redraw if position or size of playfield area has changed
1201 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1202 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1205 // redraw if position or size of door area has changed
1206 if (dx_last != DX || dy_last != DY ||
1207 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1210 // redraw if position or size of tape area has changed
1211 if (vx_last != VX || vy_last != VY ||
1212 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1218 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1221 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1223 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1226 void RedrawGlobalBorder()
1228 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1230 RedrawGlobalBorderFromBitmap(bitmap);
1232 redraw_mask = REDRAW_ALL;
1235 static void RedrawGlobalBorderIfNeeded()
1237 if (game_status == game_status_last)
1240 // copy current draw buffer to later copy back areas that have not changed
1241 if (game_status_last != GAME_MODE_TITLE)
1242 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1244 if (CheckIfGlobalBorderRedrawIsNeeded())
1246 // redraw global screen border (or clear, if defined to be empty)
1247 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1249 // copy previous playfield and door areas, if they are defined on both
1250 // previous and current screen and if they still have the same size
1252 if (real_sx_last != -1 && real_sy_last != -1 &&
1253 REAL_SX != -1 && REAL_SY != -1 &&
1254 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1255 BlitBitmap(bitmap_db_store_1, backbuffer,
1256 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1259 if (dx_last != -1 && dy_last != -1 &&
1260 DX != -1 && DY != -1 &&
1261 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1262 BlitBitmap(bitmap_db_store_1, backbuffer,
1263 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1265 if (vx_last != -1 && vy_last != -1 &&
1266 VX != -1 && VY != -1 &&
1267 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1268 BlitBitmap(bitmap_db_store_1, backbuffer,
1269 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1271 redraw_mask = REDRAW_ALL;
1274 game_status_last = game_status;
1276 global_border_bitmap_last = global_border_bitmap;
1278 real_sx_last = REAL_SX;
1279 real_sy_last = REAL_SY;
1280 full_sxsize_last = FULL_SXSIZE;
1281 full_sysize_last = FULL_SYSIZE;
1284 dxsize_last = DXSIZE;
1285 dysize_last = DYSIZE;
1288 vxsize_last = VXSIZE;
1289 vysize_last = VYSIZE;
1294 RedrawGlobalBorderIfNeeded();
1296 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1297 /* (when entering hall of fame after playing) */
1298 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1300 /* !!! maybe this should be done before clearing the background !!! */
1301 if (game_status == GAME_MODE_PLAYING)
1303 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1304 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1308 SetDrawtoField(DRAW_TO_BACKBUFFER);
1312 void MarkTileDirty(int x, int y)
1314 redraw_mask |= REDRAW_FIELD;
1317 void SetBorderElement()
1321 BorderElement = EL_EMPTY;
1323 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1325 for (x = 0; x < lev_fieldx; x++)
1327 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1328 BorderElement = EL_STEELWALL;
1330 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1336 void FloodFillLevel(int from_x, int from_y, int fill_element,
1337 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1338 int max_fieldx, int max_fieldy)
1342 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1343 static int safety = 0;
1345 /* check if starting field still has the desired content */
1346 if (field[from_x][from_y] == fill_element)
1351 if (safety > max_fieldx * max_fieldy)
1352 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1354 old_element = field[from_x][from_y];
1355 field[from_x][from_y] = fill_element;
1357 for (i = 0; i < 4; i++)
1359 x = from_x + check[i][0];
1360 y = from_y + check[i][1];
1362 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1363 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1369 void SetRandomAnimationValue(int x, int y)
1371 gfx.anim_random_frame = GfxRandom[x][y];
1374 int getGraphicAnimationFrame(int graphic, int sync_frame)
1376 /* animation synchronized with global frame counter, not move position */
1377 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1378 sync_frame = FrameCounter;
1380 return getAnimationFrame(graphic_info[graphic].anim_frames,
1381 graphic_info[graphic].anim_delay,
1382 graphic_info[graphic].anim_mode,
1383 graphic_info[graphic].anim_start_frame,
1387 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1389 struct GraphicInfo *g = &graphic_info[graphic];
1390 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1392 if (tilesize == gfx.standard_tile_size)
1393 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1394 else if (tilesize == game.tile_size)
1395 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1397 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1400 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1401 boolean get_backside)
1403 struct GraphicInfo *g = &graphic_info[graphic];
1404 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1405 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1407 if (g->offset_y == 0) /* frames are ordered horizontally */
1409 int max_width = g->anim_frames_per_line * g->width;
1410 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1412 *x = pos % max_width;
1413 *y = src_y % g->height + pos / max_width * g->height;
1415 else if (g->offset_x == 0) /* frames are ordered vertically */
1417 int max_height = g->anim_frames_per_line * g->height;
1418 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1420 *x = src_x % g->width + pos / max_height * g->width;
1421 *y = pos % max_height;
1423 else /* frames are ordered diagonally */
1425 *x = src_x + frame * g->offset_x;
1426 *y = src_y + frame * g->offset_y;
1430 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1431 Bitmap **bitmap, int *x, int *y,
1432 boolean get_backside)
1434 struct GraphicInfo *g = &graphic_info[graphic];
1436 // if no in-game graphics defined, always use standard graphic size
1437 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1438 tilesize = TILESIZE;
1440 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1441 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1443 *x = *x * tilesize / g->tile_size;
1444 *y = *y * tilesize / g->tile_size;
1447 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1448 int *x, int *y, boolean get_backside)
1450 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1454 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1455 Bitmap **bitmap, int *x, int *y)
1457 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1460 void getFixedGraphicSource(int graphic, int frame,
1461 Bitmap **bitmap, int *x, int *y)
1463 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1466 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1468 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1471 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1472 int *x, int *y, boolean get_backside)
1474 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1478 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1480 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1483 void DrawGraphic(int x, int y, int graphic, int frame)
1486 if (!IN_SCR_FIELD(x, y))
1488 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1489 printf("DrawGraphic(): This should never happen!\n");
1494 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1497 MarkTileDirty(x, y);
1500 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1503 if (!IN_SCR_FIELD(x, y))
1505 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1506 printf("DrawGraphic(): This should never happen!\n");
1511 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1513 MarkTileDirty(x, y);
1516 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1522 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1524 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1527 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1533 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1534 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1537 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1540 if (!IN_SCR_FIELD(x, y))
1542 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1543 printf("DrawGraphicThruMask(): This should never happen!\n");
1548 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1551 MarkTileDirty(x, y);
1554 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1557 if (!IN_SCR_FIELD(x, y))
1559 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1560 printf("DrawGraphicThruMask(): This should never happen!\n");
1565 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1567 MarkTileDirty(x, y);
1570 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1576 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1578 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1582 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1583 int graphic, int frame)
1588 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1590 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1594 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1596 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1598 MarkTileDirty(x / tilesize, y / tilesize);
1601 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1607 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1608 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1611 void DrawMiniGraphic(int x, int y, int graphic)
1613 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1614 MarkTileDirty(x / 2, y / 2);
1617 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1622 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1623 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1626 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1627 int graphic, int frame,
1628 int cut_mode, int mask_mode)
1633 int width = TILEX, height = TILEY;
1636 if (dx || dy) /* shifted graphic */
1638 if (x < BX1) /* object enters playfield from the left */
1645 else if (x > BX2) /* object enters playfield from the right */
1651 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1657 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1659 else if (dx) /* general horizontal movement */
1660 MarkTileDirty(x + SIGN(dx), y);
1662 if (y < BY1) /* object enters playfield from the top */
1664 if (cut_mode == CUT_BELOW) /* object completely above top border */
1672 else if (y > BY2) /* object enters playfield from the bottom */
1678 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1684 else if (dy > 0 && cut_mode == CUT_ABOVE)
1686 if (y == BY2) /* object completely above bottom border */
1692 MarkTileDirty(x, y + 1);
1693 } /* object leaves playfield to the bottom */
1694 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1696 else if (dy) /* general vertical movement */
1697 MarkTileDirty(x, y + SIGN(dy));
1701 if (!IN_SCR_FIELD(x, y))
1703 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1704 printf("DrawGraphicShifted(): This should never happen!\n");
1709 width = width * TILESIZE_VAR / TILESIZE;
1710 height = height * TILESIZE_VAR / TILESIZE;
1711 cx = cx * TILESIZE_VAR / TILESIZE;
1712 cy = cy * TILESIZE_VAR / TILESIZE;
1713 dx = dx * TILESIZE_VAR / TILESIZE;
1714 dy = dy * TILESIZE_VAR / TILESIZE;
1716 if (width > 0 && height > 0)
1718 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1723 dst_x = FX + x * TILEX_VAR + dx;
1724 dst_y = FY + y * TILEY_VAR + dy;
1726 if (mask_mode == USE_MASKING)
1727 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1730 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1733 MarkTileDirty(x, y);
1737 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1738 int graphic, int frame,
1739 int cut_mode, int mask_mode)
1744 int width = TILEX_VAR, height = TILEY_VAR;
1747 int x2 = x + SIGN(dx);
1748 int y2 = y + SIGN(dy);
1750 /* movement with two-tile animations must be sync'ed with movement position,
1751 not with current GfxFrame (which can be higher when using slow movement) */
1752 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1753 int anim_frames = graphic_info[graphic].anim_frames;
1755 /* (we also need anim_delay here for movement animations with less frames) */
1756 int anim_delay = graphic_info[graphic].anim_delay;
1757 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1759 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1760 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1762 /* re-calculate animation frame for two-tile movement animation */
1763 frame = getGraphicAnimationFrame(graphic, sync_frame);
1765 /* check if movement start graphic inside screen area and should be drawn */
1766 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1768 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1770 dst_x = FX + x1 * TILEX_VAR;
1771 dst_y = FY + y1 * TILEY_VAR;
1773 if (mask_mode == USE_MASKING)
1774 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1777 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1780 MarkTileDirty(x1, y1);
1783 /* check if movement end graphic inside screen area and should be drawn */
1784 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1786 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1788 dst_x = FX + x2 * TILEX_VAR;
1789 dst_y = FY + y2 * TILEY_VAR;
1791 if (mask_mode == USE_MASKING)
1792 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1795 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1798 MarkTileDirty(x2, y2);
1802 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1803 int graphic, int frame,
1804 int cut_mode, int mask_mode)
1808 DrawGraphic(x, y, graphic, frame);
1813 if (graphic_info[graphic].double_movement) /* EM style movement images */
1814 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1816 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1819 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1820 int frame, int cut_mode)
1822 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1825 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1826 int cut_mode, int mask_mode)
1828 int lx = LEVELX(x), ly = LEVELY(y);
1832 if (IN_LEV_FIELD(lx, ly))
1834 SetRandomAnimationValue(lx, ly);
1836 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1837 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1839 /* do not use double (EM style) movement graphic when not moving */
1840 if (graphic_info[graphic].double_movement && !dx && !dy)
1842 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1843 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1846 else /* border element */
1848 graphic = el2img(element);
1849 frame = getGraphicAnimationFrame(graphic, -1);
1852 if (element == EL_EXPANDABLE_WALL)
1854 boolean left_stopped = FALSE, right_stopped = FALSE;
1856 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1857 left_stopped = TRUE;
1858 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1859 right_stopped = TRUE;
1861 if (left_stopped && right_stopped)
1863 else if (left_stopped)
1865 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1866 frame = graphic_info[graphic].anim_frames - 1;
1868 else if (right_stopped)
1870 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1871 frame = graphic_info[graphic].anim_frames - 1;
1876 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1877 else if (mask_mode == USE_MASKING)
1878 DrawGraphicThruMask(x, y, graphic, frame);
1880 DrawGraphic(x, y, graphic, frame);
1883 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1884 int cut_mode, int mask_mode)
1886 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1887 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1888 cut_mode, mask_mode);
1891 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1894 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1897 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1900 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1903 void DrawLevelElementThruMask(int x, int y, int element)
1905 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1908 void DrawLevelFieldThruMask(int x, int y)
1910 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1913 /* !!! implementation of quicksand is totally broken !!! */
1914 #define IS_CRUMBLED_TILE(x, y, e) \
1915 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1916 !IS_MOVING(x, y) || \
1917 (e) == EL_QUICKSAND_EMPTYING || \
1918 (e) == EL_QUICKSAND_FAST_EMPTYING))
1920 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1925 int width, height, cx, cy;
1926 int sx = SCREENX(x), sy = SCREENY(y);
1927 int crumbled_border_size = graphic_info[graphic].border_size;
1928 int crumbled_tile_size = graphic_info[graphic].tile_size;
1929 int crumbled_border_size_var =
1930 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
1933 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1935 for (i = 1; i < 4; i++)
1937 int dxx = (i & 1 ? dx : 0);
1938 int dyy = (i & 2 ? dy : 0);
1941 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1944 /* check if neighbour field is of same crumble type */
1945 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1946 graphic_info[graphic].class ==
1947 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1949 /* return if check prevents inner corner */
1950 if (same == (dxx == dx && dyy == dy))
1954 /* if we reach this point, we have an inner corner */
1956 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1958 width = crumbled_border_size_var;
1959 height = crumbled_border_size_var;
1960 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1961 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1963 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1964 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1967 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1972 int width, height, bx, by, cx, cy;
1973 int sx = SCREENX(x), sy = SCREENY(y);
1974 int crumbled_border_size = graphic_info[graphic].border_size;
1975 int crumbled_tile_size = graphic_info[graphic].tile_size;
1976 int crumbled_border_size_var =
1977 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
1978 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1981 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1983 /* draw simple, sloppy, non-corner-accurate crumbled border */
1985 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1986 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1987 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1988 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1990 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1991 FX + sx * TILEX_VAR + cx,
1992 FY + sy * TILEY_VAR + cy);
1994 /* (remaining middle border part must be at least as big as corner part) */
1995 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1996 crumbled_border_size_var >= TILESIZE_VAR / 3)
1999 /* correct corners of crumbled border, if needed */
2001 for (i = -1; i <= 1; i += 2)
2003 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2004 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2005 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2008 /* check if neighbour field is of same crumble type */
2009 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2010 graphic_info[graphic].class ==
2011 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2013 /* no crumbled corner, but continued crumbled border */
2015 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2016 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2017 int b1 = (i == 1 ? crumbled_border_size_var :
2018 TILESIZE_VAR - 2 * crumbled_border_size_var);
2020 width = crumbled_border_size_var;
2021 height = crumbled_border_size_var;
2023 if (dir == 1 || dir == 2)
2038 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2040 FX + sx * TILEX_VAR + cx,
2041 FY + sy * TILEY_VAR + cy);
2046 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2048 int sx = SCREENX(x), sy = SCREENY(y);
2051 static int xy[4][2] =
2059 if (!IN_LEV_FIELD(x, y))
2062 element = TILE_GFX_ELEMENT(x, y);
2064 if (IS_CRUMBLED_TILE(x, y, element)) /* crumble field itself */
2066 if (!IN_SCR_FIELD(sx, sy))
2069 /* crumble field borders towards direct neighbour fields */
2070 for (i = 0; i < 4; i++)
2072 int xx = x + xy[i][0];
2073 int yy = y + xy[i][1];
2075 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2078 /* check if neighbour field is of same crumble type */
2079 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2080 graphic_info[graphic].class ==
2081 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2084 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2087 /* crumble inner field corners towards corner neighbour fields */
2088 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2089 graphic_info[graphic].anim_frames == 2)
2091 for (i = 0; i < 4; i++)
2093 int dx = (i & 1 ? +1 : -1);
2094 int dy = (i & 2 ? +1 : -1);
2096 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2100 MarkTileDirty(sx, sy);
2102 else /* center field is not crumbled -- crumble neighbour fields */
2104 /* crumble field borders of direct neighbour fields */
2105 for (i = 0; i < 4; i++)
2107 int xx = x + xy[i][0];
2108 int yy = y + xy[i][1];
2109 int sxx = sx + xy[i][0];
2110 int syy = sy + xy[i][1];
2112 if (!IN_LEV_FIELD(xx, yy) ||
2113 !IN_SCR_FIELD(sxx, syy))
2116 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2119 element = TILE_GFX_ELEMENT(xx, yy);
2121 if (!IS_CRUMBLED_TILE(xx, yy, element))
2124 graphic = el_act2crm(element, ACTION_DEFAULT);
2126 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2128 MarkTileDirty(sxx, syy);
2131 /* crumble inner field corners of corner neighbour fields */
2132 for (i = 0; i < 4; i++)
2134 int dx = (i & 1 ? +1 : -1);
2135 int dy = (i & 2 ? +1 : -1);
2141 if (!IN_LEV_FIELD(xx, yy) ||
2142 !IN_SCR_FIELD(sxx, syy))
2145 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2148 element = TILE_GFX_ELEMENT(xx, yy);
2150 if (!IS_CRUMBLED_TILE(xx, yy, element))
2153 graphic = el_act2crm(element, ACTION_DEFAULT);
2155 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2156 graphic_info[graphic].anim_frames == 2)
2157 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2159 MarkTileDirty(sxx, syy);
2164 void DrawLevelFieldCrumbled(int x, int y)
2168 if (!IN_LEV_FIELD(x, y))
2171 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2172 GfxElement[x][y] != EL_UNDEFINED &&
2173 GFX_CRUMBLED(GfxElement[x][y]))
2175 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2180 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2182 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2185 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2188 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2189 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2190 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2191 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2192 int sx = SCREENX(x), sy = SCREENY(y);
2194 DrawGraphic(sx, sy, graphic1, frame1);
2195 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2198 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2200 int sx = SCREENX(x), sy = SCREENY(y);
2201 static int xy[4][2] =
2210 /* crumble direct neighbour fields (required for field borders) */
2211 for (i = 0; i < 4; i++)
2213 int xx = x + xy[i][0];
2214 int yy = y + xy[i][1];
2215 int sxx = sx + xy[i][0];
2216 int syy = sy + xy[i][1];
2218 if (!IN_LEV_FIELD(xx, yy) ||
2219 !IN_SCR_FIELD(sxx, syy) ||
2220 !GFX_CRUMBLED(Feld[xx][yy]) ||
2224 DrawLevelField(xx, yy);
2227 /* crumble corner neighbour fields (required for inner field corners) */
2228 for (i = 0; i < 4; i++)
2230 int dx = (i & 1 ? +1 : -1);
2231 int dy = (i & 2 ? +1 : -1);
2237 if (!IN_LEV_FIELD(xx, yy) ||
2238 !IN_SCR_FIELD(sxx, syy) ||
2239 !GFX_CRUMBLED(Feld[xx][yy]) ||
2243 int element = TILE_GFX_ELEMENT(xx, yy);
2244 int graphic = el_act2crm(element, ACTION_DEFAULT);
2246 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2247 graphic_info[graphic].anim_frames == 2)
2248 DrawLevelField(xx, yy);
2252 static int getBorderElement(int x, int y)
2256 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2257 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2258 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2259 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2260 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2261 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2262 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2264 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2265 int steel_position = (x == -1 && y == -1 ? 0 :
2266 x == lev_fieldx && y == -1 ? 1 :
2267 x == -1 && y == lev_fieldy ? 2 :
2268 x == lev_fieldx && y == lev_fieldy ? 3 :
2269 x == -1 || x == lev_fieldx ? 4 :
2270 y == -1 || y == lev_fieldy ? 5 : 6);
2272 return border[steel_position][steel_type];
2275 void DrawScreenElement(int x, int y, int element)
2277 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2278 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2281 void DrawLevelElement(int x, int y, int element)
2283 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2284 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2287 void DrawScreenField(int x, int y)
2289 int lx = LEVELX(x), ly = LEVELY(y);
2290 int element, content;
2292 if (!IN_LEV_FIELD(lx, ly))
2294 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2297 element = getBorderElement(lx, ly);
2299 DrawScreenElement(x, y, element);
2304 element = Feld[lx][ly];
2305 content = Store[lx][ly];
2307 if (IS_MOVING(lx, ly))
2309 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2310 boolean cut_mode = NO_CUTTING;
2312 if (element == EL_QUICKSAND_EMPTYING ||
2313 element == EL_QUICKSAND_FAST_EMPTYING ||
2314 element == EL_MAGIC_WALL_EMPTYING ||
2315 element == EL_BD_MAGIC_WALL_EMPTYING ||
2316 element == EL_DC_MAGIC_WALL_EMPTYING ||
2317 element == EL_AMOEBA_DROPPING)
2318 cut_mode = CUT_ABOVE;
2319 else if (element == EL_QUICKSAND_FILLING ||
2320 element == EL_QUICKSAND_FAST_FILLING ||
2321 element == EL_MAGIC_WALL_FILLING ||
2322 element == EL_BD_MAGIC_WALL_FILLING ||
2323 element == EL_DC_MAGIC_WALL_FILLING)
2324 cut_mode = CUT_BELOW;
2326 if (cut_mode == CUT_ABOVE)
2327 DrawScreenElement(x, y, element);
2329 DrawScreenElement(x, y, EL_EMPTY);
2332 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2333 else if (cut_mode == NO_CUTTING)
2334 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2337 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2339 if (cut_mode == CUT_BELOW &&
2340 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2341 DrawLevelElement(lx, ly + 1, element);
2344 if (content == EL_ACID)
2346 int dir = MovDir[lx][ly];
2347 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2348 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2350 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2352 // prevent target field from being drawn again (but without masking)
2353 // (this would happen if target field is scanned after moving element)
2354 Stop[newlx][newly] = TRUE;
2357 else if (IS_BLOCKED(lx, ly))
2362 boolean cut_mode = NO_CUTTING;
2363 int element_old, content_old;
2365 Blocked2Moving(lx, ly, &oldx, &oldy);
2368 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2369 MovDir[oldx][oldy] == MV_RIGHT);
2371 element_old = Feld[oldx][oldy];
2372 content_old = Store[oldx][oldy];
2374 if (element_old == EL_QUICKSAND_EMPTYING ||
2375 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2376 element_old == EL_MAGIC_WALL_EMPTYING ||
2377 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2378 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2379 element_old == EL_AMOEBA_DROPPING)
2380 cut_mode = CUT_ABOVE;
2382 DrawScreenElement(x, y, EL_EMPTY);
2385 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2387 else if (cut_mode == NO_CUTTING)
2388 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2391 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2394 else if (IS_DRAWABLE(element))
2395 DrawScreenElement(x, y, element);
2397 DrawScreenElement(x, y, EL_EMPTY);
2400 void DrawLevelField(int x, int y)
2402 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2403 DrawScreenField(SCREENX(x), SCREENY(y));
2404 else if (IS_MOVING(x, y))
2408 Moving2Blocked(x, y, &newx, &newy);
2409 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2410 DrawScreenField(SCREENX(newx), SCREENY(newy));
2412 else if (IS_BLOCKED(x, y))
2416 Blocked2Moving(x, y, &oldx, &oldy);
2417 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2418 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2422 void DrawSizedElement(int x, int y, int element, int tilesize)
2426 graphic = el2edimg(element);
2427 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2430 void DrawMiniElement(int x, int y, int element)
2434 graphic = el2edimg(element);
2435 DrawMiniGraphic(x, y, graphic);
2438 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2441 int x = sx + scroll_x, y = sy + scroll_y;
2443 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2444 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2445 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2446 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2448 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2451 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2453 int x = sx + scroll_x, y = sy + scroll_y;
2455 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2456 DrawMiniElement(sx, sy, EL_EMPTY);
2457 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2458 DrawMiniElement(sx, sy, Feld[x][y]);
2460 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2463 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2464 int x, int y, int xsize, int ysize,
2465 int tile_width, int tile_height)
2469 int dst_x = startx + x * tile_width;
2470 int dst_y = starty + y * tile_height;
2471 int width = graphic_info[graphic].width;
2472 int height = graphic_info[graphic].height;
2473 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2474 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2475 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2476 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2477 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2478 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2479 boolean draw_masked = graphic_info[graphic].draw_masked;
2481 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2483 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2485 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2489 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2490 inner_sx + (x - 1) * tile_width % inner_width);
2491 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2492 inner_sy + (y - 1) * tile_height % inner_height);
2495 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2498 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2502 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2503 int x, int y, int xsize, int ysize, int font_nr)
2505 int font_width = getFontWidth(font_nr);
2506 int font_height = getFontHeight(font_nr);
2508 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2509 font_width, font_height);
2512 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2514 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2515 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2516 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2517 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2518 boolean no_delay = (tape.warp_forward);
2519 unsigned int anim_delay = 0;
2520 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2521 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2522 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2523 int font_width = getFontWidth(font_nr);
2524 int font_height = getFontHeight(font_nr);
2525 int max_xsize = level.envelope[envelope_nr].xsize;
2526 int max_ysize = level.envelope[envelope_nr].ysize;
2527 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2528 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2529 int xend = max_xsize;
2530 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2531 int xstep = (xstart < xend ? 1 : 0);
2532 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2534 int end = MAX(xend - xstart, yend - ystart);
2537 for (i = start; i <= end; i++)
2539 int last_frame = end; // last frame of this "for" loop
2540 int x = xstart + i * xstep;
2541 int y = ystart + i * ystep;
2542 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2543 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2544 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2545 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2548 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2550 BlitScreenToBitmap(backbuffer);
2552 SetDrawtoField(DRAW_TO_BACKBUFFER);
2554 for (yy = 0; yy < ysize; yy++)
2555 for (xx = 0; xx < xsize; xx++)
2556 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2558 DrawTextBuffer(sx + font_width, sy + font_height,
2559 level.envelope[envelope_nr].text, font_nr, max_xsize,
2560 xsize - 2, ysize - 2, 0, mask_mode,
2561 level.envelope[envelope_nr].autowrap,
2562 level.envelope[envelope_nr].centered, FALSE);
2564 redraw_mask |= REDRAW_FIELD;
2567 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2571 void ShowEnvelope(int envelope_nr)
2573 int element = EL_ENVELOPE_1 + envelope_nr;
2574 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2575 int sound_opening = element_info[element].sound[ACTION_OPENING];
2576 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2577 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2578 boolean no_delay = (tape.warp_forward);
2579 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2580 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2581 int anim_mode = graphic_info[graphic].anim_mode;
2582 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2583 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2585 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2587 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2589 if (anim_mode == ANIM_DEFAULT)
2590 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2592 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2595 Delay(wait_delay_value);
2597 WaitForEventToContinue();
2599 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2601 if (anim_mode != ANIM_NONE)
2602 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2604 if (anim_mode == ANIM_DEFAULT)
2605 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2607 game.envelope_active = FALSE;
2609 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2611 redraw_mask |= REDRAW_FIELD;
2615 static void setRequestBasePosition(int *x, int *y)
2617 int sx_base, sy_base;
2619 if (request.x != -1)
2620 sx_base = request.x;
2621 else if (request.align == ALIGN_LEFT)
2623 else if (request.align == ALIGN_RIGHT)
2624 sx_base = SX + SXSIZE;
2626 sx_base = SX + SXSIZE / 2;
2628 if (request.y != -1)
2629 sy_base = request.y;
2630 else if (request.valign == VALIGN_TOP)
2632 else if (request.valign == VALIGN_BOTTOM)
2633 sy_base = SY + SYSIZE;
2635 sy_base = SY + SYSIZE / 2;
2641 static void setRequestPositionExt(int *x, int *y, int width, int height,
2642 boolean add_border_size)
2644 int border_size = request.border_size;
2645 int sx_base, sy_base;
2648 setRequestBasePosition(&sx_base, &sy_base);
2650 if (request.align == ALIGN_LEFT)
2652 else if (request.align == ALIGN_RIGHT)
2653 sx = sx_base - width;
2655 sx = sx_base - width / 2;
2657 if (request.valign == VALIGN_TOP)
2659 else if (request.valign == VALIGN_BOTTOM)
2660 sy = sy_base - height;
2662 sy = sy_base - height / 2;
2664 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2665 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2667 if (add_border_size)
2677 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2679 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2682 void DrawEnvelopeRequest(char *text)
2684 char *text_final = text;
2685 char *text_door_style = NULL;
2686 int graphic = IMG_BACKGROUND_REQUEST;
2687 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2688 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2689 int font_nr = FONT_REQUEST;
2690 int font_width = getFontWidth(font_nr);
2691 int font_height = getFontHeight(font_nr);
2692 int border_size = request.border_size;
2693 int line_spacing = request.line_spacing;
2694 int line_height = font_height + line_spacing;
2695 int max_text_width = request.width - 2 * border_size;
2696 int max_text_height = request.height - 2 * border_size;
2697 int line_length = max_text_width / font_width;
2698 int max_lines = max_text_height / line_height;
2699 int text_width = line_length * font_width;
2700 int width = request.width;
2701 int height = request.height;
2702 int tile_size = MAX(request.step_offset, 1);
2703 int x_steps = width / tile_size;
2704 int y_steps = height / tile_size;
2705 int sx_offset = border_size;
2706 int sy_offset = border_size;
2710 if (request.centered)
2711 sx_offset = (request.width - text_width) / 2;
2713 if (request.wrap_single_words && !request.autowrap)
2715 char *src_text_ptr, *dst_text_ptr;
2717 text_door_style = checked_malloc(2 * strlen(text) + 1);
2719 src_text_ptr = text;
2720 dst_text_ptr = text_door_style;
2722 while (*src_text_ptr)
2724 if (*src_text_ptr == ' ' ||
2725 *src_text_ptr == '?' ||
2726 *src_text_ptr == '!')
2727 *dst_text_ptr++ = '\n';
2729 if (*src_text_ptr != ' ')
2730 *dst_text_ptr++ = *src_text_ptr;
2735 *dst_text_ptr = '\0';
2737 text_final = text_door_style;
2740 setRequestPosition(&sx, &sy, FALSE);
2742 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2744 for (y = 0; y < y_steps; y++)
2745 for (x = 0; x < x_steps; x++)
2746 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2747 x, y, x_steps, y_steps,
2748 tile_size, tile_size);
2750 /* force DOOR font inside door area */
2751 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2753 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2754 line_length, -1, max_lines, line_spacing, mask_mode,
2755 request.autowrap, request.centered, FALSE);
2759 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2760 RedrawGadget(tool_gadget[i]);
2762 // store readily prepared envelope request for later use when animating
2763 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2765 if (text_door_style)
2766 free(text_door_style);
2769 void AnimateEnvelopeRequest(int anim_mode, int action)
2771 int graphic = IMG_BACKGROUND_REQUEST;
2772 boolean draw_masked = graphic_info[graphic].draw_masked;
2773 int delay_value_normal = request.step_delay;
2774 int delay_value_fast = delay_value_normal / 2;
2775 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2776 boolean no_delay = (tape.warp_forward);
2777 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2778 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2779 unsigned int anim_delay = 0;
2781 int tile_size = MAX(request.step_offset, 1);
2782 int max_xsize = request.width / tile_size;
2783 int max_ysize = request.height / tile_size;
2784 int max_xsize_inner = max_xsize - 2;
2785 int max_ysize_inner = max_ysize - 2;
2787 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2788 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2789 int xend = max_xsize_inner;
2790 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2791 int xstep = (xstart < xend ? 1 : 0);
2792 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2794 int end = MAX(xend - xstart, yend - ystart);
2797 if (setup.quick_doors)
2804 for (i = start; i <= end; i++)
2806 int last_frame = end; // last frame of this "for" loop
2807 int x = xstart + i * xstep;
2808 int y = ystart + i * ystep;
2809 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2810 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2811 int xsize_size_left = (xsize - 1) * tile_size;
2812 int ysize_size_top = (ysize - 1) * tile_size;
2813 int max_xsize_pos = (max_xsize - 1) * tile_size;
2814 int max_ysize_pos = (max_ysize - 1) * tile_size;
2815 int width = xsize * tile_size;
2816 int height = ysize * tile_size;
2821 setRequestPosition(&src_x, &src_y, FALSE);
2822 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2824 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2826 for (yy = 0; yy < 2; yy++)
2828 for (xx = 0; xx < 2; xx++)
2830 int src_xx = src_x + xx * max_xsize_pos;
2831 int src_yy = src_y + yy * max_ysize_pos;
2832 int dst_xx = dst_x + xx * xsize_size_left;
2833 int dst_yy = dst_y + yy * ysize_size_top;
2834 int xx_size = (xx ? tile_size : xsize_size_left);
2835 int yy_size = (yy ? tile_size : ysize_size_top);
2838 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
2839 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2841 BlitBitmap(bitmap_db_store_2, backbuffer,
2842 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2846 redraw_mask |= REDRAW_FIELD;
2850 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2854 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2856 int graphic = IMG_BACKGROUND_REQUEST;
2857 int sound_opening = SND_REQUEST_OPENING;
2858 int sound_closing = SND_REQUEST_CLOSING;
2859 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2860 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2861 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2862 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2863 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2865 if (game_status == GAME_MODE_PLAYING)
2866 BlitScreenToBitmap(backbuffer);
2868 SetDrawtoField(DRAW_TO_BACKBUFFER);
2870 // SetDrawBackgroundMask(REDRAW_NONE);
2872 if (action == ACTION_OPENING)
2874 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2876 if (req_state & REQ_ASK)
2878 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2879 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2881 else if (req_state & REQ_CONFIRM)
2883 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2885 else if (req_state & REQ_PLAYER)
2887 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2888 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2889 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2890 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2893 DrawEnvelopeRequest(text);
2896 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2898 if (action == ACTION_OPENING)
2900 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2902 if (anim_mode == ANIM_DEFAULT)
2903 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2905 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2909 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2911 if (anim_mode != ANIM_NONE)
2912 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2914 if (anim_mode == ANIM_DEFAULT)
2915 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2918 game.envelope_active = FALSE;
2920 if (action == ACTION_CLOSING)
2921 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2923 // SetDrawBackgroundMask(last_draw_background_mask);
2925 redraw_mask |= REDRAW_FIELD;
2929 if (action == ACTION_CLOSING &&
2930 game_status == GAME_MODE_PLAYING &&
2931 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2932 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2935 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2939 int graphic = el2preimg(element);
2941 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2942 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2945 void DrawLevel(int draw_background_mask)
2949 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2950 SetDrawBackgroundMask(draw_background_mask);
2954 for (x = BX1; x <= BX2; x++)
2955 for (y = BY1; y <= BY2; y++)
2956 DrawScreenField(x, y);
2958 redraw_mask |= REDRAW_FIELD;
2961 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2966 for (x = 0; x < size_x; x++)
2967 for (y = 0; y < size_y; y++)
2968 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2970 redraw_mask |= REDRAW_FIELD;
2973 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2977 for (x = 0; x < size_x; x++)
2978 for (y = 0; y < size_y; y++)
2979 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2981 redraw_mask |= REDRAW_FIELD;
2984 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
2986 boolean show_level_border = (BorderElement != EL_EMPTY);
2987 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2988 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2989 int tile_size = preview.tile_size;
2990 int preview_width = preview.xsize * tile_size;
2991 int preview_height = preview.ysize * tile_size;
2992 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2993 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2994 int real_preview_width = real_preview_xsize * tile_size;
2995 int real_preview_height = real_preview_ysize * tile_size;
2996 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2997 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3000 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3003 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3005 dst_x += (preview_width - real_preview_width) / 2;
3006 dst_y += (preview_height - real_preview_height) / 2;
3008 for (x = 0; x < real_preview_xsize; x++)
3010 for (y = 0; y < real_preview_ysize; y++)
3012 int lx = from_x + x + (show_level_border ? -1 : 0);
3013 int ly = from_y + y + (show_level_border ? -1 : 0);
3014 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3015 getBorderElement(lx, ly));
3017 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3018 element, tile_size);
3022 redraw_mask |= REDRAW_FIELD;
3025 #define MICROLABEL_EMPTY 0
3026 #define MICROLABEL_LEVEL_NAME 1
3027 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3028 #define MICROLABEL_LEVEL_AUTHOR 3
3029 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3030 #define MICROLABEL_IMPORTED_FROM 5
3031 #define MICROLABEL_IMPORTED_BY_HEAD 6
3032 #define MICROLABEL_IMPORTED_BY 7
3034 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3036 int max_text_width = SXSIZE;
3037 int font_width = getFontWidth(font_nr);
3039 if (pos->align == ALIGN_CENTER)
3040 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3041 else if (pos->align == ALIGN_RIGHT)
3042 max_text_width = pos->x;
3044 max_text_width = SXSIZE - pos->x;
3046 return max_text_width / font_width;
3049 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3051 char label_text[MAX_OUTPUT_LINESIZE + 1];
3052 int max_len_label_text;
3053 int font_nr = pos->font;
3056 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3059 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3060 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3061 mode == MICROLABEL_IMPORTED_BY_HEAD)
3062 font_nr = pos->font_alt;
3064 max_len_label_text = getMaxTextLength(pos, font_nr);
3066 if (pos->size != -1)
3067 max_len_label_text = pos->size;
3069 for (i = 0; i < max_len_label_text; i++)
3070 label_text[i] = ' ';
3071 label_text[max_len_label_text] = '\0';
3073 if (strlen(label_text) > 0)
3074 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3077 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3078 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3079 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3080 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3081 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3082 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3083 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3084 max_len_label_text);
3085 label_text[max_len_label_text] = '\0';
3087 if (strlen(label_text) > 0)
3088 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3090 redraw_mask |= REDRAW_FIELD;
3093 static void DrawPreviewLevelLabel(int mode)
3095 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3098 static void DrawPreviewLevelInfo(int mode)
3100 if (mode == MICROLABEL_LEVEL_NAME)
3101 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3102 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3103 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3106 static void DrawPreviewLevelExt(boolean restart)
3108 static unsigned int scroll_delay = 0;
3109 static unsigned int label_delay = 0;
3110 static int from_x, from_y, scroll_direction;
3111 static int label_state, label_counter;
3112 unsigned int scroll_delay_value = preview.step_delay;
3113 boolean show_level_border = (BorderElement != EL_EMPTY);
3114 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3115 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3122 if (preview.anim_mode == ANIM_CENTERED)
3124 if (level_xsize > preview.xsize)
3125 from_x = (level_xsize - preview.xsize) / 2;
3126 if (level_ysize > preview.ysize)
3127 from_y = (level_ysize - preview.ysize) / 2;
3130 from_x += preview.xoffset;
3131 from_y += preview.yoffset;
3133 scroll_direction = MV_RIGHT;
3137 DrawPreviewLevelPlayfield(from_x, from_y);
3138 DrawPreviewLevelLabel(label_state);
3140 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3141 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3143 /* initialize delay counters */
3144 DelayReached(&scroll_delay, 0);
3145 DelayReached(&label_delay, 0);
3147 if (leveldir_current->name)
3149 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3150 char label_text[MAX_OUTPUT_LINESIZE + 1];
3151 int font_nr = pos->font;
3152 int max_len_label_text = getMaxTextLength(pos, font_nr);
3154 if (pos->size != -1)
3155 max_len_label_text = pos->size;
3157 strncpy(label_text, leveldir_current->name, max_len_label_text);
3158 label_text[max_len_label_text] = '\0';
3160 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3161 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3167 /* scroll preview level, if needed */
3168 if (preview.anim_mode != ANIM_NONE &&
3169 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3170 DelayReached(&scroll_delay, scroll_delay_value))
3172 switch (scroll_direction)
3177 from_x -= preview.step_offset;
3178 from_x = (from_x < 0 ? 0 : from_x);
3181 scroll_direction = MV_UP;
3185 if (from_x < level_xsize - preview.xsize)
3187 from_x += preview.step_offset;
3188 from_x = (from_x > level_xsize - preview.xsize ?
3189 level_xsize - preview.xsize : from_x);
3192 scroll_direction = MV_DOWN;
3198 from_y -= preview.step_offset;
3199 from_y = (from_y < 0 ? 0 : from_y);
3202 scroll_direction = MV_RIGHT;
3206 if (from_y < level_ysize - preview.ysize)
3208 from_y += preview.step_offset;
3209 from_y = (from_y > level_ysize - preview.ysize ?
3210 level_ysize - preview.ysize : from_y);
3213 scroll_direction = MV_LEFT;
3220 DrawPreviewLevelPlayfield(from_x, from_y);
3223 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
3224 /* redraw micro level label, if needed */
3225 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3226 !strEqual(level.author, ANONYMOUS_NAME) &&
3227 !strEqual(level.author, leveldir_current->name) &&
3228 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3230 int max_label_counter = 23;
3232 if (leveldir_current->imported_from != NULL &&
3233 strlen(leveldir_current->imported_from) > 0)
3234 max_label_counter += 14;
3235 if (leveldir_current->imported_by != NULL &&
3236 strlen(leveldir_current->imported_by) > 0)
3237 max_label_counter += 14;
3239 label_counter = (label_counter + 1) % max_label_counter;
3240 label_state = (label_counter >= 0 && label_counter <= 7 ?
3241 MICROLABEL_LEVEL_NAME :
3242 label_counter >= 9 && label_counter <= 12 ?
3243 MICROLABEL_LEVEL_AUTHOR_HEAD :
3244 label_counter >= 14 && label_counter <= 21 ?
3245 MICROLABEL_LEVEL_AUTHOR :
3246 label_counter >= 23 && label_counter <= 26 ?
3247 MICROLABEL_IMPORTED_FROM_HEAD :
3248 label_counter >= 28 && label_counter <= 35 ?
3249 MICROLABEL_IMPORTED_FROM :
3250 label_counter >= 37 && label_counter <= 40 ?
3251 MICROLABEL_IMPORTED_BY_HEAD :
3252 label_counter >= 42 && label_counter <= 49 ?
3253 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3255 if (leveldir_current->imported_from == NULL &&
3256 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3257 label_state == MICROLABEL_IMPORTED_FROM))
3258 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3259 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3261 DrawPreviewLevelLabel(label_state);
3265 void DrawPreviewLevelInitial()
3267 DrawPreviewLevelExt(TRUE);
3270 void DrawPreviewLevelAnimation()
3272 DrawPreviewLevelExt(FALSE);
3275 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3276 int graphic, int sync_frame,
3279 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3281 if (mask_mode == USE_MASKING)
3282 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3284 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3287 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3288 int graphic, int sync_frame, int mask_mode)
3290 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3292 if (mask_mode == USE_MASKING)
3293 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3295 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3298 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3300 int lx = LEVELX(x), ly = LEVELY(y);
3302 if (!IN_SCR_FIELD(x, y))
3305 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3306 graphic, GfxFrame[lx][ly], NO_MASKING);
3308 MarkTileDirty(x, y);
3311 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3313 int lx = LEVELX(x), ly = LEVELY(y);
3315 if (!IN_SCR_FIELD(x, y))
3318 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3319 graphic, GfxFrame[lx][ly], NO_MASKING);
3320 MarkTileDirty(x, y);
3323 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3325 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3328 void DrawLevelElementAnimation(int x, int y, int element)
3330 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3332 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3335 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3337 int sx = SCREENX(x), sy = SCREENY(y);
3339 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3342 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3345 DrawGraphicAnimation(sx, sy, graphic);
3348 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3349 DrawLevelFieldCrumbled(x, y);
3351 if (GFX_CRUMBLED(Feld[x][y]))
3352 DrawLevelFieldCrumbled(x, y);
3356 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3358 int sx = SCREENX(x), sy = SCREENY(y);
3361 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3364 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3366 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3369 DrawGraphicAnimation(sx, sy, graphic);
3371 if (GFX_CRUMBLED(element))
3372 DrawLevelFieldCrumbled(x, y);
3375 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3377 if (player->use_murphy)
3379 /* this works only because currently only one player can be "murphy" ... */
3380 static int last_horizontal_dir = MV_LEFT;
3381 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3383 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3384 last_horizontal_dir = move_dir;
3386 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3388 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3390 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3396 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3399 static boolean equalGraphics(int graphic1, int graphic2)
3401 struct GraphicInfo *g1 = &graphic_info[graphic1];
3402 struct GraphicInfo *g2 = &graphic_info[graphic2];
3404 return (g1->bitmap == g2->bitmap &&
3405 g1->src_x == g2->src_x &&
3406 g1->src_y == g2->src_y &&
3407 g1->anim_frames == g2->anim_frames &&
3408 g1->anim_delay == g2->anim_delay &&
3409 g1->anim_mode == g2->anim_mode);
3412 void DrawAllPlayers()
3416 for (i = 0; i < MAX_PLAYERS; i++)
3417 if (stored_player[i].active)
3418 DrawPlayer(&stored_player[i]);
3421 void DrawPlayerField(int x, int y)
3423 if (!IS_PLAYER(x, y))
3426 DrawPlayer(PLAYERINFO(x, y));
3429 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3431 void DrawPlayer(struct PlayerInfo *player)
3433 int jx = player->jx;
3434 int jy = player->jy;
3435 int move_dir = player->MovDir;
3436 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3437 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3438 int last_jx = (player->is_moving ? jx - dx : jx);
3439 int last_jy = (player->is_moving ? jy - dy : jy);
3440 int next_jx = jx + dx;
3441 int next_jy = jy + dy;
3442 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3443 boolean player_is_opaque = FALSE;
3444 int sx = SCREENX(jx), sy = SCREENY(jy);
3445 int sxx = 0, syy = 0;
3446 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3448 int action = ACTION_DEFAULT;
3449 int last_player_graphic = getPlayerGraphic(player, move_dir);
3450 int last_player_frame = player->Frame;
3453 /* GfxElement[][] is set to the element the player is digging or collecting;
3454 remove also for off-screen player if the player is not moving anymore */
3455 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3456 GfxElement[jx][jy] = EL_UNDEFINED;
3458 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3462 if (!IN_LEV_FIELD(jx, jy))
3464 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3465 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3466 printf("DrawPlayerField(): This should never happen!\n");
3471 if (element == EL_EXPLOSION)
3474 action = (player->is_pushing ? ACTION_PUSHING :
3475 player->is_digging ? ACTION_DIGGING :
3476 player->is_collecting ? ACTION_COLLECTING :
3477 player->is_moving ? ACTION_MOVING :
3478 player->is_snapping ? ACTION_SNAPPING :
3479 player->is_dropping ? ACTION_DROPPING :
3480 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3482 if (player->is_waiting)
3483 move_dir = player->dir_waiting;
3485 InitPlayerGfxAnimation(player, action, move_dir);
3487 /* ----------------------------------------------------------------------- */
3488 /* draw things in the field the player is leaving, if needed */
3489 /* ----------------------------------------------------------------------- */
3491 if (player->is_moving)
3493 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3495 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3497 if (last_element == EL_DYNAMITE_ACTIVE ||
3498 last_element == EL_EM_DYNAMITE_ACTIVE ||
3499 last_element == EL_SP_DISK_RED_ACTIVE)
3500 DrawDynamite(last_jx, last_jy);
3502 DrawLevelFieldThruMask(last_jx, last_jy);
3504 else if (last_element == EL_DYNAMITE_ACTIVE ||
3505 last_element == EL_EM_DYNAMITE_ACTIVE ||
3506 last_element == EL_SP_DISK_RED_ACTIVE)
3507 DrawDynamite(last_jx, last_jy);
3509 /* !!! this is not enough to prevent flickering of players which are
3510 moving next to each others without a free tile between them -- this
3511 can only be solved by drawing all players layer by layer (first the
3512 background, then the foreground etc.) !!! => TODO */
3513 else if (!IS_PLAYER(last_jx, last_jy))
3514 DrawLevelField(last_jx, last_jy);
3517 DrawLevelField(last_jx, last_jy);
3520 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3521 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3524 if (!IN_SCR_FIELD(sx, sy))
3527 /* ----------------------------------------------------------------------- */
3528 /* draw things behind the player, if needed */
3529 /* ----------------------------------------------------------------------- */
3532 DrawLevelElement(jx, jy, Back[jx][jy]);
3533 else if (IS_ACTIVE_BOMB(element))
3534 DrawLevelElement(jx, jy, EL_EMPTY);
3537 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3539 int old_element = GfxElement[jx][jy];
3540 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3541 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3543 if (GFX_CRUMBLED(old_element))
3544 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3546 DrawGraphic(sx, sy, old_graphic, frame);
3548 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3549 player_is_opaque = TRUE;
3553 GfxElement[jx][jy] = EL_UNDEFINED;
3555 /* make sure that pushed elements are drawn with correct frame rate */
3556 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3558 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3559 GfxFrame[jx][jy] = player->StepFrame;
3561 DrawLevelField(jx, jy);
3565 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3566 /* ----------------------------------------------------------------------- */
3567 /* draw player himself */
3568 /* ----------------------------------------------------------------------- */
3570 graphic = getPlayerGraphic(player, move_dir);
3572 /* in the case of changed player action or direction, prevent the current
3573 animation frame from being restarted for identical animations */
3574 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3575 player->Frame = last_player_frame;
3577 frame = getGraphicAnimationFrame(graphic, player->Frame);
3581 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3582 sxx = player->GfxPos;
3584 syy = player->GfxPos;
3587 if (player_is_opaque)
3588 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3590 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3592 if (SHIELD_ON(player))
3594 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3595 IMG_SHIELD_NORMAL_ACTIVE);
3596 int frame = getGraphicAnimationFrame(graphic, -1);
3598 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3602 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3605 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3606 sxx = player->GfxPos;
3608 syy = player->GfxPos;
3612 /* ----------------------------------------------------------------------- */
3613 /* draw things the player is pushing, if needed */
3614 /* ----------------------------------------------------------------------- */
3616 if (player->is_pushing && player->is_moving)
3618 int px = SCREENX(jx), py = SCREENY(jy);
3619 int pxx = (TILEX - ABS(sxx)) * dx;
3620 int pyy = (TILEY - ABS(syy)) * dy;
3621 int gfx_frame = GfxFrame[jx][jy];
3627 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3629 element = Feld[next_jx][next_jy];
3630 gfx_frame = GfxFrame[next_jx][next_jy];
3633 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3635 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3636 frame = getGraphicAnimationFrame(graphic, sync_frame);
3638 /* draw background element under pushed element (like the Sokoban field) */
3639 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3641 /* this allows transparent pushing animation over non-black background */
3644 DrawLevelElement(jx, jy, Back[jx][jy]);
3646 DrawLevelElement(jx, jy, EL_EMPTY);
3648 if (Back[next_jx][next_jy])
3649 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3651 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3653 else if (Back[next_jx][next_jy])
3654 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3657 /* do not draw (EM style) pushing animation when pushing is finished */
3658 /* (two-tile animations usually do not contain start and end frame) */
3659 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3660 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3662 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3664 /* masked drawing is needed for EMC style (double) movement graphics */
3665 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3666 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3670 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3671 /* ----------------------------------------------------------------------- */
3672 /* draw player himself */
3673 /* ----------------------------------------------------------------------- */
3675 graphic = getPlayerGraphic(player, move_dir);
3677 /* in the case of changed player action or direction, prevent the current
3678 animation frame from being restarted for identical animations */
3679 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3680 player->Frame = last_player_frame;
3682 frame = getGraphicAnimationFrame(graphic, player->Frame);
3686 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3687 sxx = player->GfxPos;
3689 syy = player->GfxPos;
3692 if (player_is_opaque)
3693 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3695 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3697 if (SHIELD_ON(player))
3699 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3700 IMG_SHIELD_NORMAL_ACTIVE);
3701 int frame = getGraphicAnimationFrame(graphic, -1);
3703 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3707 /* ----------------------------------------------------------------------- */
3708 /* draw things in front of player (active dynamite or dynabombs) */
3709 /* ----------------------------------------------------------------------- */
3711 if (IS_ACTIVE_BOMB(element))
3713 graphic = el2img(element);
3714 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3716 if (game.emulation == EMU_SUPAPLEX)
3717 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3719 DrawGraphicThruMask(sx, sy, graphic, frame);
3722 if (player_is_moving && last_element == EL_EXPLOSION)
3724 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3725 GfxElement[last_jx][last_jy] : EL_EMPTY);
3726 int graphic = el_act2img(element, ACTION_EXPLODING);
3727 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3728 int phase = ExplodePhase[last_jx][last_jy] - 1;
3729 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3732 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3735 /* ----------------------------------------------------------------------- */
3736 /* draw elements the player is just walking/passing through/under */
3737 /* ----------------------------------------------------------------------- */
3739 if (player_is_moving)
3741 /* handle the field the player is leaving ... */
3742 if (IS_ACCESSIBLE_INSIDE(last_element))
3743 DrawLevelField(last_jx, last_jy);
3744 else if (IS_ACCESSIBLE_UNDER(last_element))
3745 DrawLevelFieldThruMask(last_jx, last_jy);
3748 /* do not redraw accessible elements if the player is just pushing them */
3749 if (!player_is_moving || !player->is_pushing)
3751 /* ... and the field the player is entering */
3752 if (IS_ACCESSIBLE_INSIDE(element))
3753 DrawLevelField(jx, jy);
3754 else if (IS_ACCESSIBLE_UNDER(element))
3755 DrawLevelFieldThruMask(jx, jy);
3758 MarkTileDirty(sx, sy);
3761 /* ------------------------------------------------------------------------- */
3763 void WaitForEventToContinue()
3765 boolean still_wait = TRUE;
3767 if (program.headless)
3770 /* simulate releasing mouse button over last gadget, if still pressed */
3772 HandleGadgets(-1, -1, 0);
3774 button_status = MB_RELEASED;
3788 case EVENT_BUTTONPRESS:
3789 case EVENT_KEYPRESS:
3790 #if defined(TARGET_SDL2)
3791 case SDL_CONTROLLERBUTTONDOWN:
3793 case SDL_JOYBUTTONDOWN:
3797 case EVENT_KEYRELEASE:
3798 ClearPlayerAction();
3802 HandleOtherEvents(&event);
3806 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3815 #define MAX_REQUEST_LINES 13
3816 #define MAX_REQUEST_LINE_FONT1_LEN 7
3817 #define MAX_REQUEST_LINE_FONT2_LEN 10
3819 static int RequestHandleEvents(unsigned int req_state)
3821 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3822 local_player->LevelSolved_GameEnd);
3823 int width = request.width;
3824 int height = request.height;
3828 setRequestPosition(&sx, &sy, FALSE);
3830 button_status = MB_RELEASED;
3832 request_gadget_id = -1;
3839 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3841 HandleGameActions();
3843 SetDrawtoField(DRAW_TO_BACKBUFFER);
3845 if (global.use_envelope_request)
3847 /* copy current state of request area to middle of playfield area */
3848 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
3856 while (NextValidEvent(&event))
3860 case EVENT_BUTTONPRESS:
3861 case EVENT_BUTTONRELEASE:
3862 case EVENT_MOTIONNOTIFY:
3866 if (event.type == EVENT_MOTIONNOTIFY)
3871 motion_status = TRUE;
3872 mx = ((MotionEvent *) &event)->x;
3873 my = ((MotionEvent *) &event)->y;
3877 motion_status = FALSE;
3878 mx = ((ButtonEvent *) &event)->x;
3879 my = ((ButtonEvent *) &event)->y;
3880 if (event.type == EVENT_BUTTONPRESS)
3881 button_status = ((ButtonEvent *) &event)->button;
3883 button_status = MB_RELEASED;
3886 /* this sets 'request_gadget_id' */
3887 HandleGadgets(mx, my, button_status);
3889 switch (request_gadget_id)
3891 case TOOL_CTRL_ID_YES:
3894 case TOOL_CTRL_ID_NO:
3897 case TOOL_CTRL_ID_CONFIRM:
3898 result = TRUE | FALSE;
3901 case TOOL_CTRL_ID_PLAYER_1:
3904 case TOOL_CTRL_ID_PLAYER_2:
3907 case TOOL_CTRL_ID_PLAYER_3:
3910 case TOOL_CTRL_ID_PLAYER_4:
3921 #if defined(TARGET_SDL2)
3922 case SDL_WINDOWEVENT:
3923 HandleWindowEvent((WindowEvent *) &event);
3926 case SDL_APP_WILLENTERBACKGROUND:
3927 case SDL_APP_DIDENTERBACKGROUND:
3928 case SDL_APP_WILLENTERFOREGROUND:
3929 case SDL_APP_DIDENTERFOREGROUND:
3930 HandlePauseResumeEvent((PauseResumeEvent *) &event);
3934 case EVENT_KEYPRESS:
3936 Key key = GetEventKey((KeyEvent *)&event, TRUE);
3941 if (req_state & REQ_CONFIRM)
3946 #if defined(TARGET_SDL2)
3949 #if defined(KSYM_Rewind)
3950 case KSYM_Rewind: /* for Amazon Fire TV remote */
3957 #if defined(TARGET_SDL2)
3959 #if defined(KSYM_FastForward)
3960 case KSYM_FastForward: /* for Amazon Fire TV remote */
3967 HandleKeysDebug(key);
3971 if (req_state & REQ_PLAYER)
3977 case EVENT_KEYRELEASE:
3978 ClearPlayerAction();
3981 #if defined(TARGET_SDL2)
3982 case SDL_CONTROLLERBUTTONDOWN:
3983 switch (event.cbutton.button)
3985 case SDL_CONTROLLER_BUTTON_A:
3986 case SDL_CONTROLLER_BUTTON_X:
3987 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
3991 case SDL_CONTROLLER_BUTTON_B:
3992 case SDL_CONTROLLER_BUTTON_Y:
3993 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
3994 case SDL_CONTROLLER_BUTTON_BACK:
3999 if (req_state & REQ_PLAYER)
4004 case SDL_CONTROLLERBUTTONUP:
4005 HandleJoystickEvent(&event);
4006 ClearPlayerAction();
4011 HandleOtherEvents(&event);
4016 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4018 int joy = AnyJoystick();
4020 if (joy & JOY_BUTTON_1)
4022 else if (joy & JOY_BUTTON_2)
4028 if (global.use_envelope_request)
4030 /* copy back current state of pressed buttons inside request area */
4031 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4041 static boolean RequestDoor(char *text, unsigned int req_state)
4043 unsigned int old_door_state;
4044 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4045 int font_nr = FONT_TEXT_2;
4050 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4052 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4053 font_nr = FONT_TEXT_1;
4056 if (game_status == GAME_MODE_PLAYING)
4057 BlitScreenToBitmap(backbuffer);
4059 /* disable deactivated drawing when quick-loading level tape recording */
4060 if (tape.playing && tape.deactivate_display)
4061 TapeDeactivateDisplayOff(TRUE);
4063 SetMouseCursor(CURSOR_DEFAULT);
4065 #if defined(NETWORK_AVALIABLE)
4066 /* pause network game while waiting for request to answer */
4067 if (options.network &&
4068 game_status == GAME_MODE_PLAYING &&
4069 req_state & REQUEST_WAIT_FOR_INPUT)
4070 SendToServer_PausePlaying();
4073 old_door_state = GetDoorState();
4075 /* simulate releasing mouse button over last gadget, if still pressed */
4077 HandleGadgets(-1, -1, 0);
4081 /* draw released gadget before proceeding */
4084 if (old_door_state & DOOR_OPEN_1)
4086 CloseDoor(DOOR_CLOSE_1);
4088 /* save old door content */
4089 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4090 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4093 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4094 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4096 /* clear door drawing field */
4097 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4099 /* force DOOR font inside door area */
4100 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4102 /* write text for request */
4103 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4105 char text_line[max_request_line_len + 1];
4111 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4113 tc = *(text_ptr + tx);
4114 // if (!tc || tc == ' ')
4115 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4119 if ((tc == '?' || tc == '!') && tl == 0)
4129 strncpy(text_line, text_ptr, tl);
4132 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4133 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4134 text_line, font_nr);
4136 text_ptr += tl + (tc == ' ' ? 1 : 0);
4137 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4142 if (req_state & REQ_ASK)
4144 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4145 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4147 else if (req_state & REQ_CONFIRM)
4149 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4151 else if (req_state & REQ_PLAYER)
4153 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4154 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4155 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4156 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4159 /* copy request gadgets to door backbuffer */
4160 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4162 OpenDoor(DOOR_OPEN_1);
4164 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4166 if (game_status == GAME_MODE_PLAYING)
4168 SetPanelBackground();
4169 SetDrawBackgroundMask(REDRAW_DOOR_1);
4173 SetDrawBackgroundMask(REDRAW_FIELD);
4179 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4181 // ---------- handle request buttons ----------
4182 result = RequestHandleEvents(req_state);
4186 if (!(req_state & REQ_STAY_OPEN))
4188 CloseDoor(DOOR_CLOSE_1);
4190 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4191 (req_state & REQ_REOPEN))
4192 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4197 if (game_status == GAME_MODE_PLAYING)
4199 SetPanelBackground();
4200 SetDrawBackgroundMask(REDRAW_DOOR_1);
4204 SetDrawBackgroundMask(REDRAW_FIELD);
4207 #if defined(NETWORK_AVALIABLE)
4208 /* continue network game after request */
4209 if (options.network &&
4210 game_status == GAME_MODE_PLAYING &&
4211 req_state & REQUEST_WAIT_FOR_INPUT)
4212 SendToServer_ContinuePlaying();
4215 /* restore deactivated drawing when quick-loading level tape recording */
4216 if (tape.playing && tape.deactivate_display)
4217 TapeDeactivateDisplayOn();
4222 static boolean RequestEnvelope(char *text, unsigned int req_state)
4226 if (game_status == GAME_MODE_PLAYING)
4227 BlitScreenToBitmap(backbuffer);
4229 /* disable deactivated drawing when quick-loading level tape recording */
4230 if (tape.playing && tape.deactivate_display)
4231 TapeDeactivateDisplayOff(TRUE);
4233 SetMouseCursor(CURSOR_DEFAULT);
4235 #if defined(NETWORK_AVALIABLE)
4236 /* pause network game while waiting for request to answer */
4237 if (options.network &&
4238 game_status == GAME_MODE_PLAYING &&
4239 req_state & REQUEST_WAIT_FOR_INPUT)
4240 SendToServer_PausePlaying();
4243 /* simulate releasing mouse button over last gadget, if still pressed */
4245 HandleGadgets(-1, -1, 0);
4249 // (replace with setting corresponding request background)
4250 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4251 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4253 /* clear door drawing field */
4254 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4256 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4258 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4260 if (game_status == GAME_MODE_PLAYING)
4262 SetPanelBackground();
4263 SetDrawBackgroundMask(REDRAW_DOOR_1);
4267 SetDrawBackgroundMask(REDRAW_FIELD);
4273 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4275 // ---------- handle request buttons ----------
4276 result = RequestHandleEvents(req_state);
4280 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4284 if (game_status == GAME_MODE_PLAYING)
4286 SetPanelBackground();
4287 SetDrawBackgroundMask(REDRAW_DOOR_1);
4291 SetDrawBackgroundMask(REDRAW_FIELD);
4294 #if defined(NETWORK_AVALIABLE)
4295 /* continue network game after request */
4296 if (options.network &&
4297 game_status == GAME_MODE_PLAYING &&
4298 req_state & REQUEST_WAIT_FOR_INPUT)
4299 SendToServer_ContinuePlaying();
4302 /* restore deactivated drawing when quick-loading level tape recording */
4303 if (tape.playing && tape.deactivate_display)
4304 TapeDeactivateDisplayOn();
4309 boolean Request(char *text, unsigned int req_state)
4311 boolean overlay_active = GetOverlayActive();
4314 SetOverlayActive(FALSE);
4316 if (global.use_envelope_request)
4317 result = RequestEnvelope(text, req_state);
4319 result = RequestDoor(text, req_state);
4321 SetOverlayActive(overlay_active);
4326 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4328 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4329 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4332 if (dpo1->sort_priority != dpo2->sort_priority)
4333 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4335 compare_result = dpo1->nr - dpo2->nr;
4337 return compare_result;
4340 void InitGraphicCompatibilityInfo_Doors()
4346 struct DoorInfo *door;
4350 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4351 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4353 { -1, -1, -1, NULL }
4355 struct Rect door_rect_list[] =
4357 { DX, DY, DXSIZE, DYSIZE },
4358 { VX, VY, VXSIZE, VYSIZE }
4362 for (i = 0; doors[i].door_token != -1; i++)
4364 int door_token = doors[i].door_token;
4365 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4366 int part_1 = doors[i].part_1;
4367 int part_8 = doors[i].part_8;
4368 int part_2 = part_1 + 1;
4369 int part_3 = part_1 + 2;
4370 struct DoorInfo *door = doors[i].door;
4371 struct Rect *door_rect = &door_rect_list[door_index];
4372 boolean door_gfx_redefined = FALSE;
4374 /* check if any door part graphic definitions have been redefined */
4376 for (j = 0; door_part_controls[j].door_token != -1; j++)
4378 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4379 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4381 if (dpc->door_token == door_token && fi->redefined)
4382 door_gfx_redefined = TRUE;
4385 /* check for old-style door graphic/animation modifications */
4387 if (!door_gfx_redefined)
4389 if (door->anim_mode & ANIM_STATIC_PANEL)
4391 door->panel.step_xoffset = 0;
4392 door->panel.step_yoffset = 0;
4395 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4397 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4398 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4399 int num_door_steps, num_panel_steps;
4401 /* remove door part graphics other than the two default wings */
4403 for (j = 0; door_part_controls[j].door_token != -1; j++)
4405 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4406 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4408 if (dpc->graphic >= part_3 &&
4409 dpc->graphic <= part_8)
4413 /* set graphics and screen positions of the default wings */
4415 g_part_1->width = door_rect->width;
4416 g_part_1->height = door_rect->height;
4417 g_part_2->width = door_rect->width;
4418 g_part_2->height = door_rect->height;
4419 g_part_2->src_x = door_rect->width;
4420 g_part_2->src_y = g_part_1->src_y;
4422 door->part_2.x = door->part_1.x;
4423 door->part_2.y = door->part_1.y;
4425 if (door->width != -1)
4427 g_part_1->width = door->width;
4428 g_part_2->width = door->width;
4430 // special treatment for graphics and screen position of right wing
4431 g_part_2->src_x += door_rect->width - door->width;
4432 door->part_2.x += door_rect->width - door->width;
4435 if (door->height != -1)
4437 g_part_1->height = door->height;
4438 g_part_2->height = door->height;
4440 // special treatment for graphics and screen position of bottom wing
4441 g_part_2->src_y += door_rect->height - door->height;
4442 door->part_2.y += door_rect->height - door->height;
4445 /* set animation delays for the default wings and panels */
4447 door->part_1.step_delay = door->step_delay;
4448 door->part_2.step_delay = door->step_delay;
4449 door->panel.step_delay = door->step_delay;
4451 /* set animation draw order for the default wings */
4453 door->part_1.sort_priority = 2; /* draw left wing over ... */
4454 door->part_2.sort_priority = 1; /* ... right wing */
4456 /* set animation draw offset for the default wings */
4458 if (door->anim_mode & ANIM_HORIZONTAL)
4460 door->part_1.step_xoffset = door->step_offset;
4461 door->part_1.step_yoffset = 0;
4462 door->part_2.step_xoffset = door->step_offset * -1;
4463 door->part_2.step_yoffset = 0;
4465 num_door_steps = g_part_1->width / door->step_offset;
4467 else // ANIM_VERTICAL
4469 door->part_1.step_xoffset = 0;
4470 door->part_1.step_yoffset = door->step_offset;
4471 door->part_2.step_xoffset = 0;
4472 door->part_2.step_yoffset = door->step_offset * -1;
4474 num_door_steps = g_part_1->height / door->step_offset;
4477 /* set animation draw offset for the default panels */
4479 if (door->step_offset > 1)
4481 num_panel_steps = 2 * door_rect->height / door->step_offset;
4482 door->panel.start_step = num_panel_steps - num_door_steps;
4483 door->panel.start_step_closing = door->panel.start_step;
4487 num_panel_steps = door_rect->height / door->step_offset;
4488 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4489 door->panel.start_step_closing = door->panel.start_step;
4490 door->panel.step_delay *= 2;
4501 for (i = 0; door_part_controls[i].door_token != -1; i++)
4503 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4504 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4506 /* initialize "start_step_opening" and "start_step_closing", if needed */
4507 if (dpc->pos->start_step_opening == 0 &&
4508 dpc->pos->start_step_closing == 0)
4510 // dpc->pos->start_step_opening = dpc->pos->start_step;
4511 dpc->pos->start_step_closing = dpc->pos->start_step;
4514 /* fill structure for door part draw order (sorted below) */
4516 dpo->sort_priority = dpc->pos->sort_priority;
4519 /* sort door part controls according to sort_priority and graphic number */
4520 qsort(door_part_order, MAX_DOOR_PARTS,
4521 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4524 unsigned int OpenDoor(unsigned int door_state)
4526 if (door_state & DOOR_COPY_BACK)
4528 if (door_state & DOOR_OPEN_1)
4529 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4530 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4532 if (door_state & DOOR_OPEN_2)
4533 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4534 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4536 door_state &= ~DOOR_COPY_BACK;
4539 return MoveDoor(door_state);
4542 unsigned int CloseDoor(unsigned int door_state)
4544 unsigned int old_door_state = GetDoorState();
4546 if (!(door_state & DOOR_NO_COPY_BACK))
4548 if (old_door_state & DOOR_OPEN_1)
4549 BlitBitmap(backbuffer, bitmap_db_door_1,
4550 DX, DY, DXSIZE, DYSIZE, 0, 0);
4552 if (old_door_state & DOOR_OPEN_2)
4553 BlitBitmap(backbuffer, bitmap_db_door_2,
4554 VX, VY, VXSIZE, VYSIZE, 0, 0);
4556 door_state &= ~DOOR_NO_COPY_BACK;
4559 return MoveDoor(door_state);
4562 unsigned int GetDoorState()
4564 return MoveDoor(DOOR_GET_STATE);
4567 unsigned int SetDoorState(unsigned int door_state)
4569 return MoveDoor(door_state | DOOR_SET_STATE);
4572 int euclid(int a, int b)
4574 return (b ? euclid(b, a % b) : a);
4577 unsigned int MoveDoor(unsigned int door_state)
4579 struct Rect door_rect_list[] =
4581 { DX, DY, DXSIZE, DYSIZE },
4582 { VX, VY, VXSIZE, VYSIZE }
4584 static int door1 = DOOR_CLOSE_1;
4585 static int door2 = DOOR_CLOSE_2;
4586 unsigned int door_delay = 0;
4587 unsigned int door_delay_value;
4590 if (door_state == DOOR_GET_STATE)
4591 return (door1 | door2);
4593 if (door_state & DOOR_SET_STATE)
4595 if (door_state & DOOR_ACTION_1)
4596 door1 = door_state & DOOR_ACTION_1;
4597 if (door_state & DOOR_ACTION_2)
4598 door2 = door_state & DOOR_ACTION_2;
4600 return (door1 | door2);
4603 if (!(door_state & DOOR_FORCE_REDRAW))
4605 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4606 door_state &= ~DOOR_OPEN_1;
4607 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4608 door_state &= ~DOOR_CLOSE_1;
4609 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4610 door_state &= ~DOOR_OPEN_2;
4611 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4612 door_state &= ~DOOR_CLOSE_2;
4615 if (global.autoplay_leveldir)
4617 door_state |= DOOR_NO_DELAY;
4618 door_state &= ~DOOR_CLOSE_ALL;
4621 if (game_status == GAME_MODE_EDITOR)
4622 door_state |= DOOR_NO_DELAY;
4624 if (door_state & DOOR_ACTION)
4626 boolean door_panel_drawn[NUM_DOORS];
4627 boolean panel_has_doors[NUM_DOORS];
4628 boolean door_part_skip[MAX_DOOR_PARTS];
4629 boolean door_part_done[MAX_DOOR_PARTS];
4630 boolean door_part_done_all;
4631 int num_steps[MAX_DOOR_PARTS];
4632 int max_move_delay = 0; // delay for complete animations of all doors
4633 int max_step_delay = 0; // delay (ms) between two animation frames
4634 int num_move_steps = 0; // number of animation steps for all doors
4635 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4636 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4637 int current_move_delay = 0;
4641 for (i = 0; i < NUM_DOORS; i++)
4642 panel_has_doors[i] = FALSE;
4644 for (i = 0; i < MAX_DOOR_PARTS; i++)
4646 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4647 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4648 int door_token = dpc->door_token;
4650 door_part_done[i] = FALSE;
4651 door_part_skip[i] = (!(door_state & door_token) ||
4655 for (i = 0; i < MAX_DOOR_PARTS; i++)
4657 int nr = door_part_order[i].nr;
4658 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4659 struct DoorPartPosInfo *pos = dpc->pos;
4660 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4661 int door_token = dpc->door_token;
4662 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4663 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4664 int step_xoffset = ABS(pos->step_xoffset);
4665 int step_yoffset = ABS(pos->step_yoffset);
4666 int step_delay = pos->step_delay;
4667 int current_door_state = door_state & door_token;
4668 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4669 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4670 boolean part_opening = (is_panel ? door_closing : door_opening);
4671 int start_step = (part_opening ? pos->start_step_opening :
4672 pos->start_step_closing);
4673 float move_xsize = (step_xoffset ? g->width : 0);
4674 float move_ysize = (step_yoffset ? g->height : 0);
4675 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4676 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4677 int move_steps = (move_xsteps && move_ysteps ?
4678 MIN(move_xsteps, move_ysteps) :
4679 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4680 int move_delay = move_steps * step_delay;
4682 if (door_part_skip[nr])
4685 max_move_delay = MAX(max_move_delay, move_delay);
4686 max_step_delay = (max_step_delay == 0 ? step_delay :
4687 euclid(max_step_delay, step_delay));
4688 num_steps[nr] = move_steps;
4692 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4694 panel_has_doors[door_index] = TRUE;
4698 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4700 num_move_steps = max_move_delay / max_step_delay;
4701 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4703 door_delay_value = max_step_delay;
4705 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4707 start = num_move_steps - 1;
4711 /* opening door sound has priority over simultaneously closing door */
4712 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4713 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4714 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4715 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4718 for (k = start; k < num_move_steps; k++)
4720 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4722 door_part_done_all = TRUE;
4724 for (i = 0; i < NUM_DOORS; i++)
4725 door_panel_drawn[i] = FALSE;
4727 for (i = 0; i < MAX_DOOR_PARTS; i++)
4729 int nr = door_part_order[i].nr;
4730 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4731 struct DoorPartPosInfo *pos = dpc->pos;
4732 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4733 int door_token = dpc->door_token;
4734 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4735 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4736 boolean is_panel_and_door_has_closed = FALSE;
4737 struct Rect *door_rect = &door_rect_list[door_index];
4738 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4740 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4741 int current_door_state = door_state & door_token;
4742 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4743 boolean door_closing = !door_opening;
4744 boolean part_opening = (is_panel ? door_closing : door_opening);
4745 boolean part_closing = !part_opening;
4746 int start_step = (part_opening ? pos->start_step_opening :
4747 pos->start_step_closing);
4748 int step_delay = pos->step_delay;
4749 int step_factor = step_delay / max_step_delay;
4750 int k1 = (step_factor ? k / step_factor + 1 : k);
4751 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4752 int kk = MAX(0, k2);
4755 int src_x, src_y, src_xx, src_yy;
4756 int dst_x, dst_y, dst_xx, dst_yy;
4759 if (door_part_skip[nr])
4762 if (!(door_state & door_token))
4770 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4771 int kk_door = MAX(0, k2_door);
4772 int sync_frame = kk_door * door_delay_value;
4773 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4775 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
4776 &g_src_x, &g_src_y);
4781 if (!door_panel_drawn[door_index])
4783 ClearRectangle(drawto, door_rect->x, door_rect->y,
4784 door_rect->width, door_rect->height);
4786 door_panel_drawn[door_index] = TRUE;
4789 // draw opening or closing door parts
4791 if (pos->step_xoffset < 0) // door part on right side
4794 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4797 if (dst_xx + width > door_rect->width)
4798 width = door_rect->width - dst_xx;
4800 else // door part on left side
4803 dst_xx = pos->x - kk * pos->step_xoffset;
4807 src_xx = ABS(dst_xx);
4811 width = g->width - src_xx;
4813 if (width > door_rect->width)
4814 width = door_rect->width;
4816 // printf("::: k == %d [%d] \n", k, start_step);
4819 if (pos->step_yoffset < 0) // door part on bottom side
4822 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4825 if (dst_yy + height > door_rect->height)
4826 height = door_rect->height - dst_yy;
4828 else // door part on top side
4831 dst_yy = pos->y - kk * pos->step_yoffset;
4835 src_yy = ABS(dst_yy);
4839 height = g->height - src_yy;
4842 src_x = g_src_x + src_xx;
4843 src_y = g_src_y + src_yy;
4845 dst_x = door_rect->x + dst_xx;
4846 dst_y = door_rect->y + dst_yy;
4848 is_panel_and_door_has_closed =
4851 panel_has_doors[door_index] &&
4852 k >= num_move_steps_doors_only - 1);
4854 if (width >= 0 && width <= g->width &&
4855 height >= 0 && height <= g->height &&
4856 !is_panel_and_door_has_closed)
4858 if (is_panel || !pos->draw_masked)
4859 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4862 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4866 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4868 if ((part_opening && (width < 0 || height < 0)) ||
4869 (part_closing && (width >= g->width && height >= g->height)))
4870 door_part_done[nr] = TRUE;
4872 // continue door part animations, but not panel after door has closed
4873 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4874 door_part_done_all = FALSE;
4877 if (!(door_state & DOOR_NO_DELAY))
4881 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4883 current_move_delay += max_step_delay;
4885 /* prevent OS (Windows) from complaining about program not responding */
4889 if (door_part_done_all)
4894 if (door_state & DOOR_ACTION_1)
4895 door1 = door_state & DOOR_ACTION_1;
4896 if (door_state & DOOR_ACTION_2)
4897 door2 = door_state & DOOR_ACTION_2;
4899 // draw masked border over door area
4900 DrawMaskedBorder(REDRAW_DOOR_1);
4901 DrawMaskedBorder(REDRAW_DOOR_2);
4903 return (door1 | door2);
4906 static boolean useSpecialEditorDoor()
4908 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4909 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4911 // do not draw special editor door if editor border defined or redefined
4912 if (graphic_info[graphic].bitmap != NULL || redefined)
4915 // do not draw special editor door if global border defined to be empty
4916 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4919 // do not draw special editor door if viewport definitions do not match
4923 EY + EYSIZE != VY + VYSIZE)
4929 void DrawSpecialEditorDoor()
4931 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4932 int top_border_width = gfx1->width;
4933 int top_border_height = gfx1->height;
4934 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4935 int ex = EX - outer_border;
4936 int ey = EY - outer_border;
4937 int vy = VY - outer_border;
4938 int exsize = EXSIZE + 2 * outer_border;
4940 if (!useSpecialEditorDoor())
4943 /* draw bigger level editor toolbox window */
4944 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4945 top_border_width, top_border_height, ex, ey - top_border_height);
4946 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4947 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4949 redraw_mask |= REDRAW_ALL;
4952 void UndrawSpecialEditorDoor()
4954 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4955 int top_border_width = gfx1->width;
4956 int top_border_height = gfx1->height;
4957 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4958 int ex = EX - outer_border;
4959 int ey = EY - outer_border;
4960 int ey_top = ey - top_border_height;
4961 int exsize = EXSIZE + 2 * outer_border;
4962 int eysize = EYSIZE + 2 * outer_border;
4964 if (!useSpecialEditorDoor())
4967 /* draw normal tape recorder window */
4968 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4970 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4971 ex, ey_top, top_border_width, top_border_height,
4973 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4974 ex, ey, exsize, eysize, ex, ey);
4978 // if screen background is set to "[NONE]", clear editor toolbox window
4979 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4980 ClearRectangle(drawto, ex, ey, exsize, eysize);
4983 redraw_mask |= REDRAW_ALL;
4987 /* ---------- new tool button stuff ---------------------------------------- */
4992 struct TextPosInfo *pos;
4995 } toolbutton_info[NUM_TOOL_BUTTONS] =
4998 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
4999 TOOL_CTRL_ID_YES, "yes"
5002 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
5003 TOOL_CTRL_ID_NO, "no"
5006 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
5007 TOOL_CTRL_ID_CONFIRM, "confirm"
5010 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5011 TOOL_CTRL_ID_PLAYER_1, "player 1"
5014 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5015 TOOL_CTRL_ID_PLAYER_2, "player 2"
5018 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5019 TOOL_CTRL_ID_PLAYER_3, "player 3"
5022 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5023 TOOL_CTRL_ID_PLAYER_4, "player 4"
5027 void CreateToolButtons()
5031 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5033 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
5034 struct TextPosInfo *pos = toolbutton_info[i].pos;
5035 struct GadgetInfo *gi;
5036 Bitmap *deco_bitmap = None;
5037 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5038 unsigned int event_mask = GD_EVENT_RELEASED;
5041 int gd_x = gfx->src_x;
5042 int gd_y = gfx->src_y;
5043 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5044 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5047 if (global.use_envelope_request)
5048 setRequestPosition(&dx, &dy, TRUE);
5050 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5052 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5054 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5055 pos->size, &deco_bitmap, &deco_x, &deco_y);
5056 deco_xpos = (gfx->width - pos->size) / 2;
5057 deco_ypos = (gfx->height - pos->size) / 2;
5060 gi = CreateGadget(GDI_CUSTOM_ID, id,
5061 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5062 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
5063 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
5064 GDI_WIDTH, gfx->width,
5065 GDI_HEIGHT, gfx->height,
5066 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5067 GDI_STATE, GD_BUTTON_UNPRESSED,
5068 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5069 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5070 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5071 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5072 GDI_DECORATION_SIZE, pos->size, pos->size,
5073 GDI_DECORATION_SHIFTING, 1, 1,
5074 GDI_DIRECT_DRAW, FALSE,
5075 GDI_EVENT_MASK, event_mask,
5076 GDI_CALLBACK_ACTION, HandleToolButtons,
5080 Error(ERR_EXIT, "cannot create gadget");
5082 tool_gadget[id] = gi;
5086 void FreeToolButtons()
5090 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5091 FreeGadget(tool_gadget[i]);
5094 static void UnmapToolButtons()
5098 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5099 UnmapGadget(tool_gadget[i]);
5102 static void HandleToolButtons(struct GadgetInfo *gi)
5104 request_gadget_id = gi->custom_id;
5107 static struct Mapping_EM_to_RND_object
5110 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
5111 boolean is_backside; /* backside of moving element */
5117 em_object_mapping_list[] =
5120 Xblank, TRUE, FALSE,
5124 Yacid_splash_eB, FALSE, FALSE,
5125 EL_ACID_SPLASH_RIGHT, -1, -1
5128 Yacid_splash_wB, FALSE, FALSE,
5129 EL_ACID_SPLASH_LEFT, -1, -1
5132 #ifdef EM_ENGINE_BAD_ROLL
5134 Xstone_force_e, FALSE, FALSE,
5135 EL_ROCK, -1, MV_BIT_RIGHT
5138 Xstone_force_w, FALSE, FALSE,
5139 EL_ROCK, -1, MV_BIT_LEFT
5142 Xnut_force_e, FALSE, FALSE,
5143 EL_NUT, -1, MV_BIT_RIGHT
5146 Xnut_force_w, FALSE, FALSE,
5147 EL_NUT, -1, MV_BIT_LEFT
5150 Xspring_force_e, FALSE, FALSE,
5151 EL_SPRING, -1, MV_BIT_RIGHT
5154 Xspring_force_w, FALSE, FALSE,
5155 EL_SPRING, -1, MV_BIT_LEFT
5158 Xemerald_force_e, FALSE, FALSE,
5159 EL_EMERALD, -1, MV_BIT_RIGHT
5162 Xemerald_force_w, FALSE, FALSE,
5163 EL_EMERALD, -1, MV_BIT_LEFT
5166 Xdiamond_force_e, FALSE, FALSE,
5167 EL_DIAMOND, -1, MV_BIT_RIGHT
5170 Xdiamond_force_w, FALSE, FALSE,
5171 EL_DIAMOND, -1, MV_BIT_LEFT
5174 Xbomb_force_e, FALSE, FALSE,
5175 EL_BOMB, -1, MV_BIT_RIGHT
5178 Xbomb_force_w, FALSE, FALSE,
5179 EL_BOMB, -1, MV_BIT_LEFT
5181 #endif /* EM_ENGINE_BAD_ROLL */
5184 Xstone, TRUE, FALSE,
5188 Xstone_pause, FALSE, FALSE,
5192 Xstone_fall, FALSE, FALSE,
5196 Ystone_s, FALSE, FALSE,
5197 EL_ROCK, ACTION_FALLING, -1
5200 Ystone_sB, FALSE, TRUE,
5201 EL_ROCK, ACTION_FALLING, -1
5204 Ystone_e, FALSE, FALSE,
5205 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5208 Ystone_eB, FALSE, TRUE,
5209 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5212 Ystone_w, FALSE, FALSE,
5213 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5216 Ystone_wB, FALSE, TRUE,
5217 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5224 Xnut_pause, FALSE, FALSE,
5228 Xnut_fall, FALSE, FALSE,
5232 Ynut_s, FALSE, FALSE,
5233 EL_NUT, ACTION_FALLING, -1
5236 Ynut_sB, FALSE, TRUE,
5237 EL_NUT, ACTION_FALLING, -1
5240 Ynut_e, FALSE, FALSE,
5241 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5244 Ynut_eB, FALSE, TRUE,
5245 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5248 Ynut_w, FALSE, FALSE,
5249 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5252 Ynut_wB, FALSE, TRUE,
5253 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5256 Xbug_n, TRUE, FALSE,
5260 Xbug_e, TRUE, FALSE,
5261 EL_BUG_RIGHT, -1, -1
5264 Xbug_s, TRUE, FALSE,
5268 Xbug_w, TRUE, FALSE,
5272 Xbug_gon, FALSE, FALSE,
5276 Xbug_goe, FALSE, FALSE,
5277 EL_BUG_RIGHT, -1, -1
5280 Xbug_gos, FALSE, FALSE,
5284 Xbug_gow, FALSE, FALSE,
5288 Ybug_n, FALSE, FALSE,
5289 EL_BUG, ACTION_MOVING, MV_BIT_UP
5292 Ybug_nB, FALSE, TRUE,
5293 EL_BUG, ACTION_MOVING, MV_BIT_UP
5296 Ybug_e, FALSE, FALSE,
5297 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5300 Ybug_eB, FALSE, TRUE,
5301 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5304 Ybug_s, FALSE, FALSE,
5305 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5308 Ybug_sB, FALSE, TRUE,
5309 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5312 Ybug_w, FALSE, FALSE,
5313 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5316 Ybug_wB, FALSE, TRUE,
5317 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5320 Ybug_w_n, FALSE, FALSE,
5321 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5324 Ybug_n_e, FALSE, FALSE,
5325 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5328 Ybug_e_s, FALSE, FALSE,
5329 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5332 Ybug_s_w, FALSE, FALSE,
5333 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5336 Ybug_e_n, FALSE, FALSE,
5337 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5340 Ybug_s_e, FALSE, FALSE,
5341 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5344 Ybug_w_s, FALSE, FALSE,
5345 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5348 Ybug_n_w, FALSE, FALSE,
5349 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5352 Ybug_stone, FALSE, FALSE,
5353 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5356 Ybug_spring, FALSE, FALSE,
5357 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5360 Xtank_n, TRUE, FALSE,
5361 EL_SPACESHIP_UP, -1, -1
5364 Xtank_e, TRUE, FALSE,
5365 EL_SPACESHIP_RIGHT, -1, -1
5368 Xtank_s, TRUE, FALSE,
5369 EL_SPACESHIP_DOWN, -1, -1
5372 Xtank_w, TRUE, FALSE,
5373 EL_SPACESHIP_LEFT, -1, -1
5376 Xtank_gon, FALSE, FALSE,
5377 EL_SPACESHIP_UP, -1, -1
5380 Xtank_goe, FALSE, FALSE,
5381 EL_SPACESHIP_RIGHT, -1, -1
5384 Xtank_gos, FALSE, FALSE,
5385 EL_SPACESHIP_DOWN, -1, -1
5388 Xtank_gow, FALSE, FALSE,
5389 EL_SPACESHIP_LEFT, -1, -1
5392 Ytank_n, FALSE, FALSE,
5393 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5396 Ytank_nB, FALSE, TRUE,
5397 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5400 Ytank_e, FALSE, FALSE,
5401 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5404 Ytank_eB, FALSE, TRUE,
5405 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5408 Ytank_s, FALSE, FALSE,
5409 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5412 Ytank_sB, FALSE, TRUE,
5413 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5416 Ytank_w, FALSE, FALSE,
5417 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5420 Ytank_wB, FALSE, TRUE,
5421 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5424 Ytank_w_n, FALSE, FALSE,
5425 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5428 Ytank_n_e, FALSE, FALSE,
5429 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5432 Ytank_e_s, FALSE, FALSE,
5433 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5436 Ytank_s_w, FALSE, FALSE,
5437 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5440 Ytank_e_n, FALSE, FALSE,
5441 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5444 Ytank_s_e, FALSE, FALSE,
5445 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5448 Ytank_w_s, FALSE, FALSE,
5449 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5452 Ytank_n_w, FALSE, FALSE,
5453 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5456 Ytank_stone, FALSE, FALSE,
5457 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5460 Ytank_spring, FALSE, FALSE,
5461 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5464 Xandroid, TRUE, FALSE,
5465 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5468 Xandroid_1_n, FALSE, FALSE,
5469 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5472 Xandroid_2_n, FALSE, FALSE,
5473 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5476 Xandroid_1_e, FALSE, FALSE,
5477 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5480 Xandroid_2_e, FALSE, FALSE,
5481 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5484 Xandroid_1_w, FALSE, FALSE,
5485 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5488 Xandroid_2_w, FALSE, FALSE,
5489 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5492 Xandroid_1_s, FALSE, FALSE,
5493 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5496 Xandroid_2_s, FALSE, FALSE,
5497 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5500 Yandroid_n, FALSE, FALSE,
5501 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5504 Yandroid_nB, FALSE, TRUE,
5505 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5508 Yandroid_ne, FALSE, FALSE,
5509 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5512 Yandroid_neB, FALSE, TRUE,
5513 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5516 Yandroid_e, FALSE, FALSE,
5517 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5520 Yandroid_eB, FALSE, TRUE,
5521 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5524 Yandroid_se, FALSE, FALSE,
5525 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5528 Yandroid_seB, FALSE, TRUE,
5529 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5532 Yandroid_s, FALSE, FALSE,
5533 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5536 Yandroid_sB, FALSE, TRUE,
5537 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5540 Yandroid_sw, FALSE, FALSE,
5541 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5544 Yandroid_swB, FALSE, TRUE,
5545 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5548 Yandroid_w, FALSE, FALSE,
5549 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5552 Yandroid_wB, FALSE, TRUE,
5553 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5556 Yandroid_nw, FALSE, FALSE,
5557 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5560 Yandroid_nwB, FALSE, TRUE,
5561 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5564 Xspring, TRUE, FALSE,
5568 Xspring_pause, FALSE, FALSE,
5572 Xspring_e, FALSE, FALSE,
5576 Xspring_w, FALSE, FALSE,
5580 Xspring_fall, FALSE, FALSE,
5584 Yspring_s, FALSE, FALSE,
5585 EL_SPRING, ACTION_FALLING, -1
5588 Yspring_sB, FALSE, TRUE,
5589 EL_SPRING, ACTION_FALLING, -1
5592 Yspring_e, FALSE, FALSE,
5593 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5596 Yspring_eB, FALSE, TRUE,
5597 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5600 Yspring_w, FALSE, FALSE,
5601 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5604 Yspring_wB, FALSE, TRUE,
5605 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5608 Yspring_kill_e, FALSE, FALSE,
5609 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5612 Yspring_kill_eB, FALSE, TRUE,
5613 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5616 Yspring_kill_w, FALSE, FALSE,
5617 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5620 Yspring_kill_wB, FALSE, TRUE,
5621 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5624 Xeater_n, TRUE, FALSE,
5625 EL_YAMYAM_UP, -1, -1
5628 Xeater_e, TRUE, FALSE,
5629 EL_YAMYAM_RIGHT, -1, -1
5632 Xeater_w, TRUE, FALSE,
5633 EL_YAMYAM_LEFT, -1, -1
5636 Xeater_s, TRUE, FALSE,
5637 EL_YAMYAM_DOWN, -1, -1
5640 Yeater_n, FALSE, FALSE,
5641 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5644 Yeater_nB, FALSE, TRUE,
5645 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5648 Yeater_e, FALSE, FALSE,
5649 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5652 Yeater_eB, FALSE, TRUE,
5653 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5656 Yeater_s, FALSE, FALSE,
5657 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5660 Yeater_sB, FALSE, TRUE,
5661 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5664 Yeater_w, FALSE, FALSE,
5665 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5668 Yeater_wB, FALSE, TRUE,
5669 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5672 Yeater_stone, FALSE, FALSE,
5673 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5676 Yeater_spring, FALSE, FALSE,
5677 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5680 Xalien, TRUE, FALSE,
5684 Xalien_pause, FALSE, FALSE,
5688 Yalien_n, FALSE, FALSE,
5689 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5692 Yalien_nB, FALSE, TRUE,
5693 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5696 Yalien_e, FALSE, FALSE,
5697 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5700 Yalien_eB, FALSE, TRUE,
5701 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5704 Yalien_s, FALSE, FALSE,
5705 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5708 Yalien_sB, FALSE, TRUE,
5709 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5712 Yalien_w, FALSE, FALSE,
5713 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5716 Yalien_wB, FALSE, TRUE,
5717 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5720 Yalien_stone, FALSE, FALSE,
5721 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5724 Yalien_spring, FALSE, FALSE,
5725 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5728 Xemerald, TRUE, FALSE,
5732 Xemerald_pause, FALSE, FALSE,
5736 Xemerald_fall, FALSE, FALSE,
5740 Xemerald_shine, FALSE, FALSE,
5741 EL_EMERALD, ACTION_TWINKLING, -1
5744 Yemerald_s, FALSE, FALSE,
5745 EL_EMERALD, ACTION_FALLING, -1
5748 Yemerald_sB, FALSE, TRUE,
5749 EL_EMERALD, ACTION_FALLING, -1
5752 Yemerald_e, FALSE, FALSE,
5753 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5756 Yemerald_eB, FALSE, TRUE,
5757 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5760 Yemerald_w, FALSE, FALSE,
5761 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5764 Yemerald_wB, FALSE, TRUE,
5765 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5768 Yemerald_eat, FALSE, FALSE,
5769 EL_EMERALD, ACTION_COLLECTING, -1
5772 Yemerald_stone, FALSE, FALSE,
5773 EL_NUT, ACTION_BREAKING, -1
5776 Xdiamond, TRUE, FALSE,
5780 Xdiamond_pause, FALSE, FALSE,
5784 Xdiamond_fall, FALSE, FALSE,
5788 Xdiamond_shine, FALSE, FALSE,
5789 EL_DIAMOND, ACTION_TWINKLING, -1
5792 Ydiamond_s, FALSE, FALSE,
5793 EL_DIAMOND, ACTION_FALLING, -1
5796 Ydiamond_sB, FALSE, TRUE,
5797 EL_DIAMOND, ACTION_FALLING, -1
5800 Ydiamond_e, FALSE, FALSE,
5801 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5804 Ydiamond_eB, FALSE, TRUE,
5805 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5808 Ydiamond_w, FALSE, FALSE,
5809 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5812 Ydiamond_wB, FALSE, TRUE,
5813 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5816 Ydiamond_eat, FALSE, FALSE,
5817 EL_DIAMOND, ACTION_COLLECTING, -1
5820 Ydiamond_stone, FALSE, FALSE,
5821 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5824 Xdrip_fall, TRUE, FALSE,
5825 EL_AMOEBA_DROP, -1, -1
5828 Xdrip_stretch, FALSE, FALSE,
5829 EL_AMOEBA_DROP, ACTION_FALLING, -1
5832 Xdrip_stretchB, FALSE, TRUE,
5833 EL_AMOEBA_DROP, ACTION_FALLING, -1
5836 Xdrip_eat, FALSE, FALSE,
5837 EL_AMOEBA_DROP, ACTION_GROWING, -1
5840 Ydrip_s1, FALSE, FALSE,
5841 EL_AMOEBA_DROP, ACTION_FALLING, -1
5844 Ydrip_s1B, FALSE, TRUE,
5845 EL_AMOEBA_DROP, ACTION_FALLING, -1
5848 Ydrip_s2, FALSE, FALSE,
5849 EL_AMOEBA_DROP, ACTION_FALLING, -1
5852 Ydrip_s2B, FALSE, TRUE,
5853 EL_AMOEBA_DROP, ACTION_FALLING, -1
5860 Xbomb_pause, FALSE, FALSE,
5864 Xbomb_fall, FALSE, FALSE,
5868 Ybomb_s, FALSE, FALSE,
5869 EL_BOMB, ACTION_FALLING, -1
5872 Ybomb_sB, FALSE, TRUE,
5873 EL_BOMB, ACTION_FALLING, -1
5876 Ybomb_e, FALSE, FALSE,
5877 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5880 Ybomb_eB, FALSE, TRUE,
5881 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5884 Ybomb_w, FALSE, FALSE,
5885 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5888 Ybomb_wB, FALSE, TRUE,
5889 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5892 Ybomb_eat, FALSE, FALSE,
5893 EL_BOMB, ACTION_ACTIVATING, -1
5896 Xballoon, TRUE, FALSE,
5900 Yballoon_n, FALSE, FALSE,
5901 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5904 Yballoon_nB, FALSE, TRUE,
5905 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5908 Yballoon_e, FALSE, FALSE,
5909 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5912 Yballoon_eB, FALSE, TRUE,
5913 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5916 Yballoon_s, FALSE, FALSE,
5917 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5920 Yballoon_sB, FALSE, TRUE,
5921 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5924 Yballoon_w, FALSE, FALSE,
5925 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5928 Yballoon_wB, FALSE, TRUE,
5929 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5932 Xgrass, TRUE, FALSE,
5933 EL_EMC_GRASS, -1, -1
5936 Ygrass_nB, FALSE, FALSE,
5937 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5940 Ygrass_eB, FALSE, FALSE,
5941 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5944 Ygrass_sB, FALSE, FALSE,
5945 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5948 Ygrass_wB, FALSE, FALSE,
5949 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5956 Ydirt_nB, FALSE, FALSE,
5957 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5960 Ydirt_eB, FALSE, FALSE,
5961 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5964 Ydirt_sB, FALSE, FALSE,
5965 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5968 Ydirt_wB, FALSE, FALSE,
5969 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5972 Xacid_ne, TRUE, FALSE,
5973 EL_ACID_POOL_TOPRIGHT, -1, -1
5976 Xacid_se, TRUE, FALSE,
5977 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5980 Xacid_s, TRUE, FALSE,
5981 EL_ACID_POOL_BOTTOM, -1, -1
5984 Xacid_sw, TRUE, FALSE,
5985 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5988 Xacid_nw, TRUE, FALSE,
5989 EL_ACID_POOL_TOPLEFT, -1, -1
5992 Xacid_1, TRUE, FALSE,
5996 Xacid_2, FALSE, FALSE,
6000 Xacid_3, FALSE, FALSE,
6004 Xacid_4, FALSE, FALSE,
6008 Xacid_5, FALSE, FALSE,
6012 Xacid_6, FALSE, FALSE,
6016 Xacid_7, FALSE, FALSE,
6020 Xacid_8, FALSE, FALSE,
6024 Xball_1, TRUE, FALSE,
6025 EL_EMC_MAGIC_BALL, -1, -1
6028 Xball_1B, FALSE, FALSE,
6029 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6032 Xball_2, FALSE, FALSE,
6033 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6036 Xball_2B, FALSE, FALSE,
6037 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6040 Yball_eat, FALSE, FALSE,
6041 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6044 Ykey_1_eat, FALSE, FALSE,
6045 EL_EM_KEY_1, ACTION_COLLECTING, -1
6048 Ykey_2_eat, FALSE, FALSE,
6049 EL_EM_KEY_2, ACTION_COLLECTING, -1
6052 Ykey_3_eat, FALSE, FALSE,
6053 EL_EM_KEY_3, ACTION_COLLECTING, -1
6056 Ykey_4_eat, FALSE, FALSE,
6057 EL_EM_KEY_4, ACTION_COLLECTING, -1
6060 Ykey_5_eat, FALSE, FALSE,
6061 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6064 Ykey_6_eat, FALSE, FALSE,
6065 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6068 Ykey_7_eat, FALSE, FALSE,
6069 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6072 Ykey_8_eat, FALSE, FALSE,
6073 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6076 Ylenses_eat, FALSE, FALSE,
6077 EL_EMC_LENSES, ACTION_COLLECTING, -1
6080 Ymagnify_eat, FALSE, FALSE,
6081 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6084 Ygrass_eat, FALSE, FALSE,
6085 EL_EMC_GRASS, ACTION_SNAPPING, -1
6088 Ydirt_eat, FALSE, FALSE,
6089 EL_SAND, ACTION_SNAPPING, -1
6092 Xgrow_ns, TRUE, FALSE,
6093 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6096 Ygrow_ns_eat, FALSE, FALSE,
6097 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6100 Xgrow_ew, TRUE, FALSE,
6101 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6104 Ygrow_ew_eat, FALSE, FALSE,
6105 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6108 Xwonderwall, TRUE, FALSE,
6109 EL_MAGIC_WALL, -1, -1
6112 XwonderwallB, FALSE, FALSE,
6113 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6116 Xamoeba_1, TRUE, FALSE,
6117 EL_AMOEBA_DRY, ACTION_OTHER, -1
6120 Xamoeba_2, FALSE, FALSE,
6121 EL_AMOEBA_DRY, ACTION_OTHER, -1
6124 Xamoeba_3, FALSE, FALSE,
6125 EL_AMOEBA_DRY, ACTION_OTHER, -1
6128 Xamoeba_4, FALSE, FALSE,
6129 EL_AMOEBA_DRY, ACTION_OTHER, -1
6132 Xamoeba_5, TRUE, FALSE,
6133 EL_AMOEBA_WET, ACTION_OTHER, -1
6136 Xamoeba_6, FALSE, FALSE,
6137 EL_AMOEBA_WET, ACTION_OTHER, -1
6140 Xamoeba_7, FALSE, FALSE,
6141 EL_AMOEBA_WET, ACTION_OTHER, -1
6144 Xamoeba_8, FALSE, FALSE,
6145 EL_AMOEBA_WET, ACTION_OTHER, -1
6148 Xdoor_1, TRUE, FALSE,
6149 EL_EM_GATE_1, -1, -1
6152 Xdoor_2, TRUE, FALSE,
6153 EL_EM_GATE_2, -1, -1
6156 Xdoor_3, TRUE, FALSE,
6157 EL_EM_GATE_3, -1, -1
6160 Xdoor_4, TRUE, FALSE,
6161 EL_EM_GATE_4, -1, -1
6164 Xdoor_5, TRUE, FALSE,
6165 EL_EMC_GATE_5, -1, -1
6168 Xdoor_6, TRUE, FALSE,
6169 EL_EMC_GATE_6, -1, -1
6172 Xdoor_7, TRUE, FALSE,
6173 EL_EMC_GATE_7, -1, -1
6176 Xdoor_8, TRUE, FALSE,
6177 EL_EMC_GATE_8, -1, -1
6180 Xkey_1, TRUE, FALSE,
6184 Xkey_2, TRUE, FALSE,
6188 Xkey_3, TRUE, FALSE,
6192 Xkey_4, TRUE, FALSE,
6196 Xkey_5, TRUE, FALSE,
6197 EL_EMC_KEY_5, -1, -1
6200 Xkey_6, TRUE, FALSE,
6201 EL_EMC_KEY_6, -1, -1
6204 Xkey_7, TRUE, FALSE,
6205 EL_EMC_KEY_7, -1, -1
6208 Xkey_8, TRUE, FALSE,
6209 EL_EMC_KEY_8, -1, -1
6212 Xwind_n, TRUE, FALSE,
6213 EL_BALLOON_SWITCH_UP, -1, -1
6216 Xwind_e, TRUE, FALSE,
6217 EL_BALLOON_SWITCH_RIGHT, -1, -1
6220 Xwind_s, TRUE, FALSE,
6221 EL_BALLOON_SWITCH_DOWN, -1, -1
6224 Xwind_w, TRUE, FALSE,
6225 EL_BALLOON_SWITCH_LEFT, -1, -1
6228 Xwind_nesw, TRUE, FALSE,
6229 EL_BALLOON_SWITCH_ANY, -1, -1
6232 Xwind_stop, TRUE, FALSE,
6233 EL_BALLOON_SWITCH_NONE, -1, -1
6237 EL_EM_EXIT_CLOSED, -1, -1
6240 Xexit_1, TRUE, FALSE,
6241 EL_EM_EXIT_OPEN, -1, -1
6244 Xexit_2, FALSE, FALSE,
6245 EL_EM_EXIT_OPEN, -1, -1
6248 Xexit_3, FALSE, FALSE,
6249 EL_EM_EXIT_OPEN, -1, -1
6252 Xdynamite, TRUE, FALSE,
6253 EL_EM_DYNAMITE, -1, -1
6256 Ydynamite_eat, FALSE, FALSE,
6257 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6260 Xdynamite_1, TRUE, FALSE,
6261 EL_EM_DYNAMITE_ACTIVE, -1, -1
6264 Xdynamite_2, FALSE, FALSE,
6265 EL_EM_DYNAMITE_ACTIVE, -1, -1
6268 Xdynamite_3, FALSE, FALSE,
6269 EL_EM_DYNAMITE_ACTIVE, -1, -1
6272 Xdynamite_4, FALSE, FALSE,
6273 EL_EM_DYNAMITE_ACTIVE, -1, -1
6276 Xbumper, TRUE, FALSE,
6277 EL_EMC_SPRING_BUMPER, -1, -1
6280 XbumperB, FALSE, FALSE,
6281 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6284 Xwheel, TRUE, FALSE,
6285 EL_ROBOT_WHEEL, -1, -1
6288 XwheelB, FALSE, FALSE,
6289 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6292 Xswitch, TRUE, FALSE,
6293 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6296 XswitchB, FALSE, FALSE,
6297 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6301 EL_QUICKSAND_EMPTY, -1, -1
6304 Xsand_stone, TRUE, FALSE,
6305 EL_QUICKSAND_FULL, -1, -1
6308 Xsand_stonein_1, FALSE, TRUE,
6309 EL_ROCK, ACTION_FILLING, -1
6312 Xsand_stonein_2, FALSE, TRUE,
6313 EL_ROCK, ACTION_FILLING, -1
6316 Xsand_stonein_3, FALSE, TRUE,
6317 EL_ROCK, ACTION_FILLING, -1
6320 Xsand_stonein_4, FALSE, TRUE,
6321 EL_ROCK, ACTION_FILLING, -1
6324 Xsand_stonesand_1, FALSE, FALSE,
6325 EL_QUICKSAND_EMPTYING, -1, -1
6328 Xsand_stonesand_2, FALSE, FALSE,
6329 EL_QUICKSAND_EMPTYING, -1, -1
6332 Xsand_stonesand_3, FALSE, FALSE,
6333 EL_QUICKSAND_EMPTYING, -1, -1
6336 Xsand_stonesand_4, FALSE, FALSE,
6337 EL_QUICKSAND_EMPTYING, -1, -1
6340 Xsand_stonesand_quickout_1, FALSE, FALSE,
6341 EL_QUICKSAND_EMPTYING, -1, -1
6344 Xsand_stonesand_quickout_2, FALSE, FALSE,
6345 EL_QUICKSAND_EMPTYING, -1, -1
6348 Xsand_stoneout_1, FALSE, FALSE,
6349 EL_ROCK, ACTION_EMPTYING, -1
6352 Xsand_stoneout_2, FALSE, FALSE,
6353 EL_ROCK, ACTION_EMPTYING, -1
6356 Xsand_sandstone_1, FALSE, FALSE,
6357 EL_QUICKSAND_FILLING, -1, -1
6360 Xsand_sandstone_2, FALSE, FALSE,
6361 EL_QUICKSAND_FILLING, -1, -1
6364 Xsand_sandstone_3, FALSE, FALSE,
6365 EL_QUICKSAND_FILLING, -1, -1
6368 Xsand_sandstone_4, FALSE, FALSE,
6369 EL_QUICKSAND_FILLING, -1, -1
6372 Xplant, TRUE, FALSE,
6373 EL_EMC_PLANT, -1, -1
6376 Yplant, FALSE, FALSE,
6377 EL_EMC_PLANT, -1, -1
6380 Xlenses, TRUE, FALSE,
6381 EL_EMC_LENSES, -1, -1
6384 Xmagnify, TRUE, FALSE,
6385 EL_EMC_MAGNIFIER, -1, -1
6388 Xdripper, TRUE, FALSE,
6389 EL_EMC_DRIPPER, -1, -1
6392 XdripperB, FALSE, FALSE,
6393 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6396 Xfake_blank, TRUE, FALSE,
6397 EL_INVISIBLE_WALL, -1, -1
6400 Xfake_blankB, FALSE, FALSE,
6401 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6404 Xfake_grass, TRUE, FALSE,
6405 EL_EMC_FAKE_GRASS, -1, -1
6408 Xfake_grassB, FALSE, FALSE,
6409 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6412 Xfake_door_1, TRUE, FALSE,
6413 EL_EM_GATE_1_GRAY, -1, -1
6416 Xfake_door_2, TRUE, FALSE,
6417 EL_EM_GATE_2_GRAY, -1, -1
6420 Xfake_door_3, TRUE, FALSE,
6421 EL_EM_GATE_3_GRAY, -1, -1
6424 Xfake_door_4, TRUE, FALSE,
6425 EL_EM_GATE_4_GRAY, -1, -1
6428 Xfake_door_5, TRUE, FALSE,
6429 EL_EMC_GATE_5_GRAY, -1, -1
6432 Xfake_door_6, TRUE, FALSE,
6433 EL_EMC_GATE_6_GRAY, -1, -1
6436 Xfake_door_7, TRUE, FALSE,
6437 EL_EMC_GATE_7_GRAY, -1, -1
6440 Xfake_door_8, TRUE, FALSE,
6441 EL_EMC_GATE_8_GRAY, -1, -1
6444 Xfake_acid_1, TRUE, FALSE,
6445 EL_EMC_FAKE_ACID, -1, -1
6448 Xfake_acid_2, FALSE, FALSE,
6449 EL_EMC_FAKE_ACID, -1, -1
6452 Xfake_acid_3, FALSE, FALSE,
6453 EL_EMC_FAKE_ACID, -1, -1
6456 Xfake_acid_4, FALSE, FALSE,
6457 EL_EMC_FAKE_ACID, -1, -1
6460 Xfake_acid_5, FALSE, FALSE,
6461 EL_EMC_FAKE_ACID, -1, -1
6464 Xfake_acid_6, FALSE, FALSE,
6465 EL_EMC_FAKE_ACID, -1, -1
6468 Xfake_acid_7, FALSE, FALSE,
6469 EL_EMC_FAKE_ACID, -1, -1
6472 Xfake_acid_8, FALSE, FALSE,
6473 EL_EMC_FAKE_ACID, -1, -1
6476 Xsteel_1, TRUE, FALSE,
6477 EL_STEELWALL, -1, -1
6480 Xsteel_2, TRUE, FALSE,
6481 EL_EMC_STEELWALL_2, -1, -1
6484 Xsteel_3, TRUE, FALSE,
6485 EL_EMC_STEELWALL_3, -1, -1
6488 Xsteel_4, TRUE, FALSE,
6489 EL_EMC_STEELWALL_4, -1, -1
6492 Xwall_1, TRUE, FALSE,
6496 Xwall_2, TRUE, FALSE,
6497 EL_EMC_WALL_14, -1, -1
6500 Xwall_3, TRUE, FALSE,
6501 EL_EMC_WALL_15, -1, -1
6504 Xwall_4, TRUE, FALSE,
6505 EL_EMC_WALL_16, -1, -1
6508 Xround_wall_1, TRUE, FALSE,
6509 EL_WALL_SLIPPERY, -1, -1
6512 Xround_wall_2, TRUE, FALSE,
6513 EL_EMC_WALL_SLIPPERY_2, -1, -1
6516 Xround_wall_3, TRUE, FALSE,
6517 EL_EMC_WALL_SLIPPERY_3, -1, -1
6520 Xround_wall_4, TRUE, FALSE,
6521 EL_EMC_WALL_SLIPPERY_4, -1, -1
6524 Xdecor_1, TRUE, FALSE,
6525 EL_EMC_WALL_8, -1, -1
6528 Xdecor_2, TRUE, FALSE,
6529 EL_EMC_WALL_6, -1, -1
6532 Xdecor_3, TRUE, FALSE,
6533 EL_EMC_WALL_4, -1, -1
6536 Xdecor_4, TRUE, FALSE,
6537 EL_EMC_WALL_7, -1, -1
6540 Xdecor_5, TRUE, FALSE,
6541 EL_EMC_WALL_5, -1, -1
6544 Xdecor_6, TRUE, FALSE,
6545 EL_EMC_WALL_9, -1, -1
6548 Xdecor_7, TRUE, FALSE,
6549 EL_EMC_WALL_10, -1, -1
6552 Xdecor_8, TRUE, FALSE,
6553 EL_EMC_WALL_1, -1, -1
6556 Xdecor_9, TRUE, FALSE,
6557 EL_EMC_WALL_2, -1, -1
6560 Xdecor_10, TRUE, FALSE,
6561 EL_EMC_WALL_3, -1, -1
6564 Xdecor_11, TRUE, FALSE,
6565 EL_EMC_WALL_11, -1, -1
6568 Xdecor_12, TRUE, FALSE,
6569 EL_EMC_WALL_12, -1, -1
6572 Xalpha_0, TRUE, FALSE,
6573 EL_CHAR('0'), -1, -1
6576 Xalpha_1, TRUE, FALSE,
6577 EL_CHAR('1'), -1, -1
6580 Xalpha_2, TRUE, FALSE,
6581 EL_CHAR('2'), -1, -1
6584 Xalpha_3, TRUE, FALSE,
6585 EL_CHAR('3'), -1, -1
6588 Xalpha_4, TRUE, FALSE,
6589 EL_CHAR('4'), -1, -1
6592 Xalpha_5, TRUE, FALSE,
6593 EL_CHAR('5'), -1, -1
6596 Xalpha_6, TRUE, FALSE,
6597 EL_CHAR('6'), -1, -1
6600 Xalpha_7, TRUE, FALSE,
6601 EL_CHAR('7'), -1, -1
6604 Xalpha_8, TRUE, FALSE,
6605 EL_CHAR('8'), -1, -1
6608 Xalpha_9, TRUE, FALSE,
6609 EL_CHAR('9'), -1, -1
6612 Xalpha_excla, TRUE, FALSE,
6613 EL_CHAR('!'), -1, -1
6616 Xalpha_quote, TRUE, FALSE,
6617 EL_CHAR('"'), -1, -1
6620 Xalpha_comma, TRUE, FALSE,
6621 EL_CHAR(','), -1, -1
6624 Xalpha_minus, TRUE, FALSE,
6625 EL_CHAR('-'), -1, -1
6628 Xalpha_perio, TRUE, FALSE,
6629 EL_CHAR('.'), -1, -1
6632 Xalpha_colon, TRUE, FALSE,
6633 EL_CHAR(':'), -1, -1
6636 Xalpha_quest, TRUE, FALSE,
6637 EL_CHAR('?'), -1, -1
6640 Xalpha_a, TRUE, FALSE,
6641 EL_CHAR('A'), -1, -1
6644 Xalpha_b, TRUE, FALSE,
6645 EL_CHAR('B'), -1, -1
6648 Xalpha_c, TRUE, FALSE,
6649 EL_CHAR('C'), -1, -1
6652 Xalpha_d, TRUE, FALSE,
6653 EL_CHAR('D'), -1, -1
6656 Xalpha_e, TRUE, FALSE,
6657 EL_CHAR('E'), -1, -1
6660 Xalpha_f, TRUE, FALSE,
6661 EL_CHAR('F'), -1, -1
6664 Xalpha_g, TRUE, FALSE,
6665 EL_CHAR('G'), -1, -1
6668 Xalpha_h, TRUE, FALSE,
6669 EL_CHAR('H'), -1, -1
6672 Xalpha_i, TRUE, FALSE,
6673 EL_CHAR('I'), -1, -1
6676 Xalpha_j, TRUE, FALSE,
6677 EL_CHAR('J'), -1, -1
6680 Xalpha_k, TRUE, FALSE,
6681 EL_CHAR('K'), -1, -1
6684 Xalpha_l, TRUE, FALSE,
6685 EL_CHAR('L'), -1, -1
6688 Xalpha_m, TRUE, FALSE,
6689 EL_CHAR('M'), -1, -1
6692 Xalpha_n, TRUE, FALSE,
6693 EL_CHAR('N'), -1, -1
6696 Xalpha_o, TRUE, FALSE,
6697 EL_CHAR('O'), -1, -1
6700 Xalpha_p, TRUE, FALSE,
6701 EL_CHAR('P'), -1, -1
6704 Xalpha_q, TRUE, FALSE,
6705 EL_CHAR('Q'), -1, -1
6708 Xalpha_r, TRUE, FALSE,
6709 EL_CHAR('R'), -1, -1
6712 Xalpha_s, TRUE, FALSE,
6713 EL_CHAR('S'), -1, -1
6716 Xalpha_t, TRUE, FALSE,
6717 EL_CHAR('T'), -1, -1
6720 Xalpha_u, TRUE, FALSE,
6721 EL_CHAR('U'), -1, -1
6724 Xalpha_v, TRUE, FALSE,
6725 EL_CHAR('V'), -1, -1
6728 Xalpha_w, TRUE, FALSE,
6729 EL_CHAR('W'), -1, -1
6732 Xalpha_x, TRUE, FALSE,
6733 EL_CHAR('X'), -1, -1
6736 Xalpha_y, TRUE, FALSE,
6737 EL_CHAR('Y'), -1, -1
6740 Xalpha_z, TRUE, FALSE,
6741 EL_CHAR('Z'), -1, -1
6744 Xalpha_arrow_e, TRUE, FALSE,
6745 EL_CHAR('>'), -1, -1
6748 Xalpha_arrow_w, TRUE, FALSE,
6749 EL_CHAR('<'), -1, -1
6752 Xalpha_copyr, TRUE, FALSE,
6753 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6757 Xboom_bug, FALSE, FALSE,
6758 EL_BUG, ACTION_EXPLODING, -1
6761 Xboom_bomb, FALSE, FALSE,
6762 EL_BOMB, ACTION_EXPLODING, -1
6765 Xboom_android, FALSE, FALSE,
6766 EL_EMC_ANDROID, ACTION_OTHER, -1
6769 Xboom_1, FALSE, FALSE,
6770 EL_DEFAULT, ACTION_EXPLODING, -1
6773 Xboom_2, FALSE, FALSE,
6774 EL_DEFAULT, ACTION_EXPLODING, -1
6777 Znormal, FALSE, FALSE,
6781 Zdynamite, FALSE, FALSE,
6785 Zplayer, FALSE, FALSE,
6789 ZBORDER, FALSE, FALSE,
6799 static struct Mapping_EM_to_RND_player
6808 em_player_mapping_list[] =
6812 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6816 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6820 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6824 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6828 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6832 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6836 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6840 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6844 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6848 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6852 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6856 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6860 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6864 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6868 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6872 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6876 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6880 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6884 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6888 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6892 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6896 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6900 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6904 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6908 EL_PLAYER_1, ACTION_DEFAULT, -1,
6912 EL_PLAYER_2, ACTION_DEFAULT, -1,
6916 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6920 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6924 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6928 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6932 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6936 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6940 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6944 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6948 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6952 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6956 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6960 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6964 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6968 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6972 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6976 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6980 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6984 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6988 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6992 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6996 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
7000 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
7004 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
7008 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7012 EL_PLAYER_3, ACTION_DEFAULT, -1,
7016 EL_PLAYER_4, ACTION_DEFAULT, -1,
7025 int map_element_RND_to_EM(int element_rnd)
7027 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7028 static boolean mapping_initialized = FALSE;
7030 if (!mapping_initialized)
7034 /* return "Xalpha_quest" for all undefined elements in mapping array */
7035 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7036 mapping_RND_to_EM[i] = Xalpha_quest;
7038 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7039 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7040 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7041 em_object_mapping_list[i].element_em;
7043 mapping_initialized = TRUE;
7046 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7047 return mapping_RND_to_EM[element_rnd];
7049 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7054 int map_element_EM_to_RND(int element_em)
7056 static unsigned short mapping_EM_to_RND[TILE_MAX];
7057 static boolean mapping_initialized = FALSE;
7059 if (!mapping_initialized)
7063 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
7064 for (i = 0; i < TILE_MAX; i++)
7065 mapping_EM_to_RND[i] = EL_UNKNOWN;
7067 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7068 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7069 em_object_mapping_list[i].element_rnd;
7071 mapping_initialized = TRUE;
7074 if (element_em >= 0 && element_em < TILE_MAX)
7075 return mapping_EM_to_RND[element_em];
7077 Error(ERR_WARN, "invalid EM level element %d", element_em);
7082 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7084 struct LevelInfo_EM *level_em = level->native_em_level;
7085 struct LEVEL *lev = level_em->lev;
7088 for (i = 0; i < TILE_MAX; i++)
7089 lev->android_array[i] = Xblank;
7091 for (i = 0; i < level->num_android_clone_elements; i++)
7093 int element_rnd = level->android_clone_element[i];
7094 int element_em = map_element_RND_to_EM(element_rnd);
7096 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7097 if (em_object_mapping_list[j].element_rnd == element_rnd)
7098 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7102 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7104 struct LevelInfo_EM *level_em = level->native_em_level;
7105 struct LEVEL *lev = level_em->lev;
7108 level->num_android_clone_elements = 0;
7110 for (i = 0; i < TILE_MAX; i++)
7112 int element_em = lev->android_array[i];
7114 boolean element_found = FALSE;
7116 if (element_em == Xblank)
7119 element_rnd = map_element_EM_to_RND(element_em);
7121 for (j = 0; j < level->num_android_clone_elements; j++)
7122 if (level->android_clone_element[j] == element_rnd)
7123 element_found = TRUE;
7127 level->android_clone_element[level->num_android_clone_elements++] =
7130 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7135 if (level->num_android_clone_elements == 0)
7137 level->num_android_clone_elements = 1;
7138 level->android_clone_element[0] = EL_EMPTY;
7142 int map_direction_RND_to_EM(int direction)
7144 return (direction == MV_UP ? 0 :
7145 direction == MV_RIGHT ? 1 :
7146 direction == MV_DOWN ? 2 :
7147 direction == MV_LEFT ? 3 :
7151 int map_direction_EM_to_RND(int direction)
7153 return (direction == 0 ? MV_UP :
7154 direction == 1 ? MV_RIGHT :
7155 direction == 2 ? MV_DOWN :
7156 direction == 3 ? MV_LEFT :
7160 int map_element_RND_to_SP(int element_rnd)
7162 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
7164 if (element_rnd >= EL_SP_START &&
7165 element_rnd <= EL_SP_END)
7166 element_sp = element_rnd - EL_SP_START;
7167 else if (element_rnd == EL_EMPTY_SPACE)
7169 else if (element_rnd == EL_INVISIBLE_WALL)
7175 int map_element_SP_to_RND(int element_sp)
7177 int element_rnd = EL_UNKNOWN;
7179 if (element_sp >= 0x00 &&
7181 element_rnd = EL_SP_START + element_sp;
7182 else if (element_sp == 0x28)
7183 element_rnd = EL_INVISIBLE_WALL;
7188 int map_action_SP_to_RND(int action_sp)
7192 case actActive: return ACTION_ACTIVE;
7193 case actImpact: return ACTION_IMPACT;
7194 case actExploding: return ACTION_EXPLODING;
7195 case actDigging: return ACTION_DIGGING;
7196 case actSnapping: return ACTION_SNAPPING;
7197 case actCollecting: return ACTION_COLLECTING;
7198 case actPassing: return ACTION_PASSING;
7199 case actPushing: return ACTION_PUSHING;
7200 case actDropping: return ACTION_DROPPING;
7202 default: return ACTION_DEFAULT;
7206 int get_next_element(int element)
7210 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7211 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7212 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7213 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7214 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7215 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7216 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7217 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7218 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7219 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7220 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7222 default: return element;
7226 int el_act_dir2img(int element, int action, int direction)
7228 element = GFX_ELEMENT(element);
7229 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7231 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7232 return element_info[element].direction_graphic[action][direction];
7235 static int el_act_dir2crm(int element, int action, int direction)
7237 element = GFX_ELEMENT(element);
7238 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7240 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7241 return element_info[element].direction_crumbled[action][direction];
7244 int el_act2img(int element, int action)
7246 element = GFX_ELEMENT(element);
7248 return element_info[element].graphic[action];
7251 int el_act2crm(int element, int action)
7253 element = GFX_ELEMENT(element);
7255 return element_info[element].crumbled[action];
7258 int el_dir2img(int element, int direction)
7260 element = GFX_ELEMENT(element);
7262 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7265 int el2baseimg(int element)
7267 return element_info[element].graphic[ACTION_DEFAULT];
7270 int el2img(int element)
7272 element = GFX_ELEMENT(element);
7274 return element_info[element].graphic[ACTION_DEFAULT];
7277 int el2edimg(int element)
7279 element = GFX_ELEMENT(element);
7281 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7284 int el2preimg(int element)
7286 element = GFX_ELEMENT(element);
7288 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7291 int el2panelimg(int element)
7293 element = GFX_ELEMENT(element);
7295 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7298 int font2baseimg(int font_nr)
7300 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7303 int getBeltNrFromBeltElement(int element)
7305 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7306 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7307 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
7310 int getBeltNrFromBeltActiveElement(int element)
7312 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
7313 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
7314 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
7317 int getBeltNrFromBeltSwitchElement(int element)
7319 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
7320 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
7321 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
7324 int getBeltDirNrFromBeltElement(int element)
7326 static int belt_base_element[4] =
7328 EL_CONVEYOR_BELT_1_LEFT,
7329 EL_CONVEYOR_BELT_2_LEFT,
7330 EL_CONVEYOR_BELT_3_LEFT,
7331 EL_CONVEYOR_BELT_4_LEFT
7334 int belt_nr = getBeltNrFromBeltElement(element);
7335 int belt_dir_nr = element - belt_base_element[belt_nr];
7337 return (belt_dir_nr % 3);
7340 int getBeltDirNrFromBeltSwitchElement(int element)
7342 static int belt_base_element[4] =
7344 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7345 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7346 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7347 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7350 int belt_nr = getBeltNrFromBeltSwitchElement(element);
7351 int belt_dir_nr = element - belt_base_element[belt_nr];
7353 return (belt_dir_nr % 3);
7356 int getBeltDirFromBeltElement(int element)
7358 static int belt_move_dir[3] =
7365 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7367 return belt_move_dir[belt_dir_nr];
7370 int getBeltDirFromBeltSwitchElement(int element)
7372 static int belt_move_dir[3] =
7379 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7381 return belt_move_dir[belt_dir_nr];
7384 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7386 static int belt_base_element[4] =
7388 EL_CONVEYOR_BELT_1_LEFT,
7389 EL_CONVEYOR_BELT_2_LEFT,
7390 EL_CONVEYOR_BELT_3_LEFT,
7391 EL_CONVEYOR_BELT_4_LEFT
7394 return belt_base_element[belt_nr] + belt_dir_nr;
7397 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7399 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7401 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7404 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7406 static int belt_base_element[4] =
7408 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7409 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7410 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7411 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7414 return belt_base_element[belt_nr] + belt_dir_nr;
7417 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7419 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7421 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7424 boolean getTeamMode_EM()
7426 return game.team_mode;
7429 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7431 int game_frame_delay_value;
7433 game_frame_delay_value =
7434 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7435 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7438 if (tape.playing && tape.warp_forward && !tape.pausing)
7439 game_frame_delay_value = 0;
7441 return game_frame_delay_value;
7444 unsigned int InitRND(int seed)
7446 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7447 return InitEngineRandom_EM(seed);
7448 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7449 return InitEngineRandom_SP(seed);
7451 return InitEngineRandom_RND(seed);
7454 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7455 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7457 inline static int get_effective_element_EM(int tile, int frame_em)
7459 int element = object_mapping[tile].element_rnd;
7460 int action = object_mapping[tile].action;
7461 boolean is_backside = object_mapping[tile].is_backside;
7462 boolean action_removing = (action == ACTION_DIGGING ||
7463 action == ACTION_SNAPPING ||
7464 action == ACTION_COLLECTING);
7470 case Yacid_splash_eB:
7471 case Yacid_splash_wB:
7472 return (frame_em > 5 ? EL_EMPTY : element);
7478 else /* frame_em == 7 */
7482 case Yacid_splash_eB:
7483 case Yacid_splash_wB:
7486 case Yemerald_stone:
7489 case Ydiamond_stone:
7493 case Xdrip_stretchB:
7512 case Xsand_stonein_1:
7513 case Xsand_stonein_2:
7514 case Xsand_stonein_3:
7515 case Xsand_stonein_4:
7519 return (is_backside || action_removing ? EL_EMPTY : element);
7524 inline static boolean check_linear_animation_EM(int tile)
7528 case Xsand_stonesand_1:
7529 case Xsand_stonesand_quickout_1:
7530 case Xsand_sandstone_1:
7531 case Xsand_stonein_1:
7532 case Xsand_stoneout_1:
7551 case Yacid_splash_eB:
7552 case Yacid_splash_wB:
7553 case Yemerald_stone:
7560 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7561 boolean has_crumbled_graphics,
7562 int crumbled, int sync_frame)
7564 /* if element can be crumbled, but certain action graphics are just empty
7565 space (like instantly snapping sand to empty space in 1 frame), do not
7566 treat these empty space graphics as crumbled graphics in EMC engine */
7567 if (crumbled == IMG_EMPTY_SPACE)
7568 has_crumbled_graphics = FALSE;
7570 if (has_crumbled_graphics)
7572 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7573 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7574 g_crumbled->anim_delay,
7575 g_crumbled->anim_mode,
7576 g_crumbled->anim_start_frame,
7579 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7580 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7582 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7583 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
7585 g_em->has_crumbled_graphics = TRUE;
7589 g_em->crumbled_bitmap = NULL;
7590 g_em->crumbled_src_x = 0;
7591 g_em->crumbled_src_y = 0;
7592 g_em->crumbled_border_size = 0;
7593 g_em->crumbled_tile_size = 0;
7595 g_em->has_crumbled_graphics = FALSE;
7599 void ResetGfxAnimation_EM(int x, int y, int tile)
7604 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7605 int tile, int frame_em, int x, int y)
7607 int action = object_mapping[tile].action;
7608 int direction = object_mapping[tile].direction;
7609 int effective_element = get_effective_element_EM(tile, frame_em);
7610 int graphic = (direction == MV_NONE ?
7611 el_act2img(effective_element, action) :
7612 el_act_dir2img(effective_element, action, direction));
7613 struct GraphicInfo *g = &graphic_info[graphic];
7615 boolean action_removing = (action == ACTION_DIGGING ||
7616 action == ACTION_SNAPPING ||
7617 action == ACTION_COLLECTING);
7618 boolean action_moving = (action == ACTION_FALLING ||
7619 action == ACTION_MOVING ||
7620 action == ACTION_PUSHING ||
7621 action == ACTION_EATING ||
7622 action == ACTION_FILLING ||
7623 action == ACTION_EMPTYING);
7624 boolean action_falling = (action == ACTION_FALLING ||
7625 action == ACTION_FILLING ||
7626 action == ACTION_EMPTYING);
7628 /* special case: graphic uses "2nd movement tile" and has defined
7629 7 frames for movement animation (or less) => use default graphic
7630 for last (8th) frame which ends the movement animation */
7631 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7633 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7634 graphic = (direction == MV_NONE ?
7635 el_act2img(effective_element, action) :
7636 el_act_dir2img(effective_element, action, direction));
7638 g = &graphic_info[graphic];
7641 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7645 else if (action_moving)
7647 boolean is_backside = object_mapping[tile].is_backside;
7651 int direction = object_mapping[tile].direction;
7652 int move_dir = (action_falling ? MV_DOWN : direction);
7657 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7658 if (g->double_movement && frame_em == 0)
7662 if (move_dir == MV_LEFT)
7663 GfxFrame[x - 1][y] = GfxFrame[x][y];
7664 else if (move_dir == MV_RIGHT)
7665 GfxFrame[x + 1][y] = GfxFrame[x][y];
7666 else if (move_dir == MV_UP)
7667 GfxFrame[x][y - 1] = GfxFrame[x][y];
7668 else if (move_dir == MV_DOWN)
7669 GfxFrame[x][y + 1] = GfxFrame[x][y];
7676 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7677 if (tile == Xsand_stonesand_quickout_1 ||
7678 tile == Xsand_stonesand_quickout_2)
7682 if (graphic_info[graphic].anim_global_sync)
7683 sync_frame = FrameCounter;
7684 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7685 sync_frame = GfxFrame[x][y];
7687 sync_frame = 0; /* playfield border (pseudo steel) */
7689 SetRandomAnimationValue(x, y);
7691 int frame = getAnimationFrame(g->anim_frames,
7694 g->anim_start_frame,
7697 g_em->unique_identifier =
7698 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7701 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7702 int tile, int frame_em, int x, int y)
7704 int action = object_mapping[tile].action;
7705 int direction = object_mapping[tile].direction;
7706 boolean is_backside = object_mapping[tile].is_backside;
7707 int effective_element = get_effective_element_EM(tile, frame_em);
7708 int effective_action = action;
7709 int graphic = (direction == MV_NONE ?
7710 el_act2img(effective_element, effective_action) :
7711 el_act_dir2img(effective_element, effective_action,
7713 int crumbled = (direction == MV_NONE ?
7714 el_act2crm(effective_element, effective_action) :
7715 el_act_dir2crm(effective_element, effective_action,
7717 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7718 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7719 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7720 struct GraphicInfo *g = &graphic_info[graphic];
7723 /* special case: graphic uses "2nd movement tile" and has defined
7724 7 frames for movement animation (or less) => use default graphic
7725 for last (8th) frame which ends the movement animation */
7726 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7728 effective_action = ACTION_DEFAULT;
7729 graphic = (direction == MV_NONE ?
7730 el_act2img(effective_element, effective_action) :
7731 el_act_dir2img(effective_element, effective_action,
7733 crumbled = (direction == MV_NONE ?
7734 el_act2crm(effective_element, effective_action) :
7735 el_act_dir2crm(effective_element, effective_action,
7738 g = &graphic_info[graphic];
7741 if (graphic_info[graphic].anim_global_sync)
7742 sync_frame = FrameCounter;
7743 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7744 sync_frame = GfxFrame[x][y];
7746 sync_frame = 0; /* playfield border (pseudo steel) */
7748 SetRandomAnimationValue(x, y);
7750 int frame = getAnimationFrame(g->anim_frames,
7753 g->anim_start_frame,
7756 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7757 g->double_movement && is_backside);
7759 /* (updating the "crumbled" graphic definitions is probably not really needed,
7760 as animations for crumbled graphics can't be longer than one EMC cycle) */
7761 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7765 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7766 int player_nr, int anim, int frame_em)
7768 int element = player_mapping[player_nr][anim].element_rnd;
7769 int action = player_mapping[player_nr][anim].action;
7770 int direction = player_mapping[player_nr][anim].direction;
7771 int graphic = (direction == MV_NONE ?
7772 el_act2img(element, action) :
7773 el_act_dir2img(element, action, direction));
7774 struct GraphicInfo *g = &graphic_info[graphic];
7777 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7779 stored_player[player_nr].StepFrame = frame_em;
7781 sync_frame = stored_player[player_nr].Frame;
7783 int frame = getAnimationFrame(g->anim_frames,
7786 g->anim_start_frame,
7789 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7790 &g_em->src_x, &g_em->src_y, FALSE);
7793 void InitGraphicInfo_EM(void)
7798 int num_em_gfx_errors = 0;
7800 if (graphic_info_em_object[0][0].bitmap == NULL)
7802 /* EM graphics not yet initialized in em_open_all() */
7807 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7810 /* always start with reliable default values */
7811 for (i = 0; i < TILE_MAX; i++)
7813 object_mapping[i].element_rnd = EL_UNKNOWN;
7814 object_mapping[i].is_backside = FALSE;
7815 object_mapping[i].action = ACTION_DEFAULT;
7816 object_mapping[i].direction = MV_NONE;
7819 /* always start with reliable default values */
7820 for (p = 0; p < MAX_PLAYERS; p++)
7822 for (i = 0; i < SPR_MAX; i++)
7824 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7825 player_mapping[p][i].action = ACTION_DEFAULT;
7826 player_mapping[p][i].direction = MV_NONE;
7830 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7832 int e = em_object_mapping_list[i].element_em;
7834 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7835 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7837 if (em_object_mapping_list[i].action != -1)
7838 object_mapping[e].action = em_object_mapping_list[i].action;
7840 if (em_object_mapping_list[i].direction != -1)
7841 object_mapping[e].direction =
7842 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7845 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7847 int a = em_player_mapping_list[i].action_em;
7848 int p = em_player_mapping_list[i].player_nr;
7850 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7852 if (em_player_mapping_list[i].action != -1)
7853 player_mapping[p][a].action = em_player_mapping_list[i].action;
7855 if (em_player_mapping_list[i].direction != -1)
7856 player_mapping[p][a].direction =
7857 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7860 for (i = 0; i < TILE_MAX; i++)
7862 int element = object_mapping[i].element_rnd;
7863 int action = object_mapping[i].action;
7864 int direction = object_mapping[i].direction;
7865 boolean is_backside = object_mapping[i].is_backside;
7866 boolean action_exploding = ((action == ACTION_EXPLODING ||
7867 action == ACTION_SMASHED_BY_ROCK ||
7868 action == ACTION_SMASHED_BY_SPRING) &&
7869 element != EL_DIAMOND);
7870 boolean action_active = (action == ACTION_ACTIVE);
7871 boolean action_other = (action == ACTION_OTHER);
7873 for (j = 0; j < 8; j++)
7875 int effective_element = get_effective_element_EM(i, j);
7876 int effective_action = (j < 7 ? action :
7877 i == Xdrip_stretch ? action :
7878 i == Xdrip_stretchB ? action :
7879 i == Ydrip_s1 ? action :
7880 i == Ydrip_s1B ? action :
7881 i == Xball_1B ? action :
7882 i == Xball_2 ? action :
7883 i == Xball_2B ? action :
7884 i == Yball_eat ? action :
7885 i == Ykey_1_eat ? action :
7886 i == Ykey_2_eat ? action :
7887 i == Ykey_3_eat ? action :
7888 i == Ykey_4_eat ? action :
7889 i == Ykey_5_eat ? action :
7890 i == Ykey_6_eat ? action :
7891 i == Ykey_7_eat ? action :
7892 i == Ykey_8_eat ? action :
7893 i == Ylenses_eat ? action :
7894 i == Ymagnify_eat ? action :
7895 i == Ygrass_eat ? action :
7896 i == Ydirt_eat ? action :
7897 i == Xsand_stonein_1 ? action :
7898 i == Xsand_stonein_2 ? action :
7899 i == Xsand_stonein_3 ? action :
7900 i == Xsand_stonein_4 ? action :
7901 i == Xsand_stoneout_1 ? action :
7902 i == Xsand_stoneout_2 ? action :
7903 i == Xboom_android ? ACTION_EXPLODING :
7904 action_exploding ? ACTION_EXPLODING :
7905 action_active ? action :
7906 action_other ? action :
7908 int graphic = (el_act_dir2img(effective_element, effective_action,
7910 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7912 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7913 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7914 boolean has_action_graphics = (graphic != base_graphic);
7915 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7916 struct GraphicInfo *g = &graphic_info[graphic];
7917 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7920 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7921 boolean special_animation = (action != ACTION_DEFAULT &&
7922 g->anim_frames == 3 &&
7923 g->anim_delay == 2 &&
7924 g->anim_mode & ANIM_LINEAR);
7925 int sync_frame = (i == Xdrip_stretch ? 7 :
7926 i == Xdrip_stretchB ? 7 :
7927 i == Ydrip_s2 ? j + 8 :
7928 i == Ydrip_s2B ? j + 8 :
7937 i == Xfake_acid_1 ? 0 :
7938 i == Xfake_acid_2 ? 10 :
7939 i == Xfake_acid_3 ? 20 :
7940 i == Xfake_acid_4 ? 30 :
7941 i == Xfake_acid_5 ? 40 :
7942 i == Xfake_acid_6 ? 50 :
7943 i == Xfake_acid_7 ? 60 :
7944 i == Xfake_acid_8 ? 70 :
7946 i == Xball_2B ? j + 8 :
7947 i == Yball_eat ? j + 1 :
7948 i == Ykey_1_eat ? j + 1 :
7949 i == Ykey_2_eat ? j + 1 :
7950 i == Ykey_3_eat ? j + 1 :
7951 i == Ykey_4_eat ? j + 1 :
7952 i == Ykey_5_eat ? j + 1 :
7953 i == Ykey_6_eat ? j + 1 :
7954 i == Ykey_7_eat ? j + 1 :
7955 i == Ykey_8_eat ? j + 1 :
7956 i == Ylenses_eat ? j + 1 :
7957 i == Ymagnify_eat ? j + 1 :
7958 i == Ygrass_eat ? j + 1 :
7959 i == Ydirt_eat ? j + 1 :
7960 i == Xamoeba_1 ? 0 :
7961 i == Xamoeba_2 ? 1 :
7962 i == Xamoeba_3 ? 2 :
7963 i == Xamoeba_4 ? 3 :
7964 i == Xamoeba_5 ? 0 :
7965 i == Xamoeba_6 ? 1 :
7966 i == Xamoeba_7 ? 2 :
7967 i == Xamoeba_8 ? 3 :
7968 i == Xexit_2 ? j + 8 :
7969 i == Xexit_3 ? j + 16 :
7970 i == Xdynamite_1 ? 0 :
7971 i == Xdynamite_2 ? 8 :
7972 i == Xdynamite_3 ? 16 :
7973 i == Xdynamite_4 ? 24 :
7974 i == Xsand_stonein_1 ? j + 1 :
7975 i == Xsand_stonein_2 ? j + 9 :
7976 i == Xsand_stonein_3 ? j + 17 :
7977 i == Xsand_stonein_4 ? j + 25 :
7978 i == Xsand_stoneout_1 && j == 0 ? 0 :
7979 i == Xsand_stoneout_1 && j == 1 ? 0 :
7980 i == Xsand_stoneout_1 && j == 2 ? 1 :
7981 i == Xsand_stoneout_1 && j == 3 ? 2 :
7982 i == Xsand_stoneout_1 && j == 4 ? 2 :
7983 i == Xsand_stoneout_1 && j == 5 ? 3 :
7984 i == Xsand_stoneout_1 && j == 6 ? 4 :
7985 i == Xsand_stoneout_1 && j == 7 ? 4 :
7986 i == Xsand_stoneout_2 && j == 0 ? 5 :
7987 i == Xsand_stoneout_2 && j == 1 ? 6 :
7988 i == Xsand_stoneout_2 && j == 2 ? 7 :
7989 i == Xsand_stoneout_2 && j == 3 ? 8 :
7990 i == Xsand_stoneout_2 && j == 4 ? 9 :
7991 i == Xsand_stoneout_2 && j == 5 ? 11 :
7992 i == Xsand_stoneout_2 && j == 6 ? 13 :
7993 i == Xsand_stoneout_2 && j == 7 ? 15 :
7994 i == Xboom_bug && j == 1 ? 2 :
7995 i == Xboom_bug && j == 2 ? 2 :
7996 i == Xboom_bug && j == 3 ? 4 :
7997 i == Xboom_bug && j == 4 ? 4 :
7998 i == Xboom_bug && j == 5 ? 2 :
7999 i == Xboom_bug && j == 6 ? 2 :
8000 i == Xboom_bug && j == 7 ? 0 :
8001 i == Xboom_bomb && j == 1 ? 2 :
8002 i == Xboom_bomb && j == 2 ? 2 :
8003 i == Xboom_bomb && j == 3 ? 4 :
8004 i == Xboom_bomb && j == 4 ? 4 :
8005 i == Xboom_bomb && j == 5 ? 2 :
8006 i == Xboom_bomb && j == 6 ? 2 :
8007 i == Xboom_bomb && j == 7 ? 0 :
8008 i == Xboom_android && j == 7 ? 6 :
8009 i == Xboom_1 && j == 1 ? 2 :
8010 i == Xboom_1 && j == 2 ? 2 :
8011 i == Xboom_1 && j == 3 ? 4 :
8012 i == Xboom_1 && j == 4 ? 4 :
8013 i == Xboom_1 && j == 5 ? 6 :
8014 i == Xboom_1 && j == 6 ? 6 :
8015 i == Xboom_1 && j == 7 ? 8 :
8016 i == Xboom_2 && j == 0 ? 8 :
8017 i == Xboom_2 && j == 1 ? 8 :
8018 i == Xboom_2 && j == 2 ? 10 :
8019 i == Xboom_2 && j == 3 ? 10 :
8020 i == Xboom_2 && j == 4 ? 10 :
8021 i == Xboom_2 && j == 5 ? 12 :
8022 i == Xboom_2 && j == 6 ? 12 :
8023 i == Xboom_2 && j == 7 ? 12 :
8024 special_animation && j == 4 ? 3 :
8025 effective_action != action ? 0 :
8029 Bitmap *debug_bitmap = g_em->bitmap;
8030 int debug_src_x = g_em->src_x;
8031 int debug_src_y = g_em->src_y;
8034 int frame = getAnimationFrame(g->anim_frames,
8037 g->anim_start_frame,
8040 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8041 g->double_movement && is_backside);
8043 g_em->bitmap = src_bitmap;
8044 g_em->src_x = src_x;
8045 g_em->src_y = src_y;
8046 g_em->src_offset_x = 0;
8047 g_em->src_offset_y = 0;
8048 g_em->dst_offset_x = 0;
8049 g_em->dst_offset_y = 0;
8050 g_em->width = TILEX;
8051 g_em->height = TILEY;
8053 g_em->preserve_background = FALSE;
8055 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8058 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8059 effective_action == ACTION_MOVING ||
8060 effective_action == ACTION_PUSHING ||
8061 effective_action == ACTION_EATING)) ||
8062 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8063 effective_action == ACTION_EMPTYING)))
8066 (effective_action == ACTION_FALLING ||
8067 effective_action == ACTION_FILLING ||
8068 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8069 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8070 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8071 int num_steps = (i == Ydrip_s1 ? 16 :
8072 i == Ydrip_s1B ? 16 :
8073 i == Ydrip_s2 ? 16 :
8074 i == Ydrip_s2B ? 16 :
8075 i == Xsand_stonein_1 ? 32 :
8076 i == Xsand_stonein_2 ? 32 :
8077 i == Xsand_stonein_3 ? 32 :
8078 i == Xsand_stonein_4 ? 32 :
8079 i == Xsand_stoneout_1 ? 16 :
8080 i == Xsand_stoneout_2 ? 16 : 8);
8081 int cx = ABS(dx) * (TILEX / num_steps);
8082 int cy = ABS(dy) * (TILEY / num_steps);
8083 int step_frame = (i == Ydrip_s2 ? j + 8 :
8084 i == Ydrip_s2B ? j + 8 :
8085 i == Xsand_stonein_2 ? j + 8 :
8086 i == Xsand_stonein_3 ? j + 16 :
8087 i == Xsand_stonein_4 ? j + 24 :
8088 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8089 int step = (is_backside ? step_frame : num_steps - step_frame);
8091 if (is_backside) /* tile where movement starts */
8093 if (dx < 0 || dy < 0)
8095 g_em->src_offset_x = cx * step;
8096 g_em->src_offset_y = cy * step;
8100 g_em->dst_offset_x = cx * step;
8101 g_em->dst_offset_y = cy * step;
8104 else /* tile where movement ends */
8106 if (dx < 0 || dy < 0)
8108 g_em->dst_offset_x = cx * step;
8109 g_em->dst_offset_y = cy * step;
8113 g_em->src_offset_x = cx * step;
8114 g_em->src_offset_y = cy * step;
8118 g_em->width = TILEX - cx * step;
8119 g_em->height = TILEY - cy * step;
8122 /* create unique graphic identifier to decide if tile must be redrawn */
8123 /* bit 31 - 16 (16 bit): EM style graphic
8124 bit 15 - 12 ( 4 bit): EM style frame
8125 bit 11 - 6 ( 6 bit): graphic width
8126 bit 5 - 0 ( 6 bit): graphic height */
8127 g_em->unique_identifier =
8128 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8132 /* skip check for EMC elements not contained in original EMC artwork */
8133 if (element == EL_EMC_FAKE_ACID)
8136 if (g_em->bitmap != debug_bitmap ||
8137 g_em->src_x != debug_src_x ||
8138 g_em->src_y != debug_src_y ||
8139 g_em->src_offset_x != 0 ||
8140 g_em->src_offset_y != 0 ||
8141 g_em->dst_offset_x != 0 ||
8142 g_em->dst_offset_y != 0 ||
8143 g_em->width != TILEX ||
8144 g_em->height != TILEY)
8146 static int last_i = -1;
8154 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8155 i, element, element_info[element].token_name,
8156 element_action_info[effective_action].suffix, direction);
8158 if (element != effective_element)
8159 printf(" [%d ('%s')]",
8161 element_info[effective_element].token_name);
8165 if (g_em->bitmap != debug_bitmap)
8166 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8167 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8169 if (g_em->src_x != debug_src_x ||
8170 g_em->src_y != debug_src_y)
8171 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8172 j, (is_backside ? 'B' : 'F'),
8173 g_em->src_x, g_em->src_y,
8174 g_em->src_x / 32, g_em->src_y / 32,
8175 debug_src_x, debug_src_y,
8176 debug_src_x / 32, debug_src_y / 32);
8178 if (g_em->src_offset_x != 0 ||
8179 g_em->src_offset_y != 0 ||
8180 g_em->dst_offset_x != 0 ||
8181 g_em->dst_offset_y != 0)
8182 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8184 g_em->src_offset_x, g_em->src_offset_y,
8185 g_em->dst_offset_x, g_em->dst_offset_y);
8187 if (g_em->width != TILEX ||
8188 g_em->height != TILEY)
8189 printf(" %d (%d): size %d,%d should be %d,%d\n",
8191 g_em->width, g_em->height, TILEX, TILEY);
8193 num_em_gfx_errors++;
8200 for (i = 0; i < TILE_MAX; i++)
8202 for (j = 0; j < 8; j++)
8204 int element = object_mapping[i].element_rnd;
8205 int action = object_mapping[i].action;
8206 int direction = object_mapping[i].direction;
8207 boolean is_backside = object_mapping[i].is_backside;
8208 int graphic_action = el_act_dir2img(element, action, direction);
8209 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8211 if ((action == ACTION_SMASHED_BY_ROCK ||
8212 action == ACTION_SMASHED_BY_SPRING ||
8213 action == ACTION_EATING) &&
8214 graphic_action == graphic_default)
8216 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8217 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8218 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8219 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8222 /* no separate animation for "smashed by rock" -- use rock instead */
8223 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8224 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8226 g_em->bitmap = g_xx->bitmap;
8227 g_em->src_x = g_xx->src_x;
8228 g_em->src_y = g_xx->src_y;
8229 g_em->src_offset_x = g_xx->src_offset_x;
8230 g_em->src_offset_y = g_xx->src_offset_y;
8231 g_em->dst_offset_x = g_xx->dst_offset_x;
8232 g_em->dst_offset_y = g_xx->dst_offset_y;
8233 g_em->width = g_xx->width;
8234 g_em->height = g_xx->height;
8235 g_em->unique_identifier = g_xx->unique_identifier;
8238 g_em->preserve_background = TRUE;
8243 for (p = 0; p < MAX_PLAYERS; p++)
8245 for (i = 0; i < SPR_MAX; i++)
8247 int element = player_mapping[p][i].element_rnd;
8248 int action = player_mapping[p][i].action;
8249 int direction = player_mapping[p][i].direction;
8251 for (j = 0; j < 8; j++)
8253 int effective_element = element;
8254 int effective_action = action;
8255 int graphic = (direction == MV_NONE ?
8256 el_act2img(effective_element, effective_action) :
8257 el_act_dir2img(effective_element, effective_action,
8259 struct GraphicInfo *g = &graphic_info[graphic];
8260 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8266 Bitmap *debug_bitmap = g_em->bitmap;
8267 int debug_src_x = g_em->src_x;
8268 int debug_src_y = g_em->src_y;
8271 int frame = getAnimationFrame(g->anim_frames,
8274 g->anim_start_frame,
8277 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8279 g_em->bitmap = src_bitmap;
8280 g_em->src_x = src_x;
8281 g_em->src_y = src_y;
8282 g_em->src_offset_x = 0;
8283 g_em->src_offset_y = 0;
8284 g_em->dst_offset_x = 0;
8285 g_em->dst_offset_y = 0;
8286 g_em->width = TILEX;
8287 g_em->height = TILEY;
8291 /* skip check for EMC elements not contained in original EMC artwork */
8292 if (element == EL_PLAYER_3 ||
8293 element == EL_PLAYER_4)
8296 if (g_em->bitmap != debug_bitmap ||
8297 g_em->src_x != debug_src_x ||
8298 g_em->src_y != debug_src_y)
8300 static int last_i = -1;
8308 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
8309 p, i, element, element_info[element].token_name,
8310 element_action_info[effective_action].suffix, direction);
8312 if (element != effective_element)
8313 printf(" [%d ('%s')]",
8315 element_info[effective_element].token_name);
8319 if (g_em->bitmap != debug_bitmap)
8320 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
8321 j, (int)(g_em->bitmap), (int)(debug_bitmap));
8323 if (g_em->src_x != debug_src_x ||
8324 g_em->src_y != debug_src_y)
8325 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8327 g_em->src_x, g_em->src_y,
8328 g_em->src_x / 32, g_em->src_y / 32,
8329 debug_src_x, debug_src_y,
8330 debug_src_x / 32, debug_src_y / 32);
8332 num_em_gfx_errors++;
8342 printf("::: [%d errors found]\n", num_em_gfx_errors);
8348 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
8349 boolean any_player_moving,
8350 boolean any_player_snapping,
8351 boolean any_player_dropping)
8353 if (frame == 0 && !any_player_dropping)
8355 if (!local_player->was_waiting)
8357 if (!CheckSaveEngineSnapshotToList())
8360 local_player->was_waiting = TRUE;
8363 else if (any_player_moving || any_player_snapping || any_player_dropping)
8365 local_player->was_waiting = FALSE;
8369 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8370 boolean murphy_is_dropping)
8372 if (murphy_is_waiting)
8374 if (!local_player->was_waiting)
8376 if (!CheckSaveEngineSnapshotToList())
8379 local_player->was_waiting = TRUE;
8384 local_player->was_waiting = FALSE;
8388 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8389 boolean any_player_moving,
8390 boolean any_player_snapping,
8391 boolean any_player_dropping)
8393 if (tape.single_step && tape.recording && !tape.pausing)
8394 if (frame == 0 && !any_player_dropping)
8395 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8397 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8398 any_player_snapping, any_player_dropping);
8401 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8402 boolean murphy_is_dropping)
8404 boolean murphy_starts_dropping = FALSE;
8407 for (i = 0; i < MAX_PLAYERS; i++)
8408 if (stored_player[i].force_dropping)
8409 murphy_starts_dropping = TRUE;
8411 if (tape.single_step && tape.recording && !tape.pausing)
8412 if (murphy_is_waiting && !murphy_starts_dropping)
8413 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8415 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8418 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8419 int graphic, int sync_frame, int x, int y)
8421 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8423 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8426 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8428 return (IS_NEXT_FRAME(sync_frame, graphic));
8431 int getGraphicInfo_Delay(int graphic)
8433 return graphic_info[graphic].anim_delay;
8436 void PlayMenuSoundExt(int sound)
8438 if (sound == SND_UNDEFINED)
8441 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8442 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8445 if (IS_LOOP_SOUND(sound))
8446 PlaySoundLoop(sound);
8451 void PlayMenuSound()
8453 PlayMenuSoundExt(menu.sound[game_status]);
8456 void PlayMenuSoundStereo(int sound, int stereo_position)
8458 if (sound == SND_UNDEFINED)
8461 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8462 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8465 if (IS_LOOP_SOUND(sound))
8466 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8468 PlaySoundStereo(sound, stereo_position);
8471 void PlayMenuSoundIfLoopExt(int sound)
8473 if (sound == SND_UNDEFINED)
8476 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8477 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8480 if (IS_LOOP_SOUND(sound))
8481 PlaySoundLoop(sound);
8484 void PlayMenuSoundIfLoop()
8486 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8489 void PlayMenuMusicExt(int music)
8491 if (music == MUS_UNDEFINED)
8494 if (!setup.sound_music)
8500 void PlayMenuMusic()
8502 char *curr_music = getCurrentlyPlayingMusicFilename();
8503 char *next_music = getMusicListEntry(menu.music[game_status])->filename;
8505 if (!strEqual(curr_music, next_music))
8506 PlayMenuMusicExt(menu.music[game_status]);
8509 void PlayMenuSoundsAndMusic()
8515 static void FadeMenuSounds()
8520 static void FadeMenuMusic()
8522 char *curr_music = getCurrentlyPlayingMusicFilename();
8523 char *next_music = getMusicListEntry(menu.music[game_status])->filename;
8525 if (!strEqual(curr_music, next_music))
8529 void FadeMenuSoundsAndMusic()
8535 void PlaySoundActivating()
8538 PlaySound(SND_MENU_ITEM_ACTIVATING);
8542 void PlaySoundSelecting()
8545 PlaySound(SND_MENU_ITEM_SELECTING);
8549 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8551 boolean change_fullscreen = (setup.fullscreen !=
8552 video.fullscreen_enabled);
8553 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8554 setup.window_scaling_percent !=
8555 video.window_scaling_percent);
8557 if (change_window_scaling_percent && video.fullscreen_enabled)
8560 if (!change_window_scaling_percent && !video.fullscreen_available)
8563 #if defined(TARGET_SDL2)
8564 if (change_window_scaling_percent)
8566 SDLSetWindowScaling(setup.window_scaling_percent);
8570 else if (change_fullscreen)
8572 SDLSetWindowFullscreen(setup.fullscreen);
8574 /* set setup value according to successfully changed fullscreen mode */
8575 setup.fullscreen = video.fullscreen_enabled;
8581 if (change_fullscreen ||
8582 change_window_scaling_percent)
8584 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8586 /* save backbuffer content which gets lost when toggling fullscreen mode */
8587 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8589 if (change_window_scaling_percent)
8591 /* keep window mode, but change window scaling */
8592 video.fullscreen_enabled = TRUE; /* force new window scaling */
8595 /* toggle fullscreen */
8596 ChangeVideoModeIfNeeded(setup.fullscreen);
8598 /* set setup value according to successfully changed fullscreen mode */
8599 setup.fullscreen = video.fullscreen_enabled;
8601 /* restore backbuffer content from temporary backbuffer backup bitmap */
8602 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8604 FreeBitmap(tmp_backbuffer);
8606 /* update visible window/screen */
8607 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8611 void JoinRectangles(int *x, int *y, int *width, int *height,
8612 int x2, int y2, int width2, int height2)
8614 // do not join with "off-screen" rectangle
8615 if (x2 == -1 || y2 == -1)
8620 *width = MAX(*width, width2);
8621 *height = MAX(*height, height2);
8624 void SetAnimStatus(int anim_status_new)
8626 if (anim_status_new == GAME_MODE_MAIN)
8627 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
8628 else if (anim_status_new == GAME_MODE_SCORES)
8629 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
8631 global.anim_status_next = anim_status_new;
8633 // directly set screen modes that are entered without fading
8634 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
8635 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
8636 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
8637 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
8638 global.anim_status = global.anim_status_next;
8641 void SetGameStatus(int game_status_new)
8643 if (game_status_new != game_status)
8644 game_status_last_screen = game_status;
8646 game_status = game_status_new;
8648 SetAnimStatus(game_status_new);
8651 void SetFontStatus(int game_status_new)
8653 static int last_game_status = -1;
8655 if (game_status_new != -1)
8657 // set game status for font use after storing last game status
8658 last_game_status = game_status;
8659 game_status = game_status_new;
8663 // reset game status after font use from last stored game status
8664 game_status = last_game_status;
8668 void ResetFontStatus()
8673 void ChangeViewportPropertiesIfNeeded()
8675 int gfx_game_mode = game_status;
8676 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8678 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
8679 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8680 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8681 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8682 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8683 int new_win_xsize = vp_window->width;
8684 int new_win_ysize = vp_window->height;
8685 int border_size = vp_playfield->border_size;
8686 int new_sx = vp_playfield->x + border_size;
8687 int new_sy = vp_playfield->y + border_size;
8688 int new_sxsize = vp_playfield->width - 2 * border_size;
8689 int new_sysize = vp_playfield->height - 2 * border_size;
8690 int new_real_sx = vp_playfield->x;
8691 int new_real_sy = vp_playfield->y;
8692 int new_full_sxsize = vp_playfield->width;
8693 int new_full_sysize = vp_playfield->height;
8694 int new_dx = vp_door_1->x;
8695 int new_dy = vp_door_1->y;
8696 int new_dxsize = vp_door_1->width;
8697 int new_dysize = vp_door_1->height;
8698 int new_vx = vp_door_2->x;
8699 int new_vy = vp_door_2->y;
8700 int new_vxsize = vp_door_2->width;
8701 int new_vysize = vp_door_2->height;
8702 int new_ex = vp_door_3->x;
8703 int new_ey = vp_door_3->y;
8704 int new_exsize = vp_door_3->width;
8705 int new_eysize = vp_door_3->height;
8706 int new_tilesize_var =
8707 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8709 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8710 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8711 int new_scr_fieldx = new_sxsize / tilesize;
8712 int new_scr_fieldy = new_sysize / tilesize;
8713 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8714 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8715 boolean init_gfx_buffers = FALSE;
8716 boolean init_video_buffer = FALSE;
8717 boolean init_gadgets_and_anims = FALSE;
8718 boolean init_em_graphics = FALSE;
8720 if (new_win_xsize != WIN_XSIZE ||
8721 new_win_ysize != WIN_YSIZE)
8723 WIN_XSIZE = new_win_xsize;
8724 WIN_YSIZE = new_win_ysize;
8726 init_video_buffer = TRUE;
8727 init_gfx_buffers = TRUE;
8728 init_gadgets_and_anims = TRUE;
8730 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8733 if (new_scr_fieldx != SCR_FIELDX ||
8734 new_scr_fieldy != SCR_FIELDY)
8736 /* this always toggles between MAIN and GAME when using small tile size */
8738 SCR_FIELDX = new_scr_fieldx;
8739 SCR_FIELDY = new_scr_fieldy;
8741 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8752 new_sxsize != SXSIZE ||
8753 new_sysize != SYSIZE ||
8754 new_dxsize != DXSIZE ||
8755 new_dysize != DYSIZE ||
8756 new_vxsize != VXSIZE ||
8757 new_vysize != VYSIZE ||
8758 new_exsize != EXSIZE ||
8759 new_eysize != EYSIZE ||
8760 new_real_sx != REAL_SX ||
8761 new_real_sy != REAL_SY ||
8762 new_full_sxsize != FULL_SXSIZE ||
8763 new_full_sysize != FULL_SYSIZE ||
8764 new_tilesize_var != TILESIZE_VAR
8767 // ------------------------------------------------------------------------
8768 // determine next fading area for changed viewport definitions
8769 // ------------------------------------------------------------------------
8771 // start with current playfield area (default fading area)
8774 FADE_SXSIZE = FULL_SXSIZE;
8775 FADE_SYSIZE = FULL_SYSIZE;
8777 // add new playfield area if position or size has changed
8778 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8779 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8781 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8782 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8785 // add current and new door 1 area if position or size has changed
8786 if (new_dx != DX || new_dy != DY ||
8787 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8789 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8790 DX, DY, DXSIZE, DYSIZE);
8791 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8792 new_dx, new_dy, new_dxsize, new_dysize);
8795 // add current and new door 2 area if position or size has changed
8796 if (new_dx != VX || new_dy != VY ||
8797 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8799 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8800 VX, VY, VXSIZE, VYSIZE);
8801 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8802 new_vx, new_vy, new_vxsize, new_vysize);
8805 // ------------------------------------------------------------------------
8806 // handle changed tile size
8807 // ------------------------------------------------------------------------
8809 if (new_tilesize_var != TILESIZE_VAR)
8811 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8813 // changing tile size invalidates scroll values of engine snapshots
8814 FreeEngineSnapshotSingle();
8816 // changing tile size requires update of graphic mapping for EM engine
8817 init_em_graphics = TRUE;
8828 SXSIZE = new_sxsize;
8829 SYSIZE = new_sysize;
8830 DXSIZE = new_dxsize;
8831 DYSIZE = new_dysize;
8832 VXSIZE = new_vxsize;
8833 VYSIZE = new_vysize;
8834 EXSIZE = new_exsize;
8835 EYSIZE = new_eysize;
8836 REAL_SX = new_real_sx;
8837 REAL_SY = new_real_sy;
8838 FULL_SXSIZE = new_full_sxsize;
8839 FULL_SYSIZE = new_full_sysize;
8840 TILESIZE_VAR = new_tilesize_var;
8842 init_gfx_buffers = TRUE;
8843 init_gadgets_and_anims = TRUE;
8845 // printf("::: viewports: init_gfx_buffers\n");
8846 // printf("::: viewports: init_gadgets_and_anims\n");
8849 if (init_gfx_buffers)
8851 // printf("::: init_gfx_buffers\n");
8853 SCR_FIELDX = new_scr_fieldx_buffers;
8854 SCR_FIELDY = new_scr_fieldy_buffers;
8858 SCR_FIELDX = new_scr_fieldx;
8859 SCR_FIELDY = new_scr_fieldy;
8861 SetDrawDeactivationMask(REDRAW_NONE);
8862 SetDrawBackgroundMask(REDRAW_FIELD);
8865 if (init_video_buffer)
8867 // printf("::: init_video_buffer\n");
8869 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8870 InitImageTextures();
8873 if (init_gadgets_and_anims)
8875 // printf("::: init_gadgets_and_anims\n");
8878 InitGlobalAnimations();
8881 if (init_em_graphics)
8883 InitGraphicInfo_EM();