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 getSizedGraphicSource(int graphic, int frame, int tilesize,
1448 Bitmap **bitmap, int *x, int *y)
1450 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1453 void getFixedGraphicSource(int graphic, int frame,
1454 Bitmap **bitmap, int *x, int *y)
1456 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1459 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1461 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1464 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1465 int *x, int *y, boolean get_backside)
1467 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1471 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1473 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1476 void DrawGraphic(int x, int y, int graphic, int frame)
1479 if (!IN_SCR_FIELD(x, y))
1481 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1482 printf("DrawGraphic(): This should never happen!\n");
1487 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1490 MarkTileDirty(x, y);
1493 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1496 if (!IN_SCR_FIELD(x, y))
1498 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1499 printf("DrawGraphic(): This should never happen!\n");
1504 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1506 MarkTileDirty(x, y);
1509 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1515 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1517 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1520 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1526 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1527 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1530 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1533 if (!IN_SCR_FIELD(x, y))
1535 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1536 printf("DrawGraphicThruMask(): This should never happen!\n");
1541 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1544 MarkTileDirty(x, y);
1547 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1550 if (!IN_SCR_FIELD(x, y))
1552 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1553 printf("DrawGraphicThruMask(): This should never happen!\n");
1558 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1560 MarkTileDirty(x, y);
1563 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1569 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1571 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1575 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1576 int graphic, int frame)
1581 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1583 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1587 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1589 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1591 MarkTileDirty(x / tilesize, y / tilesize);
1594 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1600 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1601 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1604 void DrawMiniGraphic(int x, int y, int graphic)
1606 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1607 MarkTileDirty(x / 2, y / 2);
1610 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1615 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1616 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1619 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1620 int graphic, int frame,
1621 int cut_mode, int mask_mode)
1626 int width = TILEX, height = TILEY;
1629 if (dx || dy) /* shifted graphic */
1631 if (x < BX1) /* object enters playfield from the left */
1638 else if (x > BX2) /* object enters playfield from the right */
1644 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1650 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1652 else if (dx) /* general horizontal movement */
1653 MarkTileDirty(x + SIGN(dx), y);
1655 if (y < BY1) /* object enters playfield from the top */
1657 if (cut_mode == CUT_BELOW) /* object completely above top border */
1665 else if (y > BY2) /* object enters playfield from the bottom */
1671 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1677 else if (dy > 0 && cut_mode == CUT_ABOVE)
1679 if (y == BY2) /* object completely above bottom border */
1685 MarkTileDirty(x, y + 1);
1686 } /* object leaves playfield to the bottom */
1687 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1689 else if (dy) /* general vertical movement */
1690 MarkTileDirty(x, y + SIGN(dy));
1694 if (!IN_SCR_FIELD(x, y))
1696 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1697 printf("DrawGraphicShifted(): This should never happen!\n");
1702 width = width * TILESIZE_VAR / TILESIZE;
1703 height = height * TILESIZE_VAR / TILESIZE;
1704 cx = cx * TILESIZE_VAR / TILESIZE;
1705 cy = cy * TILESIZE_VAR / TILESIZE;
1706 dx = dx * TILESIZE_VAR / TILESIZE;
1707 dy = dy * TILESIZE_VAR / TILESIZE;
1709 if (width > 0 && height > 0)
1711 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1716 dst_x = FX + x * TILEX_VAR + dx;
1717 dst_y = FY + y * TILEY_VAR + dy;
1719 if (mask_mode == USE_MASKING)
1720 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1723 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1726 MarkTileDirty(x, y);
1730 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1731 int graphic, int frame,
1732 int cut_mode, int mask_mode)
1737 int width = TILEX_VAR, height = TILEY_VAR;
1740 int x2 = x + SIGN(dx);
1741 int y2 = y + SIGN(dy);
1743 /* movement with two-tile animations must be sync'ed with movement position,
1744 not with current GfxFrame (which can be higher when using slow movement) */
1745 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1746 int anim_frames = graphic_info[graphic].anim_frames;
1748 /* (we also need anim_delay here for movement animations with less frames) */
1749 int anim_delay = graphic_info[graphic].anim_delay;
1750 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1752 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1753 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1755 /* re-calculate animation frame for two-tile movement animation */
1756 frame = getGraphicAnimationFrame(graphic, sync_frame);
1758 /* check if movement start graphic inside screen area and should be drawn */
1759 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1761 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1763 dst_x = FX + x1 * TILEX_VAR;
1764 dst_y = FY + y1 * TILEY_VAR;
1766 if (mask_mode == USE_MASKING)
1767 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1770 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1773 MarkTileDirty(x1, y1);
1776 /* check if movement end graphic inside screen area and should be drawn */
1777 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1779 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1781 dst_x = FX + x2 * TILEX_VAR;
1782 dst_y = FY + y2 * TILEY_VAR;
1784 if (mask_mode == USE_MASKING)
1785 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1788 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1791 MarkTileDirty(x2, y2);
1795 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1796 int graphic, int frame,
1797 int cut_mode, int mask_mode)
1801 DrawGraphic(x, y, graphic, frame);
1806 if (graphic_info[graphic].double_movement) /* EM style movement images */
1807 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1809 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1812 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1813 int frame, int cut_mode)
1815 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1818 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1819 int cut_mode, int mask_mode)
1821 int lx = LEVELX(x), ly = LEVELY(y);
1825 if (IN_LEV_FIELD(lx, ly))
1827 SetRandomAnimationValue(lx, ly);
1829 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1830 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1832 /* do not use double (EM style) movement graphic when not moving */
1833 if (graphic_info[graphic].double_movement && !dx && !dy)
1835 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1836 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1839 else /* border element */
1841 graphic = el2img(element);
1842 frame = getGraphicAnimationFrame(graphic, -1);
1845 if (element == EL_EXPANDABLE_WALL)
1847 boolean left_stopped = FALSE, right_stopped = FALSE;
1849 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1850 left_stopped = TRUE;
1851 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1852 right_stopped = TRUE;
1854 if (left_stopped && right_stopped)
1856 else if (left_stopped)
1858 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1859 frame = graphic_info[graphic].anim_frames - 1;
1861 else if (right_stopped)
1863 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1864 frame = graphic_info[graphic].anim_frames - 1;
1869 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1870 else if (mask_mode == USE_MASKING)
1871 DrawGraphicThruMask(x, y, graphic, frame);
1873 DrawGraphic(x, y, graphic, frame);
1876 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1877 int cut_mode, int mask_mode)
1879 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1880 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1881 cut_mode, mask_mode);
1884 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1887 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1890 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1893 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1896 void DrawLevelElementThruMask(int x, int y, int element)
1898 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1901 void DrawLevelFieldThruMask(int x, int y)
1903 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1906 /* !!! implementation of quicksand is totally broken !!! */
1907 #define IS_CRUMBLED_TILE(x, y, e) \
1908 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1909 !IS_MOVING(x, y) || \
1910 (e) == EL_QUICKSAND_EMPTYING || \
1911 (e) == EL_QUICKSAND_FAST_EMPTYING))
1913 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1918 int width, height, cx, cy;
1919 int sx = SCREENX(x), sy = SCREENY(y);
1920 int crumbled_border_size = graphic_info[graphic].border_size;
1921 int crumbled_tile_size = graphic_info[graphic].tile_size;
1922 int crumbled_border_size_var =
1923 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
1926 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1928 for (i = 1; i < 4; i++)
1930 int dxx = (i & 1 ? dx : 0);
1931 int dyy = (i & 2 ? dy : 0);
1934 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1937 /* check if neighbour field is of same crumble type */
1938 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1939 graphic_info[graphic].class ==
1940 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1942 /* return if check prevents inner corner */
1943 if (same == (dxx == dx && dyy == dy))
1947 /* if we reach this point, we have an inner corner */
1949 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1951 width = crumbled_border_size_var;
1952 height = crumbled_border_size_var;
1953 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1954 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1956 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1957 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1960 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1965 int width, height, bx, by, cx, cy;
1966 int sx = SCREENX(x), sy = SCREENY(y);
1967 int crumbled_border_size = graphic_info[graphic].border_size;
1968 int crumbled_tile_size = graphic_info[graphic].tile_size;
1969 int crumbled_border_size_var =
1970 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
1971 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1974 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1976 /* draw simple, sloppy, non-corner-accurate crumbled border */
1978 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1979 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1980 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1981 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1983 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1984 FX + sx * TILEX_VAR + cx,
1985 FY + sy * TILEY_VAR + cy);
1987 /* (remaining middle border part must be at least as big as corner part) */
1988 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1989 crumbled_border_size_var >= TILESIZE_VAR / 3)
1992 /* correct corners of crumbled border, if needed */
1994 for (i = -1; i <= 1; i += 2)
1996 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1997 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1998 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2001 /* check if neighbour field is of same crumble type */
2002 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2003 graphic_info[graphic].class ==
2004 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2006 /* no crumbled corner, but continued crumbled border */
2008 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2009 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2010 int b1 = (i == 1 ? crumbled_border_size_var :
2011 TILESIZE_VAR - 2 * crumbled_border_size_var);
2013 width = crumbled_border_size_var;
2014 height = crumbled_border_size_var;
2016 if (dir == 1 || dir == 2)
2031 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2033 FX + sx * TILEX_VAR + cx,
2034 FY + sy * TILEY_VAR + cy);
2039 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2041 int sx = SCREENX(x), sy = SCREENY(y);
2044 static int xy[4][2] =
2052 if (!IN_LEV_FIELD(x, y))
2055 element = TILE_GFX_ELEMENT(x, y);
2057 if (IS_CRUMBLED_TILE(x, y, element)) /* crumble field itself */
2059 if (!IN_SCR_FIELD(sx, sy))
2062 /* crumble field borders towards direct neighbour fields */
2063 for (i = 0; i < 4; i++)
2065 int xx = x + xy[i][0];
2066 int yy = y + xy[i][1];
2068 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2071 /* check if neighbour field is of same crumble type */
2072 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2073 graphic_info[graphic].class ==
2074 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2077 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2080 /* crumble inner field corners towards corner neighbour fields */
2081 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2082 graphic_info[graphic].anim_frames == 2)
2084 for (i = 0; i < 4; i++)
2086 int dx = (i & 1 ? +1 : -1);
2087 int dy = (i & 2 ? +1 : -1);
2089 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2093 MarkTileDirty(sx, sy);
2095 else /* center field is not crumbled -- crumble neighbour fields */
2097 /* crumble field borders of direct neighbour fields */
2098 for (i = 0; i < 4; i++)
2100 int xx = x + xy[i][0];
2101 int yy = y + xy[i][1];
2102 int sxx = sx + xy[i][0];
2103 int syy = sy + xy[i][1];
2105 if (!IN_LEV_FIELD(xx, yy) ||
2106 !IN_SCR_FIELD(sxx, syy))
2109 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2112 element = TILE_GFX_ELEMENT(xx, yy);
2114 if (!IS_CRUMBLED_TILE(xx, yy, element))
2117 graphic = el_act2crm(element, ACTION_DEFAULT);
2119 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2121 MarkTileDirty(sxx, syy);
2124 /* crumble inner field corners of corner neighbour fields */
2125 for (i = 0; i < 4; i++)
2127 int dx = (i & 1 ? +1 : -1);
2128 int dy = (i & 2 ? +1 : -1);
2134 if (!IN_LEV_FIELD(xx, yy) ||
2135 !IN_SCR_FIELD(sxx, syy))
2138 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2141 element = TILE_GFX_ELEMENT(xx, yy);
2143 if (!IS_CRUMBLED_TILE(xx, yy, element))
2146 graphic = el_act2crm(element, ACTION_DEFAULT);
2148 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2149 graphic_info[graphic].anim_frames == 2)
2150 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2152 MarkTileDirty(sxx, syy);
2157 void DrawLevelFieldCrumbled(int x, int y)
2161 if (!IN_LEV_FIELD(x, y))
2164 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2165 GfxElement[x][y] != EL_UNDEFINED &&
2166 GFX_CRUMBLED(GfxElement[x][y]))
2168 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2173 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2175 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2178 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2181 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2182 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2183 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2184 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2185 int sx = SCREENX(x), sy = SCREENY(y);
2187 DrawGraphic(sx, sy, graphic1, frame1);
2188 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2191 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2193 int sx = SCREENX(x), sy = SCREENY(y);
2194 static int xy[4][2] =
2203 /* crumble direct neighbour fields (required for field borders) */
2204 for (i = 0; i < 4; i++)
2206 int xx = x + xy[i][0];
2207 int yy = y + xy[i][1];
2208 int sxx = sx + xy[i][0];
2209 int syy = sy + xy[i][1];
2211 if (!IN_LEV_FIELD(xx, yy) ||
2212 !IN_SCR_FIELD(sxx, syy) ||
2213 !GFX_CRUMBLED(Feld[xx][yy]) ||
2217 DrawLevelField(xx, yy);
2220 /* crumble corner neighbour fields (required for inner field corners) */
2221 for (i = 0; i < 4; i++)
2223 int dx = (i & 1 ? +1 : -1);
2224 int dy = (i & 2 ? +1 : -1);
2230 if (!IN_LEV_FIELD(xx, yy) ||
2231 !IN_SCR_FIELD(sxx, syy) ||
2232 !GFX_CRUMBLED(Feld[xx][yy]) ||
2236 int element = TILE_GFX_ELEMENT(xx, yy);
2237 int graphic = el_act2crm(element, ACTION_DEFAULT);
2239 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2240 graphic_info[graphic].anim_frames == 2)
2241 DrawLevelField(xx, yy);
2245 static int getBorderElement(int x, int y)
2249 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2250 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2251 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2252 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2253 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2254 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2255 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2257 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2258 int steel_position = (x == -1 && y == -1 ? 0 :
2259 x == lev_fieldx && y == -1 ? 1 :
2260 x == -1 && y == lev_fieldy ? 2 :
2261 x == lev_fieldx && y == lev_fieldy ? 3 :
2262 x == -1 || x == lev_fieldx ? 4 :
2263 y == -1 || y == lev_fieldy ? 5 : 6);
2265 return border[steel_position][steel_type];
2268 void DrawScreenElement(int x, int y, int element)
2270 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2271 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2274 void DrawLevelElement(int x, int y, int element)
2276 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2277 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2280 void DrawScreenField(int x, int y)
2282 int lx = LEVELX(x), ly = LEVELY(y);
2283 int element, content;
2285 if (!IN_LEV_FIELD(lx, ly))
2287 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2290 element = getBorderElement(lx, ly);
2292 DrawScreenElement(x, y, element);
2297 element = Feld[lx][ly];
2298 content = Store[lx][ly];
2300 if (IS_MOVING(lx, ly))
2302 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2303 boolean cut_mode = NO_CUTTING;
2305 if (element == EL_QUICKSAND_EMPTYING ||
2306 element == EL_QUICKSAND_FAST_EMPTYING ||
2307 element == EL_MAGIC_WALL_EMPTYING ||
2308 element == EL_BD_MAGIC_WALL_EMPTYING ||
2309 element == EL_DC_MAGIC_WALL_EMPTYING ||
2310 element == EL_AMOEBA_DROPPING)
2311 cut_mode = CUT_ABOVE;
2312 else if (element == EL_QUICKSAND_FILLING ||
2313 element == EL_QUICKSAND_FAST_FILLING ||
2314 element == EL_MAGIC_WALL_FILLING ||
2315 element == EL_BD_MAGIC_WALL_FILLING ||
2316 element == EL_DC_MAGIC_WALL_FILLING)
2317 cut_mode = CUT_BELOW;
2319 if (cut_mode == CUT_ABOVE)
2320 DrawScreenElement(x, y, element);
2322 DrawScreenElement(x, y, EL_EMPTY);
2325 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2326 else if (cut_mode == NO_CUTTING)
2327 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2330 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2332 if (cut_mode == CUT_BELOW &&
2333 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2334 DrawLevelElement(lx, ly + 1, element);
2337 if (content == EL_ACID)
2339 int dir = MovDir[lx][ly];
2340 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2341 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2343 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2345 // prevent target field from being drawn again (but without masking)
2346 // (this would happen if target field is scanned after moving element)
2347 Stop[newlx][newly] = TRUE;
2350 else if (IS_BLOCKED(lx, ly))
2355 boolean cut_mode = NO_CUTTING;
2356 int element_old, content_old;
2358 Blocked2Moving(lx, ly, &oldx, &oldy);
2361 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2362 MovDir[oldx][oldy] == MV_RIGHT);
2364 element_old = Feld[oldx][oldy];
2365 content_old = Store[oldx][oldy];
2367 if (element_old == EL_QUICKSAND_EMPTYING ||
2368 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2369 element_old == EL_MAGIC_WALL_EMPTYING ||
2370 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2371 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2372 element_old == EL_AMOEBA_DROPPING)
2373 cut_mode = CUT_ABOVE;
2375 DrawScreenElement(x, y, EL_EMPTY);
2378 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2380 else if (cut_mode == NO_CUTTING)
2381 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2384 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2387 else if (IS_DRAWABLE(element))
2388 DrawScreenElement(x, y, element);
2390 DrawScreenElement(x, y, EL_EMPTY);
2393 void DrawLevelField(int x, int y)
2395 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2396 DrawScreenField(SCREENX(x), SCREENY(y));
2397 else if (IS_MOVING(x, y))
2401 Moving2Blocked(x, y, &newx, &newy);
2402 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2403 DrawScreenField(SCREENX(newx), SCREENY(newy));
2405 else if (IS_BLOCKED(x, y))
2409 Blocked2Moving(x, y, &oldx, &oldy);
2410 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2411 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2415 void DrawSizedElement(int x, int y, int element, int tilesize)
2419 graphic = el2edimg(element);
2420 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2423 void DrawMiniElement(int x, int y, int element)
2427 graphic = el2edimg(element);
2428 DrawMiniGraphic(x, y, graphic);
2431 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2434 int x = sx + scroll_x, y = sy + scroll_y;
2436 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2437 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2438 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2439 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2441 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2444 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2446 int x = sx + scroll_x, y = sy + scroll_y;
2448 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2449 DrawMiniElement(sx, sy, EL_EMPTY);
2450 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2451 DrawMiniElement(sx, sy, Feld[x][y]);
2453 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2456 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2457 int x, int y, int xsize, int ysize,
2458 int tile_width, int tile_height)
2462 int dst_x = startx + x * tile_width;
2463 int dst_y = starty + y * tile_height;
2464 int width = graphic_info[graphic].width;
2465 int height = graphic_info[graphic].height;
2466 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2467 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2468 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2469 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2470 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2471 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2472 boolean draw_masked = graphic_info[graphic].draw_masked;
2474 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2476 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2478 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2482 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2483 inner_sx + (x - 1) * tile_width % inner_width);
2484 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2485 inner_sy + (y - 1) * tile_height % inner_height);
2488 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2491 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2495 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2496 int x, int y, int xsize, int ysize, int font_nr)
2498 int font_width = getFontWidth(font_nr);
2499 int font_height = getFontHeight(font_nr);
2501 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2502 font_width, font_height);
2505 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2507 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2508 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2509 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2510 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2511 boolean no_delay = (tape.warp_forward);
2512 unsigned int anim_delay = 0;
2513 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2514 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2515 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2516 int font_width = getFontWidth(font_nr);
2517 int font_height = getFontHeight(font_nr);
2518 int max_xsize = level.envelope[envelope_nr].xsize;
2519 int max_ysize = level.envelope[envelope_nr].ysize;
2520 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2521 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2522 int xend = max_xsize;
2523 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2524 int xstep = (xstart < xend ? 1 : 0);
2525 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2527 int end = MAX(xend - xstart, yend - ystart);
2530 for (i = start; i <= end; i++)
2532 int last_frame = end; // last frame of this "for" loop
2533 int x = xstart + i * xstep;
2534 int y = ystart + i * ystep;
2535 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2536 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2537 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2538 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2541 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2543 BlitScreenToBitmap(backbuffer);
2545 SetDrawtoField(DRAW_TO_BACKBUFFER);
2547 for (yy = 0; yy < ysize; yy++)
2548 for (xx = 0; xx < xsize; xx++)
2549 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2551 DrawTextBuffer(sx + font_width, sy + font_height,
2552 level.envelope[envelope_nr].text, font_nr, max_xsize,
2553 xsize - 2, ysize - 2, 0, mask_mode,
2554 level.envelope[envelope_nr].autowrap,
2555 level.envelope[envelope_nr].centered, FALSE);
2557 redraw_mask |= REDRAW_FIELD;
2560 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2564 void ShowEnvelope(int envelope_nr)
2566 int element = EL_ENVELOPE_1 + envelope_nr;
2567 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2568 int sound_opening = element_info[element].sound[ACTION_OPENING];
2569 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2570 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2571 boolean no_delay = (tape.warp_forward);
2572 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2573 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2574 int anim_mode = graphic_info[graphic].anim_mode;
2575 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2576 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2578 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2580 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2582 if (anim_mode == ANIM_DEFAULT)
2583 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2585 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2588 Delay(wait_delay_value);
2590 WaitForEventToContinue();
2592 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2594 if (anim_mode != ANIM_NONE)
2595 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2597 if (anim_mode == ANIM_DEFAULT)
2598 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2600 game.envelope_active = FALSE;
2602 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2604 redraw_mask |= REDRAW_FIELD;
2608 static void setRequestBasePosition(int *x, int *y)
2610 int sx_base, sy_base;
2612 if (request.x != -1)
2613 sx_base = request.x;
2614 else if (request.align == ALIGN_LEFT)
2616 else if (request.align == ALIGN_RIGHT)
2617 sx_base = SX + SXSIZE;
2619 sx_base = SX + SXSIZE / 2;
2621 if (request.y != -1)
2622 sy_base = request.y;
2623 else if (request.valign == VALIGN_TOP)
2625 else if (request.valign == VALIGN_BOTTOM)
2626 sy_base = SY + SYSIZE;
2628 sy_base = SY + SYSIZE / 2;
2634 static void setRequestPositionExt(int *x, int *y, int width, int height,
2635 boolean add_border_size)
2637 int border_size = request.border_size;
2638 int sx_base, sy_base;
2641 setRequestBasePosition(&sx_base, &sy_base);
2643 if (request.align == ALIGN_LEFT)
2645 else if (request.align == ALIGN_RIGHT)
2646 sx = sx_base - width;
2648 sx = sx_base - width / 2;
2650 if (request.valign == VALIGN_TOP)
2652 else if (request.valign == VALIGN_BOTTOM)
2653 sy = sy_base - height;
2655 sy = sy_base - height / 2;
2657 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2658 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2660 if (add_border_size)
2670 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2672 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2675 void DrawEnvelopeRequest(char *text)
2677 char *text_final = text;
2678 char *text_door_style = NULL;
2679 int graphic = IMG_BACKGROUND_REQUEST;
2680 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2681 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2682 int font_nr = FONT_REQUEST;
2683 int font_width = getFontWidth(font_nr);
2684 int font_height = getFontHeight(font_nr);
2685 int border_size = request.border_size;
2686 int line_spacing = request.line_spacing;
2687 int line_height = font_height + line_spacing;
2688 int max_text_width = request.width - 2 * border_size;
2689 int max_text_height = request.height - 2 * border_size;
2690 int line_length = max_text_width / font_width;
2691 int max_lines = max_text_height / line_height;
2692 int text_width = line_length * font_width;
2693 int width = request.width;
2694 int height = request.height;
2695 int tile_size = MAX(request.step_offset, 1);
2696 int x_steps = width / tile_size;
2697 int y_steps = height / tile_size;
2698 int sx_offset = border_size;
2699 int sy_offset = border_size;
2703 if (request.centered)
2704 sx_offset = (request.width - text_width) / 2;
2706 if (request.wrap_single_words && !request.autowrap)
2708 char *src_text_ptr, *dst_text_ptr;
2710 text_door_style = checked_malloc(2 * strlen(text) + 1);
2712 src_text_ptr = text;
2713 dst_text_ptr = text_door_style;
2715 while (*src_text_ptr)
2717 if (*src_text_ptr == ' ' ||
2718 *src_text_ptr == '?' ||
2719 *src_text_ptr == '!')
2720 *dst_text_ptr++ = '\n';
2722 if (*src_text_ptr != ' ')
2723 *dst_text_ptr++ = *src_text_ptr;
2728 *dst_text_ptr = '\0';
2730 text_final = text_door_style;
2733 setRequestPosition(&sx, &sy, FALSE);
2735 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2737 for (y = 0; y < y_steps; y++)
2738 for (x = 0; x < x_steps; x++)
2739 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2740 x, y, x_steps, y_steps,
2741 tile_size, tile_size);
2743 /* force DOOR font inside door area */
2744 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2746 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2747 line_length, -1, max_lines, line_spacing, mask_mode,
2748 request.autowrap, request.centered, FALSE);
2752 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2753 RedrawGadget(tool_gadget[i]);
2755 // store readily prepared envelope request for later use when animating
2756 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2758 if (text_door_style)
2759 free(text_door_style);
2762 void AnimateEnvelopeRequest(int anim_mode, int action)
2764 int graphic = IMG_BACKGROUND_REQUEST;
2765 boolean draw_masked = graphic_info[graphic].draw_masked;
2766 int delay_value_normal = request.step_delay;
2767 int delay_value_fast = delay_value_normal / 2;
2768 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2769 boolean no_delay = (tape.warp_forward);
2770 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2771 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2772 unsigned int anim_delay = 0;
2774 int tile_size = MAX(request.step_offset, 1);
2775 int max_xsize = request.width / tile_size;
2776 int max_ysize = request.height / tile_size;
2777 int max_xsize_inner = max_xsize - 2;
2778 int max_ysize_inner = max_ysize - 2;
2780 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2781 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2782 int xend = max_xsize_inner;
2783 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2784 int xstep = (xstart < xend ? 1 : 0);
2785 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2787 int end = MAX(xend - xstart, yend - ystart);
2790 if (setup.quick_doors)
2797 for (i = start; i <= end; i++)
2799 int last_frame = end; // last frame of this "for" loop
2800 int x = xstart + i * xstep;
2801 int y = ystart + i * ystep;
2802 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2803 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2804 int xsize_size_left = (xsize - 1) * tile_size;
2805 int ysize_size_top = (ysize - 1) * tile_size;
2806 int max_xsize_pos = (max_xsize - 1) * tile_size;
2807 int max_ysize_pos = (max_ysize - 1) * tile_size;
2808 int width = xsize * tile_size;
2809 int height = ysize * tile_size;
2814 setRequestPosition(&src_x, &src_y, FALSE);
2815 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2817 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2819 for (yy = 0; yy < 2; yy++)
2821 for (xx = 0; xx < 2; xx++)
2823 int src_xx = src_x + xx * max_xsize_pos;
2824 int src_yy = src_y + yy * max_ysize_pos;
2825 int dst_xx = dst_x + xx * xsize_size_left;
2826 int dst_yy = dst_y + yy * ysize_size_top;
2827 int xx_size = (xx ? tile_size : xsize_size_left);
2828 int yy_size = (yy ? tile_size : ysize_size_top);
2831 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
2832 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2834 BlitBitmap(bitmap_db_store_2, backbuffer,
2835 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2839 redraw_mask |= REDRAW_FIELD;
2843 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2847 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2849 int graphic = IMG_BACKGROUND_REQUEST;
2850 int sound_opening = SND_REQUEST_OPENING;
2851 int sound_closing = SND_REQUEST_CLOSING;
2852 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2853 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2854 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2855 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2856 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2858 if (game_status == GAME_MODE_PLAYING)
2859 BlitScreenToBitmap(backbuffer);
2861 SetDrawtoField(DRAW_TO_BACKBUFFER);
2863 // SetDrawBackgroundMask(REDRAW_NONE);
2865 if (action == ACTION_OPENING)
2867 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2869 if (req_state & REQ_ASK)
2871 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2872 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2874 else if (req_state & REQ_CONFIRM)
2876 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2878 else if (req_state & REQ_PLAYER)
2880 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2881 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2882 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2883 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2886 DrawEnvelopeRequest(text);
2889 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2891 if (action == ACTION_OPENING)
2893 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2895 if (anim_mode == ANIM_DEFAULT)
2896 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2898 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2902 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2904 if (anim_mode != ANIM_NONE)
2905 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2907 if (anim_mode == ANIM_DEFAULT)
2908 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2911 game.envelope_active = FALSE;
2913 if (action == ACTION_CLOSING)
2914 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2916 // SetDrawBackgroundMask(last_draw_background_mask);
2918 redraw_mask |= REDRAW_FIELD;
2922 if (action == ACTION_CLOSING &&
2923 game_status == GAME_MODE_PLAYING &&
2924 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2925 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2928 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2932 int graphic = el2preimg(element);
2934 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2935 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2938 void DrawLevel(int draw_background_mask)
2942 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2943 SetDrawBackgroundMask(draw_background_mask);
2947 for (x = BX1; x <= BX2; x++)
2948 for (y = BY1; y <= BY2; y++)
2949 DrawScreenField(x, y);
2951 redraw_mask |= REDRAW_FIELD;
2954 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2959 for (x = 0; x < size_x; x++)
2960 for (y = 0; y < size_y; y++)
2961 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2963 redraw_mask |= REDRAW_FIELD;
2966 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2970 for (x = 0; x < size_x; x++)
2971 for (y = 0; y < size_y; y++)
2972 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2974 redraw_mask |= REDRAW_FIELD;
2977 static void DrawPreviewLevelPlayfield(int from_x, int from_y)
2979 boolean show_level_border = (BorderElement != EL_EMPTY);
2980 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2981 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2982 int tile_size = preview.tile_size;
2983 int preview_width = preview.xsize * tile_size;
2984 int preview_height = preview.ysize * tile_size;
2985 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2986 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2987 int real_preview_width = real_preview_xsize * tile_size;
2988 int real_preview_height = real_preview_ysize * tile_size;
2989 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2990 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2993 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2996 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2998 dst_x += (preview_width - real_preview_width) / 2;
2999 dst_y += (preview_height - real_preview_height) / 2;
3001 for (x = 0; x < real_preview_xsize; x++)
3003 for (y = 0; y < real_preview_ysize; y++)
3005 int lx = from_x + x + (show_level_border ? -1 : 0);
3006 int ly = from_y + y + (show_level_border ? -1 : 0);
3007 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3008 getBorderElement(lx, ly));
3010 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3011 element, tile_size);
3015 redraw_mask |= REDRAW_FIELD;
3018 #define MICROLABEL_EMPTY 0
3019 #define MICROLABEL_LEVEL_NAME 1
3020 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3021 #define MICROLABEL_LEVEL_AUTHOR 3
3022 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3023 #define MICROLABEL_IMPORTED_FROM 5
3024 #define MICROLABEL_IMPORTED_BY_HEAD 6
3025 #define MICROLABEL_IMPORTED_BY 7
3027 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3029 int max_text_width = SXSIZE;
3030 int font_width = getFontWidth(font_nr);
3032 if (pos->align == ALIGN_CENTER)
3033 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3034 else if (pos->align == ALIGN_RIGHT)
3035 max_text_width = pos->x;
3037 max_text_width = SXSIZE - pos->x;
3039 return max_text_width / font_width;
3042 static void DrawPreviewLevelLabelExt(int mode, struct TextPosInfo *pos)
3044 char label_text[MAX_OUTPUT_LINESIZE + 1];
3045 int max_len_label_text;
3046 int font_nr = pos->font;
3049 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3052 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3053 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3054 mode == MICROLABEL_IMPORTED_BY_HEAD)
3055 font_nr = pos->font_alt;
3057 max_len_label_text = getMaxTextLength(pos, font_nr);
3059 if (pos->size != -1)
3060 max_len_label_text = pos->size;
3062 for (i = 0; i < max_len_label_text; i++)
3063 label_text[i] = ' ';
3064 label_text[max_len_label_text] = '\0';
3066 if (strlen(label_text) > 0)
3067 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3070 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3071 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3072 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3073 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3074 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3075 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3076 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3077 max_len_label_text);
3078 label_text[max_len_label_text] = '\0';
3080 if (strlen(label_text) > 0)
3081 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3083 redraw_mask |= REDRAW_FIELD;
3086 static void DrawPreviewLevelLabel(int mode)
3088 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_info_2);
3091 static void DrawPreviewLevelInfo(int mode)
3093 if (mode == MICROLABEL_LEVEL_NAME)
3094 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_name);
3095 else if (mode == MICROLABEL_LEVEL_AUTHOR)
3096 DrawPreviewLevelLabelExt(mode, &menu.main.text.level_author);
3099 static void DrawPreviewLevelExt(boolean restart)
3101 static unsigned int scroll_delay = 0;
3102 static unsigned int label_delay = 0;
3103 static int from_x, from_y, scroll_direction;
3104 static int label_state, label_counter;
3105 unsigned int scroll_delay_value = preview.step_delay;
3106 boolean show_level_border = (BorderElement != EL_EMPTY);
3107 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3108 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3115 if (preview.anim_mode == ANIM_CENTERED)
3117 if (level_xsize > preview.xsize)
3118 from_x = (level_xsize - preview.xsize) / 2;
3119 if (level_ysize > preview.ysize)
3120 from_y = (level_ysize - preview.ysize) / 2;
3123 from_x += preview.xoffset;
3124 from_y += preview.yoffset;
3126 scroll_direction = MV_RIGHT;
3130 DrawPreviewLevelPlayfield(from_x, from_y);
3131 DrawPreviewLevelLabel(label_state);
3133 DrawPreviewLevelInfo(MICROLABEL_LEVEL_NAME);
3134 DrawPreviewLevelInfo(MICROLABEL_LEVEL_AUTHOR);
3136 /* initialize delay counters */
3137 DelayReached(&scroll_delay, 0);
3138 DelayReached(&label_delay, 0);
3140 if (leveldir_current->name)
3142 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3143 char label_text[MAX_OUTPUT_LINESIZE + 1];
3144 int font_nr = pos->font;
3145 int max_len_label_text = getMaxTextLength(pos, font_nr);
3147 if (pos->size != -1)
3148 max_len_label_text = pos->size;
3150 strncpy(label_text, leveldir_current->name, max_len_label_text);
3151 label_text[max_len_label_text] = '\0';
3153 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3154 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3160 /* scroll preview level, if needed */
3161 if (preview.anim_mode != ANIM_NONE &&
3162 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3163 DelayReached(&scroll_delay, scroll_delay_value))
3165 switch (scroll_direction)
3170 from_x -= preview.step_offset;
3171 from_x = (from_x < 0 ? 0 : from_x);
3174 scroll_direction = MV_UP;
3178 if (from_x < level_xsize - preview.xsize)
3180 from_x += preview.step_offset;
3181 from_x = (from_x > level_xsize - preview.xsize ?
3182 level_xsize - preview.xsize : from_x);
3185 scroll_direction = MV_DOWN;
3191 from_y -= preview.step_offset;
3192 from_y = (from_y < 0 ? 0 : from_y);
3195 scroll_direction = MV_RIGHT;
3199 if (from_y < level_ysize - preview.ysize)
3201 from_y += preview.step_offset;
3202 from_y = (from_y > level_ysize - preview.ysize ?
3203 level_ysize - preview.ysize : from_y);
3206 scroll_direction = MV_LEFT;
3213 DrawPreviewLevelPlayfield(from_x, from_y);
3216 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
3217 /* redraw micro level label, if needed */
3218 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3219 !strEqual(level.author, ANONYMOUS_NAME) &&
3220 !strEqual(level.author, leveldir_current->name) &&
3221 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3223 int max_label_counter = 23;
3225 if (leveldir_current->imported_from != NULL &&
3226 strlen(leveldir_current->imported_from) > 0)
3227 max_label_counter += 14;
3228 if (leveldir_current->imported_by != NULL &&
3229 strlen(leveldir_current->imported_by) > 0)
3230 max_label_counter += 14;
3232 label_counter = (label_counter + 1) % max_label_counter;
3233 label_state = (label_counter >= 0 && label_counter <= 7 ?
3234 MICROLABEL_LEVEL_NAME :
3235 label_counter >= 9 && label_counter <= 12 ?
3236 MICROLABEL_LEVEL_AUTHOR_HEAD :
3237 label_counter >= 14 && label_counter <= 21 ?
3238 MICROLABEL_LEVEL_AUTHOR :
3239 label_counter >= 23 && label_counter <= 26 ?
3240 MICROLABEL_IMPORTED_FROM_HEAD :
3241 label_counter >= 28 && label_counter <= 35 ?
3242 MICROLABEL_IMPORTED_FROM :
3243 label_counter >= 37 && label_counter <= 40 ?
3244 MICROLABEL_IMPORTED_BY_HEAD :
3245 label_counter >= 42 && label_counter <= 49 ?
3246 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3248 if (leveldir_current->imported_from == NULL &&
3249 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3250 label_state == MICROLABEL_IMPORTED_FROM))
3251 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3252 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3254 DrawPreviewLevelLabel(label_state);
3258 void DrawPreviewLevelInitial()
3260 DrawPreviewLevelExt(TRUE);
3263 void DrawPreviewLevelAnimation()
3265 DrawPreviewLevelExt(FALSE);
3268 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3269 int graphic, int sync_frame,
3272 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3274 if (mask_mode == USE_MASKING)
3275 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3277 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3280 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3281 int graphic, int sync_frame, int mask_mode)
3283 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3285 if (mask_mode == USE_MASKING)
3286 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3288 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3291 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3293 int lx = LEVELX(x), ly = LEVELY(y);
3295 if (!IN_SCR_FIELD(x, y))
3298 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3299 graphic, GfxFrame[lx][ly], NO_MASKING);
3301 MarkTileDirty(x, y);
3304 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3306 int lx = LEVELX(x), ly = LEVELY(y);
3308 if (!IN_SCR_FIELD(x, y))
3311 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3312 graphic, GfxFrame[lx][ly], NO_MASKING);
3313 MarkTileDirty(x, y);
3316 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3318 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3321 void DrawLevelElementAnimation(int x, int y, int element)
3323 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3325 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3328 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3330 int sx = SCREENX(x), sy = SCREENY(y);
3332 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3335 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3338 DrawGraphicAnimation(sx, sy, graphic);
3341 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3342 DrawLevelFieldCrumbled(x, y);
3344 if (GFX_CRUMBLED(Feld[x][y]))
3345 DrawLevelFieldCrumbled(x, y);
3349 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3351 int sx = SCREENX(x), sy = SCREENY(y);
3354 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3357 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3359 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3362 DrawGraphicAnimation(sx, sy, graphic);
3364 if (GFX_CRUMBLED(element))
3365 DrawLevelFieldCrumbled(x, y);
3368 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3370 if (player->use_murphy)
3372 /* this works only because currently only one player can be "murphy" ... */
3373 static int last_horizontal_dir = MV_LEFT;
3374 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3376 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3377 last_horizontal_dir = move_dir;
3379 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3381 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3383 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3389 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3392 static boolean equalGraphics(int graphic1, int graphic2)
3394 struct GraphicInfo *g1 = &graphic_info[graphic1];
3395 struct GraphicInfo *g2 = &graphic_info[graphic2];
3397 return (g1->bitmap == g2->bitmap &&
3398 g1->src_x == g2->src_x &&
3399 g1->src_y == g2->src_y &&
3400 g1->anim_frames == g2->anim_frames &&
3401 g1->anim_delay == g2->anim_delay &&
3402 g1->anim_mode == g2->anim_mode);
3405 void DrawAllPlayers()
3409 for (i = 0; i < MAX_PLAYERS; i++)
3410 if (stored_player[i].active)
3411 DrawPlayer(&stored_player[i]);
3414 void DrawPlayerField(int x, int y)
3416 if (!IS_PLAYER(x, y))
3419 DrawPlayer(PLAYERINFO(x, y));
3422 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3424 void DrawPlayer(struct PlayerInfo *player)
3426 int jx = player->jx;
3427 int jy = player->jy;
3428 int move_dir = player->MovDir;
3429 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3430 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3431 int last_jx = (player->is_moving ? jx - dx : jx);
3432 int last_jy = (player->is_moving ? jy - dy : jy);
3433 int next_jx = jx + dx;
3434 int next_jy = jy + dy;
3435 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3436 boolean player_is_opaque = FALSE;
3437 int sx = SCREENX(jx), sy = SCREENY(jy);
3438 int sxx = 0, syy = 0;
3439 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3441 int action = ACTION_DEFAULT;
3442 int last_player_graphic = getPlayerGraphic(player, move_dir);
3443 int last_player_frame = player->Frame;
3446 /* GfxElement[][] is set to the element the player is digging or collecting;
3447 remove also for off-screen player if the player is not moving anymore */
3448 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3449 GfxElement[jx][jy] = EL_UNDEFINED;
3451 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3455 if (!IN_LEV_FIELD(jx, jy))
3457 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3458 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3459 printf("DrawPlayerField(): This should never happen!\n");
3464 if (element == EL_EXPLOSION)
3467 action = (player->is_pushing ? ACTION_PUSHING :
3468 player->is_digging ? ACTION_DIGGING :
3469 player->is_collecting ? ACTION_COLLECTING :
3470 player->is_moving ? ACTION_MOVING :
3471 player->is_snapping ? ACTION_SNAPPING :
3472 player->is_dropping ? ACTION_DROPPING :
3473 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3475 if (player->is_waiting)
3476 move_dir = player->dir_waiting;
3478 InitPlayerGfxAnimation(player, action, move_dir);
3480 /* ----------------------------------------------------------------------- */
3481 /* draw things in the field the player is leaving, if needed */
3482 /* ----------------------------------------------------------------------- */
3484 if (player->is_moving)
3486 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3488 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3490 if (last_element == EL_DYNAMITE_ACTIVE ||
3491 last_element == EL_EM_DYNAMITE_ACTIVE ||
3492 last_element == EL_SP_DISK_RED_ACTIVE)
3493 DrawDynamite(last_jx, last_jy);
3495 DrawLevelFieldThruMask(last_jx, last_jy);
3497 else 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 /* !!! this is not enough to prevent flickering of players which are
3503 moving next to each others without a free tile between them -- this
3504 can only be solved by drawing all players layer by layer (first the
3505 background, then the foreground etc.) !!! => TODO */
3506 else if (!IS_PLAYER(last_jx, last_jy))
3507 DrawLevelField(last_jx, last_jy);
3510 DrawLevelField(last_jx, last_jy);
3513 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3514 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3517 if (!IN_SCR_FIELD(sx, sy))
3520 /* ----------------------------------------------------------------------- */
3521 /* draw things behind the player, if needed */
3522 /* ----------------------------------------------------------------------- */
3525 DrawLevelElement(jx, jy, Back[jx][jy]);
3526 else if (IS_ACTIVE_BOMB(element))
3527 DrawLevelElement(jx, jy, EL_EMPTY);
3530 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3532 int old_element = GfxElement[jx][jy];
3533 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3534 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3536 if (GFX_CRUMBLED(old_element))
3537 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3539 DrawGraphic(sx, sy, old_graphic, frame);
3541 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3542 player_is_opaque = TRUE;
3546 GfxElement[jx][jy] = EL_UNDEFINED;
3548 /* make sure that pushed elements are drawn with correct frame rate */
3549 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3551 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3552 GfxFrame[jx][jy] = player->StepFrame;
3554 DrawLevelField(jx, jy);
3558 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3559 /* ----------------------------------------------------------------------- */
3560 /* draw player himself */
3561 /* ----------------------------------------------------------------------- */
3563 graphic = getPlayerGraphic(player, move_dir);
3565 /* in the case of changed player action or direction, prevent the current
3566 animation frame from being restarted for identical animations */
3567 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3568 player->Frame = last_player_frame;
3570 frame = getGraphicAnimationFrame(graphic, player->Frame);
3574 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3575 sxx = player->GfxPos;
3577 syy = player->GfxPos;
3580 if (player_is_opaque)
3581 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3583 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3585 if (SHIELD_ON(player))
3587 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3588 IMG_SHIELD_NORMAL_ACTIVE);
3589 int frame = getGraphicAnimationFrame(graphic, -1);
3591 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3595 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3598 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3599 sxx = player->GfxPos;
3601 syy = player->GfxPos;
3605 /* ----------------------------------------------------------------------- */
3606 /* draw things the player is pushing, if needed */
3607 /* ----------------------------------------------------------------------- */
3609 if (player->is_pushing && player->is_moving)
3611 int px = SCREENX(jx), py = SCREENY(jy);
3612 int pxx = (TILEX - ABS(sxx)) * dx;
3613 int pyy = (TILEY - ABS(syy)) * dy;
3614 int gfx_frame = GfxFrame[jx][jy];
3620 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3622 element = Feld[next_jx][next_jy];
3623 gfx_frame = GfxFrame[next_jx][next_jy];
3626 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3628 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3629 frame = getGraphicAnimationFrame(graphic, sync_frame);
3631 /* draw background element under pushed element (like the Sokoban field) */
3632 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3634 /* this allows transparent pushing animation over non-black background */
3637 DrawLevelElement(jx, jy, Back[jx][jy]);
3639 DrawLevelElement(jx, jy, EL_EMPTY);
3641 if (Back[next_jx][next_jy])
3642 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3644 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3646 else if (Back[next_jx][next_jy])
3647 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3650 /* do not draw (EM style) pushing animation when pushing is finished */
3651 /* (two-tile animations usually do not contain start and end frame) */
3652 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3653 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3655 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3657 /* masked drawing is needed for EMC style (double) movement graphics */
3658 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3659 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3663 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3664 /* ----------------------------------------------------------------------- */
3665 /* draw player himself */
3666 /* ----------------------------------------------------------------------- */
3668 graphic = getPlayerGraphic(player, move_dir);
3670 /* in the case of changed player action or direction, prevent the current
3671 animation frame from being restarted for identical animations */
3672 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3673 player->Frame = last_player_frame;
3675 frame = getGraphicAnimationFrame(graphic, player->Frame);
3679 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3680 sxx = player->GfxPos;
3682 syy = player->GfxPos;
3685 if (player_is_opaque)
3686 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3688 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3690 if (SHIELD_ON(player))
3692 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3693 IMG_SHIELD_NORMAL_ACTIVE);
3694 int frame = getGraphicAnimationFrame(graphic, -1);
3696 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3700 /* ----------------------------------------------------------------------- */
3701 /* draw things in front of player (active dynamite or dynabombs) */
3702 /* ----------------------------------------------------------------------- */
3704 if (IS_ACTIVE_BOMB(element))
3706 graphic = el2img(element);
3707 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3709 if (game.emulation == EMU_SUPAPLEX)
3710 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3712 DrawGraphicThruMask(sx, sy, graphic, frame);
3715 if (player_is_moving && last_element == EL_EXPLOSION)
3717 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3718 GfxElement[last_jx][last_jy] : EL_EMPTY);
3719 int graphic = el_act2img(element, ACTION_EXPLODING);
3720 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3721 int phase = ExplodePhase[last_jx][last_jy] - 1;
3722 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3725 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3728 /* ----------------------------------------------------------------------- */
3729 /* draw elements the player is just walking/passing through/under */
3730 /* ----------------------------------------------------------------------- */
3732 if (player_is_moving)
3734 /* handle the field the player is leaving ... */
3735 if (IS_ACCESSIBLE_INSIDE(last_element))
3736 DrawLevelField(last_jx, last_jy);
3737 else if (IS_ACCESSIBLE_UNDER(last_element))
3738 DrawLevelFieldThruMask(last_jx, last_jy);
3741 /* do not redraw accessible elements if the player is just pushing them */
3742 if (!player_is_moving || !player->is_pushing)
3744 /* ... and the field the player is entering */
3745 if (IS_ACCESSIBLE_INSIDE(element))
3746 DrawLevelField(jx, jy);
3747 else if (IS_ACCESSIBLE_UNDER(element))
3748 DrawLevelFieldThruMask(jx, jy);
3751 MarkTileDirty(sx, sy);
3754 /* ------------------------------------------------------------------------- */
3756 void WaitForEventToContinue()
3758 boolean still_wait = TRUE;
3760 if (program.headless)
3763 /* simulate releasing mouse button over last gadget, if still pressed */
3765 HandleGadgets(-1, -1, 0);
3767 button_status = MB_RELEASED;
3775 if (NextValidEvent(&event))
3779 case EVENT_BUTTONPRESS:
3780 case EVENT_KEYPRESS:
3781 #if defined(TARGET_SDL2)
3782 case SDL_CONTROLLERBUTTONDOWN:
3784 case SDL_JOYBUTTONDOWN:
3788 case EVENT_KEYRELEASE:
3789 ClearPlayerAction();
3793 HandleOtherEvents(&event);
3797 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3806 #define MAX_REQUEST_LINES 13
3807 #define MAX_REQUEST_LINE_FONT1_LEN 7
3808 #define MAX_REQUEST_LINE_FONT2_LEN 10
3810 static int RequestHandleEvents(unsigned int req_state)
3812 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3813 local_player->LevelSolved_GameEnd);
3814 int width = request.width;
3815 int height = request.height;
3819 setRequestPosition(&sx, &sy, FALSE);
3821 button_status = MB_RELEASED;
3823 request_gadget_id = -1;
3830 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3832 HandleGameActions();
3834 SetDrawtoField(DRAW_TO_BACKBUFFER);
3836 if (global.use_envelope_request)
3838 /* copy current state of request area to middle of playfield area */
3839 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
3847 while (NextValidEvent(&event))
3851 case EVENT_BUTTONPRESS:
3852 case EVENT_BUTTONRELEASE:
3853 case EVENT_MOTIONNOTIFY:
3857 if (event.type == EVENT_MOTIONNOTIFY)
3862 motion_status = TRUE;
3863 mx = ((MotionEvent *) &event)->x;
3864 my = ((MotionEvent *) &event)->y;
3868 motion_status = FALSE;
3869 mx = ((ButtonEvent *) &event)->x;
3870 my = ((ButtonEvent *) &event)->y;
3871 if (event.type == EVENT_BUTTONPRESS)
3872 button_status = ((ButtonEvent *) &event)->button;
3874 button_status = MB_RELEASED;
3877 /* this sets 'request_gadget_id' */
3878 HandleGadgets(mx, my, button_status);
3880 switch (request_gadget_id)
3882 case TOOL_CTRL_ID_YES:
3885 case TOOL_CTRL_ID_NO:
3888 case TOOL_CTRL_ID_CONFIRM:
3889 result = TRUE | FALSE;
3892 case TOOL_CTRL_ID_PLAYER_1:
3895 case TOOL_CTRL_ID_PLAYER_2:
3898 case TOOL_CTRL_ID_PLAYER_3:
3901 case TOOL_CTRL_ID_PLAYER_4:
3912 #if defined(TARGET_SDL2)
3913 case SDL_WINDOWEVENT:
3914 HandleWindowEvent((WindowEvent *) &event);
3917 case SDL_APP_WILLENTERBACKGROUND:
3918 case SDL_APP_DIDENTERBACKGROUND:
3919 case SDL_APP_WILLENTERFOREGROUND:
3920 case SDL_APP_DIDENTERFOREGROUND:
3921 HandlePauseResumeEvent((PauseResumeEvent *) &event);
3925 case EVENT_KEYPRESS:
3927 Key key = GetEventKey((KeyEvent *)&event, TRUE);
3932 if (req_state & REQ_CONFIRM)
3937 #if defined(TARGET_SDL2)
3940 #if defined(KSYM_Rewind)
3941 case KSYM_Rewind: /* for Amazon Fire TV remote */
3948 #if defined(TARGET_SDL2)
3950 #if defined(KSYM_FastForward)
3951 case KSYM_FastForward: /* for Amazon Fire TV remote */
3958 HandleKeysDebug(key);
3962 if (req_state & REQ_PLAYER)
3968 case EVENT_KEYRELEASE:
3969 ClearPlayerAction();
3972 #if defined(TARGET_SDL2)
3973 case SDL_CONTROLLERBUTTONDOWN:
3974 switch (event.cbutton.button)
3976 case SDL_CONTROLLER_BUTTON_A:
3977 case SDL_CONTROLLER_BUTTON_X:
3978 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
3982 case SDL_CONTROLLER_BUTTON_B:
3983 case SDL_CONTROLLER_BUTTON_Y:
3984 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
3985 case SDL_CONTROLLER_BUTTON_BACK:
3990 if (req_state & REQ_PLAYER)
3995 case SDL_CONTROLLERBUTTONUP:
3996 HandleJoystickEvent(&event);
3997 ClearPlayerAction();
4002 HandleOtherEvents(&event);
4007 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4009 int joy = AnyJoystick();
4011 if (joy & JOY_BUTTON_1)
4013 else if (joy & JOY_BUTTON_2)
4019 if (global.use_envelope_request)
4021 /* copy back current state of pressed buttons inside request area */
4022 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4032 static boolean RequestDoor(char *text, unsigned int req_state)
4034 unsigned int old_door_state;
4035 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4036 int font_nr = FONT_TEXT_2;
4041 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4043 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4044 font_nr = FONT_TEXT_1;
4047 if (game_status == GAME_MODE_PLAYING)
4048 BlitScreenToBitmap(backbuffer);
4050 /* disable deactivated drawing when quick-loading level tape recording */
4051 if (tape.playing && tape.deactivate_display)
4052 TapeDeactivateDisplayOff(TRUE);
4054 SetMouseCursor(CURSOR_DEFAULT);
4056 #if defined(NETWORK_AVALIABLE)
4057 /* pause network game while waiting for request to answer */
4058 if (options.network &&
4059 game_status == GAME_MODE_PLAYING &&
4060 req_state & REQUEST_WAIT_FOR_INPUT)
4061 SendToServer_PausePlaying();
4064 old_door_state = GetDoorState();
4066 /* simulate releasing mouse button over last gadget, if still pressed */
4068 HandleGadgets(-1, -1, 0);
4072 /* draw released gadget before proceeding */
4075 if (old_door_state & DOOR_OPEN_1)
4077 CloseDoor(DOOR_CLOSE_1);
4079 /* save old door content */
4080 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4081 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4084 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4085 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4087 /* clear door drawing field */
4088 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4090 /* force DOOR font inside door area */
4091 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4093 /* write text for request */
4094 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4096 char text_line[max_request_line_len + 1];
4102 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4104 tc = *(text_ptr + tx);
4105 // if (!tc || tc == ' ')
4106 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4110 if ((tc == '?' || tc == '!') && tl == 0)
4120 strncpy(text_line, text_ptr, tl);
4123 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4124 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4125 text_line, font_nr);
4127 text_ptr += tl + (tc == ' ' ? 1 : 0);
4128 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4133 if (req_state & REQ_ASK)
4135 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4136 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4138 else if (req_state & REQ_CONFIRM)
4140 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4142 else if (req_state & REQ_PLAYER)
4144 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4145 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4146 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4147 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4150 /* copy request gadgets to door backbuffer */
4151 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4153 OpenDoor(DOOR_OPEN_1);
4155 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4157 if (game_status == GAME_MODE_PLAYING)
4159 SetPanelBackground();
4160 SetDrawBackgroundMask(REDRAW_DOOR_1);
4164 SetDrawBackgroundMask(REDRAW_FIELD);
4170 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4172 // ---------- handle request buttons ----------
4173 result = RequestHandleEvents(req_state);
4177 if (!(req_state & REQ_STAY_OPEN))
4179 CloseDoor(DOOR_CLOSE_1);
4181 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4182 (req_state & REQ_REOPEN))
4183 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4188 if (game_status == GAME_MODE_PLAYING)
4190 SetPanelBackground();
4191 SetDrawBackgroundMask(REDRAW_DOOR_1);
4195 SetDrawBackgroundMask(REDRAW_FIELD);
4198 #if defined(NETWORK_AVALIABLE)
4199 /* continue network game after request */
4200 if (options.network &&
4201 game_status == GAME_MODE_PLAYING &&
4202 req_state & REQUEST_WAIT_FOR_INPUT)
4203 SendToServer_ContinuePlaying();
4206 /* restore deactivated drawing when quick-loading level tape recording */
4207 if (tape.playing && tape.deactivate_display)
4208 TapeDeactivateDisplayOn();
4213 static boolean RequestEnvelope(char *text, unsigned int req_state)
4217 if (game_status == GAME_MODE_PLAYING)
4218 BlitScreenToBitmap(backbuffer);
4220 /* disable deactivated drawing when quick-loading level tape recording */
4221 if (tape.playing && tape.deactivate_display)
4222 TapeDeactivateDisplayOff(TRUE);
4224 SetMouseCursor(CURSOR_DEFAULT);
4226 #if defined(NETWORK_AVALIABLE)
4227 /* pause network game while waiting for request to answer */
4228 if (options.network &&
4229 game_status == GAME_MODE_PLAYING &&
4230 req_state & REQUEST_WAIT_FOR_INPUT)
4231 SendToServer_PausePlaying();
4234 /* simulate releasing mouse button over last gadget, if still pressed */
4236 HandleGadgets(-1, -1, 0);
4240 // (replace with setting corresponding request background)
4241 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4242 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4244 /* clear door drawing field */
4245 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4247 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4249 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4251 if (game_status == GAME_MODE_PLAYING)
4253 SetPanelBackground();
4254 SetDrawBackgroundMask(REDRAW_DOOR_1);
4258 SetDrawBackgroundMask(REDRAW_FIELD);
4264 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4266 // ---------- handle request buttons ----------
4267 result = RequestHandleEvents(req_state);
4271 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4275 if (game_status == GAME_MODE_PLAYING)
4277 SetPanelBackground();
4278 SetDrawBackgroundMask(REDRAW_DOOR_1);
4282 SetDrawBackgroundMask(REDRAW_FIELD);
4285 #if defined(NETWORK_AVALIABLE)
4286 /* continue network game after request */
4287 if (options.network &&
4288 game_status == GAME_MODE_PLAYING &&
4289 req_state & REQUEST_WAIT_FOR_INPUT)
4290 SendToServer_ContinuePlaying();
4293 /* restore deactivated drawing when quick-loading level tape recording */
4294 if (tape.playing && tape.deactivate_display)
4295 TapeDeactivateDisplayOn();
4300 boolean Request(char *text, unsigned int req_state)
4302 boolean overlay_active = GetOverlayActive();
4305 SetOverlayActive(FALSE);
4307 if (global.use_envelope_request)
4308 result = RequestEnvelope(text, req_state);
4310 result = RequestDoor(text, req_state);
4312 SetOverlayActive(overlay_active);
4317 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4319 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4320 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4323 if (dpo1->sort_priority != dpo2->sort_priority)
4324 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4326 compare_result = dpo1->nr - dpo2->nr;
4328 return compare_result;
4331 void InitGraphicCompatibilityInfo_Doors()
4337 struct DoorInfo *door;
4341 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4342 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4344 { -1, -1, -1, NULL }
4346 struct Rect door_rect_list[] =
4348 { DX, DY, DXSIZE, DYSIZE },
4349 { VX, VY, VXSIZE, VYSIZE }
4353 for (i = 0; doors[i].door_token != -1; i++)
4355 int door_token = doors[i].door_token;
4356 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4357 int part_1 = doors[i].part_1;
4358 int part_8 = doors[i].part_8;
4359 int part_2 = part_1 + 1;
4360 int part_3 = part_1 + 2;
4361 struct DoorInfo *door = doors[i].door;
4362 struct Rect *door_rect = &door_rect_list[door_index];
4363 boolean door_gfx_redefined = FALSE;
4365 /* check if any door part graphic definitions have been redefined */
4367 for (j = 0; door_part_controls[j].door_token != -1; j++)
4369 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4370 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4372 if (dpc->door_token == door_token && fi->redefined)
4373 door_gfx_redefined = TRUE;
4376 /* check for old-style door graphic/animation modifications */
4378 if (!door_gfx_redefined)
4380 if (door->anim_mode & ANIM_STATIC_PANEL)
4382 door->panel.step_xoffset = 0;
4383 door->panel.step_yoffset = 0;
4386 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4388 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4389 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4390 int num_door_steps, num_panel_steps;
4392 /* remove door part graphics other than the two default wings */
4394 for (j = 0; door_part_controls[j].door_token != -1; j++)
4396 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4397 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4399 if (dpc->graphic >= part_3 &&
4400 dpc->graphic <= part_8)
4404 /* set graphics and screen positions of the default wings */
4406 g_part_1->width = door_rect->width;
4407 g_part_1->height = door_rect->height;
4408 g_part_2->width = door_rect->width;
4409 g_part_2->height = door_rect->height;
4410 g_part_2->src_x = door_rect->width;
4411 g_part_2->src_y = g_part_1->src_y;
4413 door->part_2.x = door->part_1.x;
4414 door->part_2.y = door->part_1.y;
4416 if (door->width != -1)
4418 g_part_1->width = door->width;
4419 g_part_2->width = door->width;
4421 // special treatment for graphics and screen position of right wing
4422 g_part_2->src_x += door_rect->width - door->width;
4423 door->part_2.x += door_rect->width - door->width;
4426 if (door->height != -1)
4428 g_part_1->height = door->height;
4429 g_part_2->height = door->height;
4431 // special treatment for graphics and screen position of bottom wing
4432 g_part_2->src_y += door_rect->height - door->height;
4433 door->part_2.y += door_rect->height - door->height;
4436 /* set animation delays for the default wings and panels */
4438 door->part_1.step_delay = door->step_delay;
4439 door->part_2.step_delay = door->step_delay;
4440 door->panel.step_delay = door->step_delay;
4442 /* set animation draw order for the default wings */
4444 door->part_1.sort_priority = 2; /* draw left wing over ... */
4445 door->part_2.sort_priority = 1; /* ... right wing */
4447 /* set animation draw offset for the default wings */
4449 if (door->anim_mode & ANIM_HORIZONTAL)
4451 door->part_1.step_xoffset = door->step_offset;
4452 door->part_1.step_yoffset = 0;
4453 door->part_2.step_xoffset = door->step_offset * -1;
4454 door->part_2.step_yoffset = 0;
4456 num_door_steps = g_part_1->width / door->step_offset;
4458 else // ANIM_VERTICAL
4460 door->part_1.step_xoffset = 0;
4461 door->part_1.step_yoffset = door->step_offset;
4462 door->part_2.step_xoffset = 0;
4463 door->part_2.step_yoffset = door->step_offset * -1;
4465 num_door_steps = g_part_1->height / door->step_offset;
4468 /* set animation draw offset for the default panels */
4470 if (door->step_offset > 1)
4472 num_panel_steps = 2 * door_rect->height / door->step_offset;
4473 door->panel.start_step = num_panel_steps - num_door_steps;
4474 door->panel.start_step_closing = door->panel.start_step;
4478 num_panel_steps = door_rect->height / door->step_offset;
4479 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4480 door->panel.start_step_closing = door->panel.start_step;
4481 door->panel.step_delay *= 2;
4492 for (i = 0; door_part_controls[i].door_token != -1; i++)
4494 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4495 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4497 /* initialize "start_step_opening" and "start_step_closing", if needed */
4498 if (dpc->pos->start_step_opening == 0 &&
4499 dpc->pos->start_step_closing == 0)
4501 // dpc->pos->start_step_opening = dpc->pos->start_step;
4502 dpc->pos->start_step_closing = dpc->pos->start_step;
4505 /* fill structure for door part draw order (sorted below) */
4507 dpo->sort_priority = dpc->pos->sort_priority;
4510 /* sort door part controls according to sort_priority and graphic number */
4511 qsort(door_part_order, MAX_DOOR_PARTS,
4512 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4515 unsigned int OpenDoor(unsigned int door_state)
4517 if (door_state & DOOR_COPY_BACK)
4519 if (door_state & DOOR_OPEN_1)
4520 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4521 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4523 if (door_state & DOOR_OPEN_2)
4524 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4525 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4527 door_state &= ~DOOR_COPY_BACK;
4530 return MoveDoor(door_state);
4533 unsigned int CloseDoor(unsigned int door_state)
4535 unsigned int old_door_state = GetDoorState();
4537 if (!(door_state & DOOR_NO_COPY_BACK))
4539 if (old_door_state & DOOR_OPEN_1)
4540 BlitBitmap(backbuffer, bitmap_db_door_1,
4541 DX, DY, DXSIZE, DYSIZE, 0, 0);
4543 if (old_door_state & DOOR_OPEN_2)
4544 BlitBitmap(backbuffer, bitmap_db_door_2,
4545 VX, VY, VXSIZE, VYSIZE, 0, 0);
4547 door_state &= ~DOOR_NO_COPY_BACK;
4550 return MoveDoor(door_state);
4553 unsigned int GetDoorState()
4555 return MoveDoor(DOOR_GET_STATE);
4558 unsigned int SetDoorState(unsigned int door_state)
4560 return MoveDoor(door_state | DOOR_SET_STATE);
4563 int euclid(int a, int b)
4565 return (b ? euclid(b, a % b) : a);
4568 unsigned int MoveDoor(unsigned int door_state)
4570 struct Rect door_rect_list[] =
4572 { DX, DY, DXSIZE, DYSIZE },
4573 { VX, VY, VXSIZE, VYSIZE }
4575 static int door1 = DOOR_CLOSE_1;
4576 static int door2 = DOOR_CLOSE_2;
4577 unsigned int door_delay = 0;
4578 unsigned int door_delay_value;
4581 if (door_state == DOOR_GET_STATE)
4582 return (door1 | door2);
4584 if (door_state & DOOR_SET_STATE)
4586 if (door_state & DOOR_ACTION_1)
4587 door1 = door_state & DOOR_ACTION_1;
4588 if (door_state & DOOR_ACTION_2)
4589 door2 = door_state & DOOR_ACTION_2;
4591 return (door1 | door2);
4594 if (!(door_state & DOOR_FORCE_REDRAW))
4596 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4597 door_state &= ~DOOR_OPEN_1;
4598 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4599 door_state &= ~DOOR_CLOSE_1;
4600 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4601 door_state &= ~DOOR_OPEN_2;
4602 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4603 door_state &= ~DOOR_CLOSE_2;
4606 if (global.autoplay_leveldir)
4608 door_state |= DOOR_NO_DELAY;
4609 door_state &= ~DOOR_CLOSE_ALL;
4612 if (game_status == GAME_MODE_EDITOR)
4613 door_state |= DOOR_NO_DELAY;
4615 if (door_state & DOOR_ACTION)
4617 boolean door_panel_drawn[NUM_DOORS];
4618 boolean panel_has_doors[NUM_DOORS];
4619 boolean door_part_skip[MAX_DOOR_PARTS];
4620 boolean door_part_done[MAX_DOOR_PARTS];
4621 boolean door_part_done_all;
4622 int num_steps[MAX_DOOR_PARTS];
4623 int max_move_delay = 0; // delay for complete animations of all doors
4624 int max_step_delay = 0; // delay (ms) between two animation frames
4625 int num_move_steps = 0; // number of animation steps for all doors
4626 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4627 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4628 int current_move_delay = 0;
4632 for (i = 0; i < NUM_DOORS; i++)
4633 panel_has_doors[i] = FALSE;
4635 for (i = 0; i < MAX_DOOR_PARTS; i++)
4637 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4638 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4639 int door_token = dpc->door_token;
4641 door_part_done[i] = FALSE;
4642 door_part_skip[i] = (!(door_state & door_token) ||
4646 for (i = 0; i < MAX_DOOR_PARTS; i++)
4648 int nr = door_part_order[i].nr;
4649 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4650 struct DoorPartPosInfo *pos = dpc->pos;
4651 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4652 int door_token = dpc->door_token;
4653 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4654 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4655 int step_xoffset = ABS(pos->step_xoffset);
4656 int step_yoffset = ABS(pos->step_yoffset);
4657 int step_delay = pos->step_delay;
4658 int current_door_state = door_state & door_token;
4659 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4660 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4661 boolean part_opening = (is_panel ? door_closing : door_opening);
4662 int start_step = (part_opening ? pos->start_step_opening :
4663 pos->start_step_closing);
4664 float move_xsize = (step_xoffset ? g->width : 0);
4665 float move_ysize = (step_yoffset ? g->height : 0);
4666 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4667 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4668 int move_steps = (move_xsteps && move_ysteps ?
4669 MIN(move_xsteps, move_ysteps) :
4670 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4671 int move_delay = move_steps * step_delay;
4673 if (door_part_skip[nr])
4676 max_move_delay = MAX(max_move_delay, move_delay);
4677 max_step_delay = (max_step_delay == 0 ? step_delay :
4678 euclid(max_step_delay, step_delay));
4679 num_steps[nr] = move_steps;
4683 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4685 panel_has_doors[door_index] = TRUE;
4689 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4691 num_move_steps = max_move_delay / max_step_delay;
4692 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4694 door_delay_value = max_step_delay;
4696 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4698 start = num_move_steps - 1;
4702 /* opening door sound has priority over simultaneously closing door */
4703 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4704 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4705 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4706 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4709 for (k = start; k < num_move_steps; k++)
4711 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4713 door_part_done_all = TRUE;
4715 for (i = 0; i < NUM_DOORS; i++)
4716 door_panel_drawn[i] = FALSE;
4718 for (i = 0; i < MAX_DOOR_PARTS; i++)
4720 int nr = door_part_order[i].nr;
4721 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4722 struct DoorPartPosInfo *pos = dpc->pos;
4723 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4724 int door_token = dpc->door_token;
4725 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4726 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4727 boolean is_panel_and_door_has_closed = FALSE;
4728 struct Rect *door_rect = &door_rect_list[door_index];
4729 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4731 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4732 int current_door_state = door_state & door_token;
4733 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4734 boolean door_closing = !door_opening;
4735 boolean part_opening = (is_panel ? door_closing : door_opening);
4736 boolean part_closing = !part_opening;
4737 int start_step = (part_opening ? pos->start_step_opening :
4738 pos->start_step_closing);
4739 int step_delay = pos->step_delay;
4740 int step_factor = step_delay / max_step_delay;
4741 int k1 = (step_factor ? k / step_factor + 1 : k);
4742 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4743 int kk = MAX(0, k2);
4746 int src_x, src_y, src_xx, src_yy;
4747 int dst_x, dst_y, dst_xx, dst_yy;
4750 if (door_part_skip[nr])
4753 if (!(door_state & door_token))
4761 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4762 int kk_door = MAX(0, k2_door);
4763 int sync_frame = kk_door * door_delay_value;
4764 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4766 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
4767 &g_src_x, &g_src_y);
4772 if (!door_panel_drawn[door_index])
4774 ClearRectangle(drawto, door_rect->x, door_rect->y,
4775 door_rect->width, door_rect->height);
4777 door_panel_drawn[door_index] = TRUE;
4780 // draw opening or closing door parts
4782 if (pos->step_xoffset < 0) // door part on right side
4785 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4788 if (dst_xx + width > door_rect->width)
4789 width = door_rect->width - dst_xx;
4791 else // door part on left side
4794 dst_xx = pos->x - kk * pos->step_xoffset;
4798 src_xx = ABS(dst_xx);
4802 width = g->width - src_xx;
4804 if (width > door_rect->width)
4805 width = door_rect->width;
4807 // printf("::: k == %d [%d] \n", k, start_step);
4810 if (pos->step_yoffset < 0) // door part on bottom side
4813 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4816 if (dst_yy + height > door_rect->height)
4817 height = door_rect->height - dst_yy;
4819 else // door part on top side
4822 dst_yy = pos->y - kk * pos->step_yoffset;
4826 src_yy = ABS(dst_yy);
4830 height = g->height - src_yy;
4833 src_x = g_src_x + src_xx;
4834 src_y = g_src_y + src_yy;
4836 dst_x = door_rect->x + dst_xx;
4837 dst_y = door_rect->y + dst_yy;
4839 is_panel_and_door_has_closed =
4842 panel_has_doors[door_index] &&
4843 k >= num_move_steps_doors_only - 1);
4845 if (width >= 0 && width <= g->width &&
4846 height >= 0 && height <= g->height &&
4847 !is_panel_and_door_has_closed)
4849 if (is_panel || !pos->draw_masked)
4850 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4853 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4857 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4859 if ((part_opening && (width < 0 || height < 0)) ||
4860 (part_closing && (width >= g->width && height >= g->height)))
4861 door_part_done[nr] = TRUE;
4863 // continue door part animations, but not panel after door has closed
4864 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4865 door_part_done_all = FALSE;
4868 if (!(door_state & DOOR_NO_DELAY))
4872 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4874 current_move_delay += max_step_delay;
4876 /* prevent OS (Windows) from complaining about program not responding */
4880 if (door_part_done_all)
4885 if (door_state & DOOR_ACTION_1)
4886 door1 = door_state & DOOR_ACTION_1;
4887 if (door_state & DOOR_ACTION_2)
4888 door2 = door_state & DOOR_ACTION_2;
4890 // draw masked border over door area
4891 DrawMaskedBorder(REDRAW_DOOR_1);
4892 DrawMaskedBorder(REDRAW_DOOR_2);
4894 return (door1 | door2);
4897 static boolean useSpecialEditorDoor()
4899 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4900 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4902 // do not draw special editor door if editor border defined or redefined
4903 if (graphic_info[graphic].bitmap != NULL || redefined)
4906 // do not draw special editor door if global border defined to be empty
4907 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4910 // do not draw special editor door if viewport definitions do not match
4914 EY + EYSIZE != VY + VYSIZE)
4920 void DrawSpecialEditorDoor()
4922 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4923 int top_border_width = gfx1->width;
4924 int top_border_height = gfx1->height;
4925 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4926 int ex = EX - outer_border;
4927 int ey = EY - outer_border;
4928 int vy = VY - outer_border;
4929 int exsize = EXSIZE + 2 * outer_border;
4931 if (!useSpecialEditorDoor())
4934 /* draw bigger level editor toolbox window */
4935 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4936 top_border_width, top_border_height, ex, ey - top_border_height);
4937 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4938 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4940 redraw_mask |= REDRAW_ALL;
4943 void UndrawSpecialEditorDoor()
4945 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4946 int top_border_width = gfx1->width;
4947 int top_border_height = gfx1->height;
4948 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4949 int ex = EX - outer_border;
4950 int ey = EY - outer_border;
4951 int ey_top = ey - top_border_height;
4952 int exsize = EXSIZE + 2 * outer_border;
4953 int eysize = EYSIZE + 2 * outer_border;
4955 if (!useSpecialEditorDoor())
4958 /* draw normal tape recorder window */
4959 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4961 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4962 ex, ey_top, top_border_width, top_border_height,
4964 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4965 ex, ey, exsize, eysize, ex, ey);
4969 // if screen background is set to "[NONE]", clear editor toolbox window
4970 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4971 ClearRectangle(drawto, ex, ey, exsize, eysize);
4974 redraw_mask |= REDRAW_ALL;
4978 /* ---------- new tool button stuff ---------------------------------------- */
4983 struct TextPosInfo *pos;
4986 } toolbutton_info[NUM_TOOL_BUTTONS] =
4989 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
4990 TOOL_CTRL_ID_YES, "yes"
4993 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
4994 TOOL_CTRL_ID_NO, "no"
4997 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
4998 TOOL_CTRL_ID_CONFIRM, "confirm"
5001 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
5002 TOOL_CTRL_ID_PLAYER_1, "player 1"
5005 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5006 TOOL_CTRL_ID_PLAYER_2, "player 2"
5009 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5010 TOOL_CTRL_ID_PLAYER_3, "player 3"
5013 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5014 TOOL_CTRL_ID_PLAYER_4, "player 4"
5018 void CreateToolButtons()
5022 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5024 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
5025 struct TextPosInfo *pos = toolbutton_info[i].pos;
5026 struct GadgetInfo *gi;
5027 Bitmap *deco_bitmap = None;
5028 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5029 unsigned int event_mask = GD_EVENT_RELEASED;
5032 int gd_x = gfx->src_x;
5033 int gd_y = gfx->src_y;
5034 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5035 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5038 if (global.use_envelope_request)
5039 setRequestPosition(&dx, &dy, TRUE);
5041 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5043 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5045 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5046 pos->size, &deco_bitmap, &deco_x, &deco_y);
5047 deco_xpos = (gfx->width - pos->size) / 2;
5048 deco_ypos = (gfx->height - pos->size) / 2;
5051 gi = CreateGadget(GDI_CUSTOM_ID, id,
5052 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5053 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
5054 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
5055 GDI_WIDTH, gfx->width,
5056 GDI_HEIGHT, gfx->height,
5057 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5058 GDI_STATE, GD_BUTTON_UNPRESSED,
5059 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5060 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5061 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5062 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5063 GDI_DECORATION_SIZE, pos->size, pos->size,
5064 GDI_DECORATION_SHIFTING, 1, 1,
5065 GDI_DIRECT_DRAW, FALSE,
5066 GDI_EVENT_MASK, event_mask,
5067 GDI_CALLBACK_ACTION, HandleToolButtons,
5071 Error(ERR_EXIT, "cannot create gadget");
5073 tool_gadget[id] = gi;
5077 void FreeToolButtons()
5081 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5082 FreeGadget(tool_gadget[i]);
5085 static void UnmapToolButtons()
5089 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5090 UnmapGadget(tool_gadget[i]);
5093 static void HandleToolButtons(struct GadgetInfo *gi)
5095 request_gadget_id = gi->custom_id;
5098 static struct Mapping_EM_to_RND_object
5101 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
5102 boolean is_backside; /* backside of moving element */
5108 em_object_mapping_list[] =
5111 Xblank, TRUE, FALSE,
5115 Yacid_splash_eB, FALSE, FALSE,
5116 EL_ACID_SPLASH_RIGHT, -1, -1
5119 Yacid_splash_wB, FALSE, FALSE,
5120 EL_ACID_SPLASH_LEFT, -1, -1
5123 #ifdef EM_ENGINE_BAD_ROLL
5125 Xstone_force_e, FALSE, FALSE,
5126 EL_ROCK, -1, MV_BIT_RIGHT
5129 Xstone_force_w, FALSE, FALSE,
5130 EL_ROCK, -1, MV_BIT_LEFT
5133 Xnut_force_e, FALSE, FALSE,
5134 EL_NUT, -1, MV_BIT_RIGHT
5137 Xnut_force_w, FALSE, FALSE,
5138 EL_NUT, -1, MV_BIT_LEFT
5141 Xspring_force_e, FALSE, FALSE,
5142 EL_SPRING, -1, MV_BIT_RIGHT
5145 Xspring_force_w, FALSE, FALSE,
5146 EL_SPRING, -1, MV_BIT_LEFT
5149 Xemerald_force_e, FALSE, FALSE,
5150 EL_EMERALD, -1, MV_BIT_RIGHT
5153 Xemerald_force_w, FALSE, FALSE,
5154 EL_EMERALD, -1, MV_BIT_LEFT
5157 Xdiamond_force_e, FALSE, FALSE,
5158 EL_DIAMOND, -1, MV_BIT_RIGHT
5161 Xdiamond_force_w, FALSE, FALSE,
5162 EL_DIAMOND, -1, MV_BIT_LEFT
5165 Xbomb_force_e, FALSE, FALSE,
5166 EL_BOMB, -1, MV_BIT_RIGHT
5169 Xbomb_force_w, FALSE, FALSE,
5170 EL_BOMB, -1, MV_BIT_LEFT
5172 #endif /* EM_ENGINE_BAD_ROLL */
5175 Xstone, TRUE, FALSE,
5179 Xstone_pause, FALSE, FALSE,
5183 Xstone_fall, FALSE, FALSE,
5187 Ystone_s, FALSE, FALSE,
5188 EL_ROCK, ACTION_FALLING, -1
5191 Ystone_sB, FALSE, TRUE,
5192 EL_ROCK, ACTION_FALLING, -1
5195 Ystone_e, FALSE, FALSE,
5196 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5199 Ystone_eB, FALSE, TRUE,
5200 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5203 Ystone_w, FALSE, FALSE,
5204 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5207 Ystone_wB, FALSE, TRUE,
5208 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5215 Xnut_pause, FALSE, FALSE,
5219 Xnut_fall, FALSE, FALSE,
5223 Ynut_s, FALSE, FALSE,
5224 EL_NUT, ACTION_FALLING, -1
5227 Ynut_sB, FALSE, TRUE,
5228 EL_NUT, ACTION_FALLING, -1
5231 Ynut_e, FALSE, FALSE,
5232 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5235 Ynut_eB, FALSE, TRUE,
5236 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5239 Ynut_w, FALSE, FALSE,
5240 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5243 Ynut_wB, FALSE, TRUE,
5244 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5247 Xbug_n, TRUE, FALSE,
5251 Xbug_e, TRUE, FALSE,
5252 EL_BUG_RIGHT, -1, -1
5255 Xbug_s, TRUE, FALSE,
5259 Xbug_w, TRUE, FALSE,
5263 Xbug_gon, FALSE, FALSE,
5267 Xbug_goe, FALSE, FALSE,
5268 EL_BUG_RIGHT, -1, -1
5271 Xbug_gos, FALSE, FALSE,
5275 Xbug_gow, FALSE, FALSE,
5279 Ybug_n, FALSE, FALSE,
5280 EL_BUG, ACTION_MOVING, MV_BIT_UP
5283 Ybug_nB, FALSE, TRUE,
5284 EL_BUG, ACTION_MOVING, MV_BIT_UP
5287 Ybug_e, FALSE, FALSE,
5288 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5291 Ybug_eB, FALSE, TRUE,
5292 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5295 Ybug_s, FALSE, FALSE,
5296 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5299 Ybug_sB, FALSE, TRUE,
5300 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5303 Ybug_w, FALSE, FALSE,
5304 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5307 Ybug_wB, FALSE, TRUE,
5308 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5311 Ybug_w_n, FALSE, FALSE,
5312 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5315 Ybug_n_e, FALSE, FALSE,
5316 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5319 Ybug_e_s, FALSE, FALSE,
5320 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5323 Ybug_s_w, FALSE, FALSE,
5324 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5327 Ybug_e_n, FALSE, FALSE,
5328 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5331 Ybug_s_e, FALSE, FALSE,
5332 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5335 Ybug_w_s, FALSE, FALSE,
5336 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5339 Ybug_n_w, FALSE, FALSE,
5340 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5343 Ybug_stone, FALSE, FALSE,
5344 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5347 Ybug_spring, FALSE, FALSE,
5348 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5351 Xtank_n, TRUE, FALSE,
5352 EL_SPACESHIP_UP, -1, -1
5355 Xtank_e, TRUE, FALSE,
5356 EL_SPACESHIP_RIGHT, -1, -1
5359 Xtank_s, TRUE, FALSE,
5360 EL_SPACESHIP_DOWN, -1, -1
5363 Xtank_w, TRUE, FALSE,
5364 EL_SPACESHIP_LEFT, -1, -1
5367 Xtank_gon, FALSE, FALSE,
5368 EL_SPACESHIP_UP, -1, -1
5371 Xtank_goe, FALSE, FALSE,
5372 EL_SPACESHIP_RIGHT, -1, -1
5375 Xtank_gos, FALSE, FALSE,
5376 EL_SPACESHIP_DOWN, -1, -1
5379 Xtank_gow, FALSE, FALSE,
5380 EL_SPACESHIP_LEFT, -1, -1
5383 Ytank_n, FALSE, FALSE,
5384 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5387 Ytank_nB, FALSE, TRUE,
5388 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5391 Ytank_e, FALSE, FALSE,
5392 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5395 Ytank_eB, FALSE, TRUE,
5396 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5399 Ytank_s, FALSE, FALSE,
5400 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5403 Ytank_sB, FALSE, TRUE,
5404 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5407 Ytank_w, FALSE, FALSE,
5408 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5411 Ytank_wB, FALSE, TRUE,
5412 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5415 Ytank_w_n, FALSE, FALSE,
5416 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5419 Ytank_n_e, FALSE, FALSE,
5420 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5423 Ytank_e_s, FALSE, FALSE,
5424 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5427 Ytank_s_w, FALSE, FALSE,
5428 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5431 Ytank_e_n, FALSE, FALSE,
5432 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5435 Ytank_s_e, FALSE, FALSE,
5436 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5439 Ytank_w_s, FALSE, FALSE,
5440 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5443 Ytank_n_w, FALSE, FALSE,
5444 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5447 Ytank_stone, FALSE, FALSE,
5448 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5451 Ytank_spring, FALSE, FALSE,
5452 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5455 Xandroid, TRUE, FALSE,
5456 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5459 Xandroid_1_n, FALSE, FALSE,
5460 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5463 Xandroid_2_n, FALSE, FALSE,
5464 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5467 Xandroid_1_e, FALSE, FALSE,
5468 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5471 Xandroid_2_e, FALSE, FALSE,
5472 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5475 Xandroid_1_w, FALSE, FALSE,
5476 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5479 Xandroid_2_w, FALSE, FALSE,
5480 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5483 Xandroid_1_s, FALSE, FALSE,
5484 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5487 Xandroid_2_s, FALSE, FALSE,
5488 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5491 Yandroid_n, FALSE, FALSE,
5492 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5495 Yandroid_nB, FALSE, TRUE,
5496 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5499 Yandroid_ne, FALSE, FALSE,
5500 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5503 Yandroid_neB, FALSE, TRUE,
5504 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5507 Yandroid_e, FALSE, FALSE,
5508 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5511 Yandroid_eB, FALSE, TRUE,
5512 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5515 Yandroid_se, FALSE, FALSE,
5516 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5519 Yandroid_seB, FALSE, TRUE,
5520 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5523 Yandroid_s, FALSE, FALSE,
5524 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5527 Yandroid_sB, FALSE, TRUE,
5528 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5531 Yandroid_sw, FALSE, FALSE,
5532 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5535 Yandroid_swB, FALSE, TRUE,
5536 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5539 Yandroid_w, FALSE, FALSE,
5540 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5543 Yandroid_wB, FALSE, TRUE,
5544 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5547 Yandroid_nw, FALSE, FALSE,
5548 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5551 Yandroid_nwB, FALSE, TRUE,
5552 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5555 Xspring, TRUE, FALSE,
5559 Xspring_pause, FALSE, FALSE,
5563 Xspring_e, FALSE, FALSE,
5567 Xspring_w, FALSE, FALSE,
5571 Xspring_fall, FALSE, FALSE,
5575 Yspring_s, FALSE, FALSE,
5576 EL_SPRING, ACTION_FALLING, -1
5579 Yspring_sB, FALSE, TRUE,
5580 EL_SPRING, ACTION_FALLING, -1
5583 Yspring_e, FALSE, FALSE,
5584 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5587 Yspring_eB, FALSE, TRUE,
5588 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5591 Yspring_w, FALSE, FALSE,
5592 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5595 Yspring_wB, FALSE, TRUE,
5596 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5599 Yspring_kill_e, FALSE, FALSE,
5600 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5603 Yspring_kill_eB, FALSE, TRUE,
5604 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5607 Yspring_kill_w, FALSE, FALSE,
5608 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5611 Yspring_kill_wB, FALSE, TRUE,
5612 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5615 Xeater_n, TRUE, FALSE,
5616 EL_YAMYAM_UP, -1, -1
5619 Xeater_e, TRUE, FALSE,
5620 EL_YAMYAM_RIGHT, -1, -1
5623 Xeater_w, TRUE, FALSE,
5624 EL_YAMYAM_LEFT, -1, -1
5627 Xeater_s, TRUE, FALSE,
5628 EL_YAMYAM_DOWN, -1, -1
5631 Yeater_n, FALSE, FALSE,
5632 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5635 Yeater_nB, FALSE, TRUE,
5636 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5639 Yeater_e, FALSE, FALSE,
5640 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5643 Yeater_eB, FALSE, TRUE,
5644 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5647 Yeater_s, FALSE, FALSE,
5648 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5651 Yeater_sB, FALSE, TRUE,
5652 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5655 Yeater_w, FALSE, FALSE,
5656 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5659 Yeater_wB, FALSE, TRUE,
5660 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5663 Yeater_stone, FALSE, FALSE,
5664 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5667 Yeater_spring, FALSE, FALSE,
5668 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5671 Xalien, TRUE, FALSE,
5675 Xalien_pause, FALSE, FALSE,
5679 Yalien_n, FALSE, FALSE,
5680 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5683 Yalien_nB, FALSE, TRUE,
5684 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5687 Yalien_e, FALSE, FALSE,
5688 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5691 Yalien_eB, FALSE, TRUE,
5692 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5695 Yalien_s, FALSE, FALSE,
5696 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5699 Yalien_sB, FALSE, TRUE,
5700 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5703 Yalien_w, FALSE, FALSE,
5704 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5707 Yalien_wB, FALSE, TRUE,
5708 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5711 Yalien_stone, FALSE, FALSE,
5712 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5715 Yalien_spring, FALSE, FALSE,
5716 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5719 Xemerald, TRUE, FALSE,
5723 Xemerald_pause, FALSE, FALSE,
5727 Xemerald_fall, FALSE, FALSE,
5731 Xemerald_shine, FALSE, FALSE,
5732 EL_EMERALD, ACTION_TWINKLING, -1
5735 Yemerald_s, FALSE, FALSE,
5736 EL_EMERALD, ACTION_FALLING, -1
5739 Yemerald_sB, FALSE, TRUE,
5740 EL_EMERALD, ACTION_FALLING, -1
5743 Yemerald_e, FALSE, FALSE,
5744 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5747 Yemerald_eB, FALSE, TRUE,
5748 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5751 Yemerald_w, FALSE, FALSE,
5752 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5755 Yemerald_wB, FALSE, TRUE,
5756 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5759 Yemerald_eat, FALSE, FALSE,
5760 EL_EMERALD, ACTION_COLLECTING, -1
5763 Yemerald_stone, FALSE, FALSE,
5764 EL_NUT, ACTION_BREAKING, -1
5767 Xdiamond, TRUE, FALSE,
5771 Xdiamond_pause, FALSE, FALSE,
5775 Xdiamond_fall, FALSE, FALSE,
5779 Xdiamond_shine, FALSE, FALSE,
5780 EL_DIAMOND, ACTION_TWINKLING, -1
5783 Ydiamond_s, FALSE, FALSE,
5784 EL_DIAMOND, ACTION_FALLING, -1
5787 Ydiamond_sB, FALSE, TRUE,
5788 EL_DIAMOND, ACTION_FALLING, -1
5791 Ydiamond_e, FALSE, FALSE,
5792 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5795 Ydiamond_eB, FALSE, TRUE,
5796 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5799 Ydiamond_w, FALSE, FALSE,
5800 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5803 Ydiamond_wB, FALSE, TRUE,
5804 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5807 Ydiamond_eat, FALSE, FALSE,
5808 EL_DIAMOND, ACTION_COLLECTING, -1
5811 Ydiamond_stone, FALSE, FALSE,
5812 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5815 Xdrip_fall, TRUE, FALSE,
5816 EL_AMOEBA_DROP, -1, -1
5819 Xdrip_stretch, FALSE, FALSE,
5820 EL_AMOEBA_DROP, ACTION_FALLING, -1
5823 Xdrip_stretchB, FALSE, TRUE,
5824 EL_AMOEBA_DROP, ACTION_FALLING, -1
5827 Xdrip_eat, FALSE, FALSE,
5828 EL_AMOEBA_DROP, ACTION_GROWING, -1
5831 Ydrip_s1, FALSE, FALSE,
5832 EL_AMOEBA_DROP, ACTION_FALLING, -1
5835 Ydrip_s1B, FALSE, TRUE,
5836 EL_AMOEBA_DROP, ACTION_FALLING, -1
5839 Ydrip_s2, FALSE, FALSE,
5840 EL_AMOEBA_DROP, ACTION_FALLING, -1
5843 Ydrip_s2B, FALSE, TRUE,
5844 EL_AMOEBA_DROP, ACTION_FALLING, -1
5851 Xbomb_pause, FALSE, FALSE,
5855 Xbomb_fall, FALSE, FALSE,
5859 Ybomb_s, FALSE, FALSE,
5860 EL_BOMB, ACTION_FALLING, -1
5863 Ybomb_sB, FALSE, TRUE,
5864 EL_BOMB, ACTION_FALLING, -1
5867 Ybomb_e, FALSE, FALSE,
5868 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5871 Ybomb_eB, FALSE, TRUE,
5872 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5875 Ybomb_w, FALSE, FALSE,
5876 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5879 Ybomb_wB, FALSE, TRUE,
5880 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5883 Ybomb_eat, FALSE, FALSE,
5884 EL_BOMB, ACTION_ACTIVATING, -1
5887 Xballoon, TRUE, FALSE,
5891 Yballoon_n, FALSE, FALSE,
5892 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5895 Yballoon_nB, FALSE, TRUE,
5896 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5899 Yballoon_e, FALSE, FALSE,
5900 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5903 Yballoon_eB, FALSE, TRUE,
5904 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5907 Yballoon_s, FALSE, FALSE,
5908 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5911 Yballoon_sB, FALSE, TRUE,
5912 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5915 Yballoon_w, FALSE, FALSE,
5916 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5919 Yballoon_wB, FALSE, TRUE,
5920 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5923 Xgrass, TRUE, FALSE,
5924 EL_EMC_GRASS, -1, -1
5927 Ygrass_nB, FALSE, FALSE,
5928 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5931 Ygrass_eB, FALSE, FALSE,
5932 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5935 Ygrass_sB, FALSE, FALSE,
5936 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5939 Ygrass_wB, FALSE, FALSE,
5940 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5947 Ydirt_nB, FALSE, FALSE,
5948 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5951 Ydirt_eB, FALSE, FALSE,
5952 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5955 Ydirt_sB, FALSE, FALSE,
5956 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5959 Ydirt_wB, FALSE, FALSE,
5960 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5963 Xacid_ne, TRUE, FALSE,
5964 EL_ACID_POOL_TOPRIGHT, -1, -1
5967 Xacid_se, TRUE, FALSE,
5968 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5971 Xacid_s, TRUE, FALSE,
5972 EL_ACID_POOL_BOTTOM, -1, -1
5975 Xacid_sw, TRUE, FALSE,
5976 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5979 Xacid_nw, TRUE, FALSE,
5980 EL_ACID_POOL_TOPLEFT, -1, -1
5983 Xacid_1, TRUE, FALSE,
5987 Xacid_2, FALSE, FALSE,
5991 Xacid_3, FALSE, FALSE,
5995 Xacid_4, FALSE, FALSE,
5999 Xacid_5, FALSE, FALSE,
6003 Xacid_6, FALSE, FALSE,
6007 Xacid_7, FALSE, FALSE,
6011 Xacid_8, FALSE, FALSE,
6015 Xball_1, TRUE, FALSE,
6016 EL_EMC_MAGIC_BALL, -1, -1
6019 Xball_1B, FALSE, FALSE,
6020 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6023 Xball_2, FALSE, FALSE,
6024 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6027 Xball_2B, FALSE, FALSE,
6028 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6031 Yball_eat, FALSE, FALSE,
6032 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6035 Ykey_1_eat, FALSE, FALSE,
6036 EL_EM_KEY_1, ACTION_COLLECTING, -1
6039 Ykey_2_eat, FALSE, FALSE,
6040 EL_EM_KEY_2, ACTION_COLLECTING, -1
6043 Ykey_3_eat, FALSE, FALSE,
6044 EL_EM_KEY_3, ACTION_COLLECTING, -1
6047 Ykey_4_eat, FALSE, FALSE,
6048 EL_EM_KEY_4, ACTION_COLLECTING, -1
6051 Ykey_5_eat, FALSE, FALSE,
6052 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6055 Ykey_6_eat, FALSE, FALSE,
6056 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6059 Ykey_7_eat, FALSE, FALSE,
6060 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6063 Ykey_8_eat, FALSE, FALSE,
6064 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6067 Ylenses_eat, FALSE, FALSE,
6068 EL_EMC_LENSES, ACTION_COLLECTING, -1
6071 Ymagnify_eat, FALSE, FALSE,
6072 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6075 Ygrass_eat, FALSE, FALSE,
6076 EL_EMC_GRASS, ACTION_SNAPPING, -1
6079 Ydirt_eat, FALSE, FALSE,
6080 EL_SAND, ACTION_SNAPPING, -1
6083 Xgrow_ns, TRUE, FALSE,
6084 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6087 Ygrow_ns_eat, FALSE, FALSE,
6088 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6091 Xgrow_ew, TRUE, FALSE,
6092 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6095 Ygrow_ew_eat, FALSE, FALSE,
6096 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6099 Xwonderwall, TRUE, FALSE,
6100 EL_MAGIC_WALL, -1, -1
6103 XwonderwallB, FALSE, FALSE,
6104 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6107 Xamoeba_1, TRUE, FALSE,
6108 EL_AMOEBA_DRY, ACTION_OTHER, -1
6111 Xamoeba_2, FALSE, FALSE,
6112 EL_AMOEBA_DRY, ACTION_OTHER, -1
6115 Xamoeba_3, FALSE, FALSE,
6116 EL_AMOEBA_DRY, ACTION_OTHER, -1
6119 Xamoeba_4, FALSE, FALSE,
6120 EL_AMOEBA_DRY, ACTION_OTHER, -1
6123 Xamoeba_5, TRUE, FALSE,
6124 EL_AMOEBA_WET, ACTION_OTHER, -1
6127 Xamoeba_6, FALSE, FALSE,
6128 EL_AMOEBA_WET, ACTION_OTHER, -1
6131 Xamoeba_7, FALSE, FALSE,
6132 EL_AMOEBA_WET, ACTION_OTHER, -1
6135 Xamoeba_8, FALSE, FALSE,
6136 EL_AMOEBA_WET, ACTION_OTHER, -1
6139 Xdoor_1, TRUE, FALSE,
6140 EL_EM_GATE_1, -1, -1
6143 Xdoor_2, TRUE, FALSE,
6144 EL_EM_GATE_2, -1, -1
6147 Xdoor_3, TRUE, FALSE,
6148 EL_EM_GATE_3, -1, -1
6151 Xdoor_4, TRUE, FALSE,
6152 EL_EM_GATE_4, -1, -1
6155 Xdoor_5, TRUE, FALSE,
6156 EL_EMC_GATE_5, -1, -1
6159 Xdoor_6, TRUE, FALSE,
6160 EL_EMC_GATE_6, -1, -1
6163 Xdoor_7, TRUE, FALSE,
6164 EL_EMC_GATE_7, -1, -1
6167 Xdoor_8, TRUE, FALSE,
6168 EL_EMC_GATE_8, -1, -1
6171 Xkey_1, TRUE, FALSE,
6175 Xkey_2, TRUE, FALSE,
6179 Xkey_3, TRUE, FALSE,
6183 Xkey_4, TRUE, FALSE,
6187 Xkey_5, TRUE, FALSE,
6188 EL_EMC_KEY_5, -1, -1
6191 Xkey_6, TRUE, FALSE,
6192 EL_EMC_KEY_6, -1, -1
6195 Xkey_7, TRUE, FALSE,
6196 EL_EMC_KEY_7, -1, -1
6199 Xkey_8, TRUE, FALSE,
6200 EL_EMC_KEY_8, -1, -1
6203 Xwind_n, TRUE, FALSE,
6204 EL_BALLOON_SWITCH_UP, -1, -1
6207 Xwind_e, TRUE, FALSE,
6208 EL_BALLOON_SWITCH_RIGHT, -1, -1
6211 Xwind_s, TRUE, FALSE,
6212 EL_BALLOON_SWITCH_DOWN, -1, -1
6215 Xwind_w, TRUE, FALSE,
6216 EL_BALLOON_SWITCH_LEFT, -1, -1
6219 Xwind_nesw, TRUE, FALSE,
6220 EL_BALLOON_SWITCH_ANY, -1, -1
6223 Xwind_stop, TRUE, FALSE,
6224 EL_BALLOON_SWITCH_NONE, -1, -1
6228 EL_EM_EXIT_CLOSED, -1, -1
6231 Xexit_1, TRUE, FALSE,
6232 EL_EM_EXIT_OPEN, -1, -1
6235 Xexit_2, FALSE, FALSE,
6236 EL_EM_EXIT_OPEN, -1, -1
6239 Xexit_3, FALSE, FALSE,
6240 EL_EM_EXIT_OPEN, -1, -1
6243 Xdynamite, TRUE, FALSE,
6244 EL_EM_DYNAMITE, -1, -1
6247 Ydynamite_eat, FALSE, FALSE,
6248 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6251 Xdynamite_1, TRUE, FALSE,
6252 EL_EM_DYNAMITE_ACTIVE, -1, -1
6255 Xdynamite_2, FALSE, FALSE,
6256 EL_EM_DYNAMITE_ACTIVE, -1, -1
6259 Xdynamite_3, FALSE, FALSE,
6260 EL_EM_DYNAMITE_ACTIVE, -1, -1
6263 Xdynamite_4, FALSE, FALSE,
6264 EL_EM_DYNAMITE_ACTIVE, -1, -1
6267 Xbumper, TRUE, FALSE,
6268 EL_EMC_SPRING_BUMPER, -1, -1
6271 XbumperB, FALSE, FALSE,
6272 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6275 Xwheel, TRUE, FALSE,
6276 EL_ROBOT_WHEEL, -1, -1
6279 XwheelB, FALSE, FALSE,
6280 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6283 Xswitch, TRUE, FALSE,
6284 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6287 XswitchB, FALSE, FALSE,
6288 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6292 EL_QUICKSAND_EMPTY, -1, -1
6295 Xsand_stone, TRUE, FALSE,
6296 EL_QUICKSAND_FULL, -1, -1
6299 Xsand_stonein_1, FALSE, TRUE,
6300 EL_ROCK, ACTION_FILLING, -1
6303 Xsand_stonein_2, FALSE, TRUE,
6304 EL_ROCK, ACTION_FILLING, -1
6307 Xsand_stonein_3, FALSE, TRUE,
6308 EL_ROCK, ACTION_FILLING, -1
6311 Xsand_stonein_4, FALSE, TRUE,
6312 EL_ROCK, ACTION_FILLING, -1
6315 Xsand_stonesand_1, FALSE, FALSE,
6316 EL_QUICKSAND_EMPTYING, -1, -1
6319 Xsand_stonesand_2, FALSE, FALSE,
6320 EL_QUICKSAND_EMPTYING, -1, -1
6323 Xsand_stonesand_3, FALSE, FALSE,
6324 EL_QUICKSAND_EMPTYING, -1, -1
6327 Xsand_stonesand_4, FALSE, FALSE,
6328 EL_QUICKSAND_EMPTYING, -1, -1
6331 Xsand_stonesand_quickout_1, FALSE, FALSE,
6332 EL_QUICKSAND_EMPTYING, -1, -1
6335 Xsand_stonesand_quickout_2, FALSE, FALSE,
6336 EL_QUICKSAND_EMPTYING, -1, -1
6339 Xsand_stoneout_1, FALSE, FALSE,
6340 EL_ROCK, ACTION_EMPTYING, -1
6343 Xsand_stoneout_2, FALSE, FALSE,
6344 EL_ROCK, ACTION_EMPTYING, -1
6347 Xsand_sandstone_1, FALSE, FALSE,
6348 EL_QUICKSAND_FILLING, -1, -1
6351 Xsand_sandstone_2, FALSE, FALSE,
6352 EL_QUICKSAND_FILLING, -1, -1
6355 Xsand_sandstone_3, FALSE, FALSE,
6356 EL_QUICKSAND_FILLING, -1, -1
6359 Xsand_sandstone_4, FALSE, FALSE,
6360 EL_QUICKSAND_FILLING, -1, -1
6363 Xplant, TRUE, FALSE,
6364 EL_EMC_PLANT, -1, -1
6367 Yplant, FALSE, FALSE,
6368 EL_EMC_PLANT, -1, -1
6371 Xlenses, TRUE, FALSE,
6372 EL_EMC_LENSES, -1, -1
6375 Xmagnify, TRUE, FALSE,
6376 EL_EMC_MAGNIFIER, -1, -1
6379 Xdripper, TRUE, FALSE,
6380 EL_EMC_DRIPPER, -1, -1
6383 XdripperB, FALSE, FALSE,
6384 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6387 Xfake_blank, TRUE, FALSE,
6388 EL_INVISIBLE_WALL, -1, -1
6391 Xfake_blankB, FALSE, FALSE,
6392 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6395 Xfake_grass, TRUE, FALSE,
6396 EL_EMC_FAKE_GRASS, -1, -1
6399 Xfake_grassB, FALSE, FALSE,
6400 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6403 Xfake_door_1, TRUE, FALSE,
6404 EL_EM_GATE_1_GRAY, -1, -1
6407 Xfake_door_2, TRUE, FALSE,
6408 EL_EM_GATE_2_GRAY, -1, -1
6411 Xfake_door_3, TRUE, FALSE,
6412 EL_EM_GATE_3_GRAY, -1, -1
6415 Xfake_door_4, TRUE, FALSE,
6416 EL_EM_GATE_4_GRAY, -1, -1
6419 Xfake_door_5, TRUE, FALSE,
6420 EL_EMC_GATE_5_GRAY, -1, -1
6423 Xfake_door_6, TRUE, FALSE,
6424 EL_EMC_GATE_6_GRAY, -1, -1
6427 Xfake_door_7, TRUE, FALSE,
6428 EL_EMC_GATE_7_GRAY, -1, -1
6431 Xfake_door_8, TRUE, FALSE,
6432 EL_EMC_GATE_8_GRAY, -1, -1
6435 Xfake_acid_1, TRUE, FALSE,
6436 EL_EMC_FAKE_ACID, -1, -1
6439 Xfake_acid_2, FALSE, FALSE,
6440 EL_EMC_FAKE_ACID, -1, -1
6443 Xfake_acid_3, FALSE, FALSE,
6444 EL_EMC_FAKE_ACID, -1, -1
6447 Xfake_acid_4, FALSE, FALSE,
6448 EL_EMC_FAKE_ACID, -1, -1
6451 Xfake_acid_5, FALSE, FALSE,
6452 EL_EMC_FAKE_ACID, -1, -1
6455 Xfake_acid_6, FALSE, FALSE,
6456 EL_EMC_FAKE_ACID, -1, -1
6459 Xfake_acid_7, FALSE, FALSE,
6460 EL_EMC_FAKE_ACID, -1, -1
6463 Xfake_acid_8, FALSE, FALSE,
6464 EL_EMC_FAKE_ACID, -1, -1
6467 Xsteel_1, TRUE, FALSE,
6468 EL_STEELWALL, -1, -1
6471 Xsteel_2, TRUE, FALSE,
6472 EL_EMC_STEELWALL_2, -1, -1
6475 Xsteel_3, TRUE, FALSE,
6476 EL_EMC_STEELWALL_3, -1, -1
6479 Xsteel_4, TRUE, FALSE,
6480 EL_EMC_STEELWALL_4, -1, -1
6483 Xwall_1, TRUE, FALSE,
6487 Xwall_2, TRUE, FALSE,
6488 EL_EMC_WALL_14, -1, -1
6491 Xwall_3, TRUE, FALSE,
6492 EL_EMC_WALL_15, -1, -1
6495 Xwall_4, TRUE, FALSE,
6496 EL_EMC_WALL_16, -1, -1
6499 Xround_wall_1, TRUE, FALSE,
6500 EL_WALL_SLIPPERY, -1, -1
6503 Xround_wall_2, TRUE, FALSE,
6504 EL_EMC_WALL_SLIPPERY_2, -1, -1
6507 Xround_wall_3, TRUE, FALSE,
6508 EL_EMC_WALL_SLIPPERY_3, -1, -1
6511 Xround_wall_4, TRUE, FALSE,
6512 EL_EMC_WALL_SLIPPERY_4, -1, -1
6515 Xdecor_1, TRUE, FALSE,
6516 EL_EMC_WALL_8, -1, -1
6519 Xdecor_2, TRUE, FALSE,
6520 EL_EMC_WALL_6, -1, -1
6523 Xdecor_3, TRUE, FALSE,
6524 EL_EMC_WALL_4, -1, -1
6527 Xdecor_4, TRUE, FALSE,
6528 EL_EMC_WALL_7, -1, -1
6531 Xdecor_5, TRUE, FALSE,
6532 EL_EMC_WALL_5, -1, -1
6535 Xdecor_6, TRUE, FALSE,
6536 EL_EMC_WALL_9, -1, -1
6539 Xdecor_7, TRUE, FALSE,
6540 EL_EMC_WALL_10, -1, -1
6543 Xdecor_8, TRUE, FALSE,
6544 EL_EMC_WALL_1, -1, -1
6547 Xdecor_9, TRUE, FALSE,
6548 EL_EMC_WALL_2, -1, -1
6551 Xdecor_10, TRUE, FALSE,
6552 EL_EMC_WALL_3, -1, -1
6555 Xdecor_11, TRUE, FALSE,
6556 EL_EMC_WALL_11, -1, -1
6559 Xdecor_12, TRUE, FALSE,
6560 EL_EMC_WALL_12, -1, -1
6563 Xalpha_0, TRUE, FALSE,
6564 EL_CHAR('0'), -1, -1
6567 Xalpha_1, TRUE, FALSE,
6568 EL_CHAR('1'), -1, -1
6571 Xalpha_2, TRUE, FALSE,
6572 EL_CHAR('2'), -1, -1
6575 Xalpha_3, TRUE, FALSE,
6576 EL_CHAR('3'), -1, -1
6579 Xalpha_4, TRUE, FALSE,
6580 EL_CHAR('4'), -1, -1
6583 Xalpha_5, TRUE, FALSE,
6584 EL_CHAR('5'), -1, -1
6587 Xalpha_6, TRUE, FALSE,
6588 EL_CHAR('6'), -1, -1
6591 Xalpha_7, TRUE, FALSE,
6592 EL_CHAR('7'), -1, -1
6595 Xalpha_8, TRUE, FALSE,
6596 EL_CHAR('8'), -1, -1
6599 Xalpha_9, TRUE, FALSE,
6600 EL_CHAR('9'), -1, -1
6603 Xalpha_excla, TRUE, FALSE,
6604 EL_CHAR('!'), -1, -1
6607 Xalpha_quote, TRUE, FALSE,
6608 EL_CHAR('"'), -1, -1
6611 Xalpha_comma, TRUE, FALSE,
6612 EL_CHAR(','), -1, -1
6615 Xalpha_minus, TRUE, FALSE,
6616 EL_CHAR('-'), -1, -1
6619 Xalpha_perio, TRUE, FALSE,
6620 EL_CHAR('.'), -1, -1
6623 Xalpha_colon, TRUE, FALSE,
6624 EL_CHAR(':'), -1, -1
6627 Xalpha_quest, TRUE, FALSE,
6628 EL_CHAR('?'), -1, -1
6631 Xalpha_a, TRUE, FALSE,
6632 EL_CHAR('A'), -1, -1
6635 Xalpha_b, TRUE, FALSE,
6636 EL_CHAR('B'), -1, -1
6639 Xalpha_c, TRUE, FALSE,
6640 EL_CHAR('C'), -1, -1
6643 Xalpha_d, TRUE, FALSE,
6644 EL_CHAR('D'), -1, -1
6647 Xalpha_e, TRUE, FALSE,
6648 EL_CHAR('E'), -1, -1
6651 Xalpha_f, TRUE, FALSE,
6652 EL_CHAR('F'), -1, -1
6655 Xalpha_g, TRUE, FALSE,
6656 EL_CHAR('G'), -1, -1
6659 Xalpha_h, TRUE, FALSE,
6660 EL_CHAR('H'), -1, -1
6663 Xalpha_i, TRUE, FALSE,
6664 EL_CHAR('I'), -1, -1
6667 Xalpha_j, TRUE, FALSE,
6668 EL_CHAR('J'), -1, -1
6671 Xalpha_k, TRUE, FALSE,
6672 EL_CHAR('K'), -1, -1
6675 Xalpha_l, TRUE, FALSE,
6676 EL_CHAR('L'), -1, -1
6679 Xalpha_m, TRUE, FALSE,
6680 EL_CHAR('M'), -1, -1
6683 Xalpha_n, TRUE, FALSE,
6684 EL_CHAR('N'), -1, -1
6687 Xalpha_o, TRUE, FALSE,
6688 EL_CHAR('O'), -1, -1
6691 Xalpha_p, TRUE, FALSE,
6692 EL_CHAR('P'), -1, -1
6695 Xalpha_q, TRUE, FALSE,
6696 EL_CHAR('Q'), -1, -1
6699 Xalpha_r, TRUE, FALSE,
6700 EL_CHAR('R'), -1, -1
6703 Xalpha_s, TRUE, FALSE,
6704 EL_CHAR('S'), -1, -1
6707 Xalpha_t, TRUE, FALSE,
6708 EL_CHAR('T'), -1, -1
6711 Xalpha_u, TRUE, FALSE,
6712 EL_CHAR('U'), -1, -1
6715 Xalpha_v, TRUE, FALSE,
6716 EL_CHAR('V'), -1, -1
6719 Xalpha_w, TRUE, FALSE,
6720 EL_CHAR('W'), -1, -1
6723 Xalpha_x, TRUE, FALSE,
6724 EL_CHAR('X'), -1, -1
6727 Xalpha_y, TRUE, FALSE,
6728 EL_CHAR('Y'), -1, -1
6731 Xalpha_z, TRUE, FALSE,
6732 EL_CHAR('Z'), -1, -1
6735 Xalpha_arrow_e, TRUE, FALSE,
6736 EL_CHAR('>'), -1, -1
6739 Xalpha_arrow_w, TRUE, FALSE,
6740 EL_CHAR('<'), -1, -1
6743 Xalpha_copyr, TRUE, FALSE,
6744 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6748 Xboom_bug, FALSE, FALSE,
6749 EL_BUG, ACTION_EXPLODING, -1
6752 Xboom_bomb, FALSE, FALSE,
6753 EL_BOMB, ACTION_EXPLODING, -1
6756 Xboom_android, FALSE, FALSE,
6757 EL_EMC_ANDROID, ACTION_OTHER, -1
6760 Xboom_1, FALSE, FALSE,
6761 EL_DEFAULT, ACTION_EXPLODING, -1
6764 Xboom_2, FALSE, FALSE,
6765 EL_DEFAULT, ACTION_EXPLODING, -1
6768 Znormal, FALSE, FALSE,
6772 Zdynamite, FALSE, FALSE,
6776 Zplayer, FALSE, FALSE,
6780 ZBORDER, FALSE, FALSE,
6790 static struct Mapping_EM_to_RND_player
6799 em_player_mapping_list[] =
6803 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6807 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6811 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6815 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6819 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6823 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6827 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6831 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6835 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6839 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6843 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6847 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6851 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6855 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6859 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6863 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6867 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6871 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6875 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6879 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6883 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6887 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6891 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6895 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6899 EL_PLAYER_1, ACTION_DEFAULT, -1,
6903 EL_PLAYER_2, ACTION_DEFAULT, -1,
6907 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6911 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6915 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6919 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6923 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6927 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6931 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6935 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6939 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6943 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6947 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6951 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6955 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6959 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6963 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6967 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6971 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6975 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6979 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6983 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6987 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6991 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6995 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6999 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
7003 EL_PLAYER_3, ACTION_DEFAULT, -1,
7007 EL_PLAYER_4, ACTION_DEFAULT, -1,
7016 int map_element_RND_to_EM(int element_rnd)
7018 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7019 static boolean mapping_initialized = FALSE;
7021 if (!mapping_initialized)
7025 /* return "Xalpha_quest" for all undefined elements in mapping array */
7026 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7027 mapping_RND_to_EM[i] = Xalpha_quest;
7029 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7030 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7031 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7032 em_object_mapping_list[i].element_em;
7034 mapping_initialized = TRUE;
7037 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7038 return mapping_RND_to_EM[element_rnd];
7040 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7045 int map_element_EM_to_RND(int element_em)
7047 static unsigned short mapping_EM_to_RND[TILE_MAX];
7048 static boolean mapping_initialized = FALSE;
7050 if (!mapping_initialized)
7054 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
7055 for (i = 0; i < TILE_MAX; i++)
7056 mapping_EM_to_RND[i] = EL_UNKNOWN;
7058 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7059 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7060 em_object_mapping_list[i].element_rnd;
7062 mapping_initialized = TRUE;
7065 if (element_em >= 0 && element_em < TILE_MAX)
7066 return mapping_EM_to_RND[element_em];
7068 Error(ERR_WARN, "invalid EM level element %d", element_em);
7073 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7075 struct LevelInfo_EM *level_em = level->native_em_level;
7076 struct LEVEL *lev = level_em->lev;
7079 for (i = 0; i < TILE_MAX; i++)
7080 lev->android_array[i] = Xblank;
7082 for (i = 0; i < level->num_android_clone_elements; i++)
7084 int element_rnd = level->android_clone_element[i];
7085 int element_em = map_element_RND_to_EM(element_rnd);
7087 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7088 if (em_object_mapping_list[j].element_rnd == element_rnd)
7089 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7093 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7095 struct LevelInfo_EM *level_em = level->native_em_level;
7096 struct LEVEL *lev = level_em->lev;
7099 level->num_android_clone_elements = 0;
7101 for (i = 0; i < TILE_MAX; i++)
7103 int element_em = lev->android_array[i];
7105 boolean element_found = FALSE;
7107 if (element_em == Xblank)
7110 element_rnd = map_element_EM_to_RND(element_em);
7112 for (j = 0; j < level->num_android_clone_elements; j++)
7113 if (level->android_clone_element[j] == element_rnd)
7114 element_found = TRUE;
7118 level->android_clone_element[level->num_android_clone_elements++] =
7121 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7126 if (level->num_android_clone_elements == 0)
7128 level->num_android_clone_elements = 1;
7129 level->android_clone_element[0] = EL_EMPTY;
7133 int map_direction_RND_to_EM(int direction)
7135 return (direction == MV_UP ? 0 :
7136 direction == MV_RIGHT ? 1 :
7137 direction == MV_DOWN ? 2 :
7138 direction == MV_LEFT ? 3 :
7142 int map_direction_EM_to_RND(int direction)
7144 return (direction == 0 ? MV_UP :
7145 direction == 1 ? MV_RIGHT :
7146 direction == 2 ? MV_DOWN :
7147 direction == 3 ? MV_LEFT :
7151 int map_element_RND_to_SP(int element_rnd)
7153 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
7155 if (element_rnd >= EL_SP_START &&
7156 element_rnd <= EL_SP_END)
7157 element_sp = element_rnd - EL_SP_START;
7158 else if (element_rnd == EL_EMPTY_SPACE)
7160 else if (element_rnd == EL_INVISIBLE_WALL)
7166 int map_element_SP_to_RND(int element_sp)
7168 int element_rnd = EL_UNKNOWN;
7170 if (element_sp >= 0x00 &&
7172 element_rnd = EL_SP_START + element_sp;
7173 else if (element_sp == 0x28)
7174 element_rnd = EL_INVISIBLE_WALL;
7179 int map_action_SP_to_RND(int action_sp)
7183 case actActive: return ACTION_ACTIVE;
7184 case actImpact: return ACTION_IMPACT;
7185 case actExploding: return ACTION_EXPLODING;
7186 case actDigging: return ACTION_DIGGING;
7187 case actSnapping: return ACTION_SNAPPING;
7188 case actCollecting: return ACTION_COLLECTING;
7189 case actPassing: return ACTION_PASSING;
7190 case actPushing: return ACTION_PUSHING;
7191 case actDropping: return ACTION_DROPPING;
7193 default: return ACTION_DEFAULT;
7197 int get_next_element(int element)
7201 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7202 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7203 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7204 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7205 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7206 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7207 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7208 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7209 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7210 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7211 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7213 default: return element;
7217 int el_act_dir2img(int element, int action, int direction)
7219 element = GFX_ELEMENT(element);
7220 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7222 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7223 return element_info[element].direction_graphic[action][direction];
7226 static int el_act_dir2crm(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_crumbled[action][direction];
7235 int el_act2img(int element, int action)
7237 element = GFX_ELEMENT(element);
7239 return element_info[element].graphic[action];
7242 int el_act2crm(int element, int action)
7244 element = GFX_ELEMENT(element);
7246 return element_info[element].crumbled[action];
7249 int el_dir2img(int element, int direction)
7251 element = GFX_ELEMENT(element);
7253 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7256 int el2baseimg(int element)
7258 return element_info[element].graphic[ACTION_DEFAULT];
7261 int el2img(int element)
7263 element = GFX_ELEMENT(element);
7265 return element_info[element].graphic[ACTION_DEFAULT];
7268 int el2edimg(int element)
7270 element = GFX_ELEMENT(element);
7272 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7275 int el2preimg(int element)
7277 element = GFX_ELEMENT(element);
7279 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7282 int el2panelimg(int element)
7284 element = GFX_ELEMENT(element);
7286 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7289 int font2baseimg(int font_nr)
7291 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7294 int getBeltNrFromBeltElement(int element)
7296 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7297 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7298 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
7301 int getBeltNrFromBeltActiveElement(int element)
7303 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
7304 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
7305 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
7308 int getBeltNrFromBeltSwitchElement(int element)
7310 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
7311 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
7312 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
7315 int getBeltDirNrFromBeltElement(int element)
7317 static int belt_base_element[4] =
7319 EL_CONVEYOR_BELT_1_LEFT,
7320 EL_CONVEYOR_BELT_2_LEFT,
7321 EL_CONVEYOR_BELT_3_LEFT,
7322 EL_CONVEYOR_BELT_4_LEFT
7325 int belt_nr = getBeltNrFromBeltElement(element);
7326 int belt_dir_nr = element - belt_base_element[belt_nr];
7328 return (belt_dir_nr % 3);
7331 int getBeltDirNrFromBeltSwitchElement(int element)
7333 static int belt_base_element[4] =
7335 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7336 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7337 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7338 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7341 int belt_nr = getBeltNrFromBeltSwitchElement(element);
7342 int belt_dir_nr = element - belt_base_element[belt_nr];
7344 return (belt_dir_nr % 3);
7347 int getBeltDirFromBeltElement(int element)
7349 static int belt_move_dir[3] =
7356 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7358 return belt_move_dir[belt_dir_nr];
7361 int getBeltDirFromBeltSwitchElement(int element)
7363 static int belt_move_dir[3] =
7370 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7372 return belt_move_dir[belt_dir_nr];
7375 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7377 static int belt_base_element[4] =
7379 EL_CONVEYOR_BELT_1_LEFT,
7380 EL_CONVEYOR_BELT_2_LEFT,
7381 EL_CONVEYOR_BELT_3_LEFT,
7382 EL_CONVEYOR_BELT_4_LEFT
7385 return belt_base_element[belt_nr] + belt_dir_nr;
7388 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7390 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7392 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7395 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7397 static int belt_base_element[4] =
7399 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7400 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7401 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7402 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7405 return belt_base_element[belt_nr] + belt_dir_nr;
7408 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7410 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7412 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7415 boolean getTeamMode_EM()
7417 return game.team_mode;
7420 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7422 int game_frame_delay_value;
7424 game_frame_delay_value =
7425 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7426 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7429 if (tape.playing && tape.warp_forward && !tape.pausing)
7430 game_frame_delay_value = 0;
7432 return game_frame_delay_value;
7435 unsigned int InitRND(int seed)
7437 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7438 return InitEngineRandom_EM(seed);
7439 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7440 return InitEngineRandom_SP(seed);
7442 return InitEngineRandom_RND(seed);
7445 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7446 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7448 inline static int get_effective_element_EM(int tile, int frame_em)
7450 int element = object_mapping[tile].element_rnd;
7451 int action = object_mapping[tile].action;
7452 boolean is_backside = object_mapping[tile].is_backside;
7453 boolean action_removing = (action == ACTION_DIGGING ||
7454 action == ACTION_SNAPPING ||
7455 action == ACTION_COLLECTING);
7461 case Yacid_splash_eB:
7462 case Yacid_splash_wB:
7463 return (frame_em > 5 ? EL_EMPTY : element);
7469 else /* frame_em == 7 */
7473 case Yacid_splash_eB:
7474 case Yacid_splash_wB:
7477 case Yemerald_stone:
7480 case Ydiamond_stone:
7484 case Xdrip_stretchB:
7503 case Xsand_stonein_1:
7504 case Xsand_stonein_2:
7505 case Xsand_stonein_3:
7506 case Xsand_stonein_4:
7510 return (is_backside || action_removing ? EL_EMPTY : element);
7515 inline static boolean check_linear_animation_EM(int tile)
7519 case Xsand_stonesand_1:
7520 case Xsand_stonesand_quickout_1:
7521 case Xsand_sandstone_1:
7522 case Xsand_stonein_1:
7523 case Xsand_stoneout_1:
7542 case Yacid_splash_eB:
7543 case Yacid_splash_wB:
7544 case Yemerald_stone:
7551 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7552 boolean has_crumbled_graphics,
7553 int crumbled, int sync_frame)
7555 /* if element can be crumbled, but certain action graphics are just empty
7556 space (like instantly snapping sand to empty space in 1 frame), do not
7557 treat these empty space graphics as crumbled graphics in EMC engine */
7558 if (crumbled == IMG_EMPTY_SPACE)
7559 has_crumbled_graphics = FALSE;
7561 if (has_crumbled_graphics)
7563 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7564 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7565 g_crumbled->anim_delay,
7566 g_crumbled->anim_mode,
7567 g_crumbled->anim_start_frame,
7570 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7571 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7573 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7574 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
7576 g_em->has_crumbled_graphics = TRUE;
7580 g_em->crumbled_bitmap = NULL;
7581 g_em->crumbled_src_x = 0;
7582 g_em->crumbled_src_y = 0;
7583 g_em->crumbled_border_size = 0;
7584 g_em->crumbled_tile_size = 0;
7586 g_em->has_crumbled_graphics = FALSE;
7590 void ResetGfxAnimation_EM(int x, int y, int tile)
7595 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7596 int tile, int frame_em, int x, int y)
7598 int action = object_mapping[tile].action;
7599 int direction = object_mapping[tile].direction;
7600 int effective_element = get_effective_element_EM(tile, frame_em);
7601 int graphic = (direction == MV_NONE ?
7602 el_act2img(effective_element, action) :
7603 el_act_dir2img(effective_element, action, direction));
7604 struct GraphicInfo *g = &graphic_info[graphic];
7606 boolean action_removing = (action == ACTION_DIGGING ||
7607 action == ACTION_SNAPPING ||
7608 action == ACTION_COLLECTING);
7609 boolean action_moving = (action == ACTION_FALLING ||
7610 action == ACTION_MOVING ||
7611 action == ACTION_PUSHING ||
7612 action == ACTION_EATING ||
7613 action == ACTION_FILLING ||
7614 action == ACTION_EMPTYING);
7615 boolean action_falling = (action == ACTION_FALLING ||
7616 action == ACTION_FILLING ||
7617 action == ACTION_EMPTYING);
7619 /* special case: graphic uses "2nd movement tile" and has defined
7620 7 frames for movement animation (or less) => use default graphic
7621 for last (8th) frame which ends the movement animation */
7622 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7624 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7625 graphic = (direction == MV_NONE ?
7626 el_act2img(effective_element, action) :
7627 el_act_dir2img(effective_element, action, direction));
7629 g = &graphic_info[graphic];
7632 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7636 else if (action_moving)
7638 boolean is_backside = object_mapping[tile].is_backside;
7642 int direction = object_mapping[tile].direction;
7643 int move_dir = (action_falling ? MV_DOWN : direction);
7648 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7649 if (g->double_movement && frame_em == 0)
7653 if (move_dir == MV_LEFT)
7654 GfxFrame[x - 1][y] = GfxFrame[x][y];
7655 else if (move_dir == MV_RIGHT)
7656 GfxFrame[x + 1][y] = GfxFrame[x][y];
7657 else if (move_dir == MV_UP)
7658 GfxFrame[x][y - 1] = GfxFrame[x][y];
7659 else if (move_dir == MV_DOWN)
7660 GfxFrame[x][y + 1] = GfxFrame[x][y];
7667 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7668 if (tile == Xsand_stonesand_quickout_1 ||
7669 tile == Xsand_stonesand_quickout_2)
7673 if (graphic_info[graphic].anim_global_sync)
7674 sync_frame = FrameCounter;
7675 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7676 sync_frame = GfxFrame[x][y];
7678 sync_frame = 0; /* playfield border (pseudo steel) */
7680 SetRandomAnimationValue(x, y);
7682 int frame = getAnimationFrame(g->anim_frames,
7685 g->anim_start_frame,
7688 g_em->unique_identifier =
7689 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7692 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7693 int tile, int frame_em, int x, int y)
7695 int action = object_mapping[tile].action;
7696 int direction = object_mapping[tile].direction;
7697 boolean is_backside = object_mapping[tile].is_backside;
7698 int effective_element = get_effective_element_EM(tile, frame_em);
7699 int effective_action = action;
7700 int graphic = (direction == MV_NONE ?
7701 el_act2img(effective_element, effective_action) :
7702 el_act_dir2img(effective_element, effective_action,
7704 int crumbled = (direction == MV_NONE ?
7705 el_act2crm(effective_element, effective_action) :
7706 el_act_dir2crm(effective_element, effective_action,
7708 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7709 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7710 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7711 struct GraphicInfo *g = &graphic_info[graphic];
7714 /* special case: graphic uses "2nd movement tile" and has defined
7715 7 frames for movement animation (or less) => use default graphic
7716 for last (8th) frame which ends the movement animation */
7717 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7719 effective_action = ACTION_DEFAULT;
7720 graphic = (direction == MV_NONE ?
7721 el_act2img(effective_element, effective_action) :
7722 el_act_dir2img(effective_element, effective_action,
7724 crumbled = (direction == MV_NONE ?
7725 el_act2crm(effective_element, effective_action) :
7726 el_act_dir2crm(effective_element, effective_action,
7729 g = &graphic_info[graphic];
7732 if (graphic_info[graphic].anim_global_sync)
7733 sync_frame = FrameCounter;
7734 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7735 sync_frame = GfxFrame[x][y];
7737 sync_frame = 0; /* playfield border (pseudo steel) */
7739 SetRandomAnimationValue(x, y);
7741 int frame = getAnimationFrame(g->anim_frames,
7744 g->anim_start_frame,
7747 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7748 g->double_movement && is_backside);
7750 /* (updating the "crumbled" graphic definitions is probably not really needed,
7751 as animations for crumbled graphics can't be longer than one EMC cycle) */
7752 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7756 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7757 int player_nr, int anim, int frame_em)
7759 int element = player_mapping[player_nr][anim].element_rnd;
7760 int action = player_mapping[player_nr][anim].action;
7761 int direction = player_mapping[player_nr][anim].direction;
7762 int graphic = (direction == MV_NONE ?
7763 el_act2img(element, action) :
7764 el_act_dir2img(element, action, direction));
7765 struct GraphicInfo *g = &graphic_info[graphic];
7768 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7770 stored_player[player_nr].StepFrame = frame_em;
7772 sync_frame = stored_player[player_nr].Frame;
7774 int frame = getAnimationFrame(g->anim_frames,
7777 g->anim_start_frame,
7780 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7781 &g_em->src_x, &g_em->src_y, FALSE);
7784 void InitGraphicInfo_EM(void)
7789 int num_em_gfx_errors = 0;
7791 if (graphic_info_em_object[0][0].bitmap == NULL)
7793 /* EM graphics not yet initialized in em_open_all() */
7798 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7801 /* always start with reliable default values */
7802 for (i = 0; i < TILE_MAX; i++)
7804 object_mapping[i].element_rnd = EL_UNKNOWN;
7805 object_mapping[i].is_backside = FALSE;
7806 object_mapping[i].action = ACTION_DEFAULT;
7807 object_mapping[i].direction = MV_NONE;
7810 /* always start with reliable default values */
7811 for (p = 0; p < MAX_PLAYERS; p++)
7813 for (i = 0; i < SPR_MAX; i++)
7815 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7816 player_mapping[p][i].action = ACTION_DEFAULT;
7817 player_mapping[p][i].direction = MV_NONE;
7821 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7823 int e = em_object_mapping_list[i].element_em;
7825 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7826 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7828 if (em_object_mapping_list[i].action != -1)
7829 object_mapping[e].action = em_object_mapping_list[i].action;
7831 if (em_object_mapping_list[i].direction != -1)
7832 object_mapping[e].direction =
7833 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7836 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7838 int a = em_player_mapping_list[i].action_em;
7839 int p = em_player_mapping_list[i].player_nr;
7841 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7843 if (em_player_mapping_list[i].action != -1)
7844 player_mapping[p][a].action = em_player_mapping_list[i].action;
7846 if (em_player_mapping_list[i].direction != -1)
7847 player_mapping[p][a].direction =
7848 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7851 for (i = 0; i < TILE_MAX; i++)
7853 int element = object_mapping[i].element_rnd;
7854 int action = object_mapping[i].action;
7855 int direction = object_mapping[i].direction;
7856 boolean is_backside = object_mapping[i].is_backside;
7857 boolean action_exploding = ((action == ACTION_EXPLODING ||
7858 action == ACTION_SMASHED_BY_ROCK ||
7859 action == ACTION_SMASHED_BY_SPRING) &&
7860 element != EL_DIAMOND);
7861 boolean action_active = (action == ACTION_ACTIVE);
7862 boolean action_other = (action == ACTION_OTHER);
7864 for (j = 0; j < 8; j++)
7866 int effective_element = get_effective_element_EM(i, j);
7867 int effective_action = (j < 7 ? action :
7868 i == Xdrip_stretch ? action :
7869 i == Xdrip_stretchB ? action :
7870 i == Ydrip_s1 ? action :
7871 i == Ydrip_s1B ? action :
7872 i == Xball_1B ? action :
7873 i == Xball_2 ? action :
7874 i == Xball_2B ? action :
7875 i == Yball_eat ? action :
7876 i == Ykey_1_eat ? action :
7877 i == Ykey_2_eat ? action :
7878 i == Ykey_3_eat ? action :
7879 i == Ykey_4_eat ? action :
7880 i == Ykey_5_eat ? action :
7881 i == Ykey_6_eat ? action :
7882 i == Ykey_7_eat ? action :
7883 i == Ykey_8_eat ? action :
7884 i == Ylenses_eat ? action :
7885 i == Ymagnify_eat ? action :
7886 i == Ygrass_eat ? action :
7887 i == Ydirt_eat ? action :
7888 i == Xsand_stonein_1 ? action :
7889 i == Xsand_stonein_2 ? action :
7890 i == Xsand_stonein_3 ? action :
7891 i == Xsand_stonein_4 ? action :
7892 i == Xsand_stoneout_1 ? action :
7893 i == Xsand_stoneout_2 ? action :
7894 i == Xboom_android ? ACTION_EXPLODING :
7895 action_exploding ? ACTION_EXPLODING :
7896 action_active ? action :
7897 action_other ? action :
7899 int graphic = (el_act_dir2img(effective_element, effective_action,
7901 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7903 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7904 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7905 boolean has_action_graphics = (graphic != base_graphic);
7906 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7907 struct GraphicInfo *g = &graphic_info[graphic];
7908 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7911 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7912 boolean special_animation = (action != ACTION_DEFAULT &&
7913 g->anim_frames == 3 &&
7914 g->anim_delay == 2 &&
7915 g->anim_mode & ANIM_LINEAR);
7916 int sync_frame = (i == Xdrip_stretch ? 7 :
7917 i == Xdrip_stretchB ? 7 :
7918 i == Ydrip_s2 ? j + 8 :
7919 i == Ydrip_s2B ? j + 8 :
7928 i == Xfake_acid_1 ? 0 :
7929 i == Xfake_acid_2 ? 10 :
7930 i == Xfake_acid_3 ? 20 :
7931 i == Xfake_acid_4 ? 30 :
7932 i == Xfake_acid_5 ? 40 :
7933 i == Xfake_acid_6 ? 50 :
7934 i == Xfake_acid_7 ? 60 :
7935 i == Xfake_acid_8 ? 70 :
7937 i == Xball_2B ? j + 8 :
7938 i == Yball_eat ? j + 1 :
7939 i == Ykey_1_eat ? j + 1 :
7940 i == Ykey_2_eat ? j + 1 :
7941 i == Ykey_3_eat ? j + 1 :
7942 i == Ykey_4_eat ? j + 1 :
7943 i == Ykey_5_eat ? j + 1 :
7944 i == Ykey_6_eat ? j + 1 :
7945 i == Ykey_7_eat ? j + 1 :
7946 i == Ykey_8_eat ? j + 1 :
7947 i == Ylenses_eat ? j + 1 :
7948 i == Ymagnify_eat ? j + 1 :
7949 i == Ygrass_eat ? j + 1 :
7950 i == Ydirt_eat ? j + 1 :
7951 i == Xamoeba_1 ? 0 :
7952 i == Xamoeba_2 ? 1 :
7953 i == Xamoeba_3 ? 2 :
7954 i == Xamoeba_4 ? 3 :
7955 i == Xamoeba_5 ? 0 :
7956 i == Xamoeba_6 ? 1 :
7957 i == Xamoeba_7 ? 2 :
7958 i == Xamoeba_8 ? 3 :
7959 i == Xexit_2 ? j + 8 :
7960 i == Xexit_3 ? j + 16 :
7961 i == Xdynamite_1 ? 0 :
7962 i == Xdynamite_2 ? 8 :
7963 i == Xdynamite_3 ? 16 :
7964 i == Xdynamite_4 ? 24 :
7965 i == Xsand_stonein_1 ? j + 1 :
7966 i == Xsand_stonein_2 ? j + 9 :
7967 i == Xsand_stonein_3 ? j + 17 :
7968 i == Xsand_stonein_4 ? j + 25 :
7969 i == Xsand_stoneout_1 && j == 0 ? 0 :
7970 i == Xsand_stoneout_1 && j == 1 ? 0 :
7971 i == Xsand_stoneout_1 && j == 2 ? 1 :
7972 i == Xsand_stoneout_1 && j == 3 ? 2 :
7973 i == Xsand_stoneout_1 && j == 4 ? 2 :
7974 i == Xsand_stoneout_1 && j == 5 ? 3 :
7975 i == Xsand_stoneout_1 && j == 6 ? 4 :
7976 i == Xsand_stoneout_1 && j == 7 ? 4 :
7977 i == Xsand_stoneout_2 && j == 0 ? 5 :
7978 i == Xsand_stoneout_2 && j == 1 ? 6 :
7979 i == Xsand_stoneout_2 && j == 2 ? 7 :
7980 i == Xsand_stoneout_2 && j == 3 ? 8 :
7981 i == Xsand_stoneout_2 && j == 4 ? 9 :
7982 i == Xsand_stoneout_2 && j == 5 ? 11 :
7983 i == Xsand_stoneout_2 && j == 6 ? 13 :
7984 i == Xsand_stoneout_2 && j == 7 ? 15 :
7985 i == Xboom_bug && j == 1 ? 2 :
7986 i == Xboom_bug && j == 2 ? 2 :
7987 i == Xboom_bug && j == 3 ? 4 :
7988 i == Xboom_bug && j == 4 ? 4 :
7989 i == Xboom_bug && j == 5 ? 2 :
7990 i == Xboom_bug && j == 6 ? 2 :
7991 i == Xboom_bug && j == 7 ? 0 :
7992 i == Xboom_bomb && j == 1 ? 2 :
7993 i == Xboom_bomb && j == 2 ? 2 :
7994 i == Xboom_bomb && j == 3 ? 4 :
7995 i == Xboom_bomb && j == 4 ? 4 :
7996 i == Xboom_bomb && j == 5 ? 2 :
7997 i == Xboom_bomb && j == 6 ? 2 :
7998 i == Xboom_bomb && j == 7 ? 0 :
7999 i == Xboom_android && j == 7 ? 6 :
8000 i == Xboom_1 && j == 1 ? 2 :
8001 i == Xboom_1 && j == 2 ? 2 :
8002 i == Xboom_1 && j == 3 ? 4 :
8003 i == Xboom_1 && j == 4 ? 4 :
8004 i == Xboom_1 && j == 5 ? 6 :
8005 i == Xboom_1 && j == 6 ? 6 :
8006 i == Xboom_1 && j == 7 ? 8 :
8007 i == Xboom_2 && j == 0 ? 8 :
8008 i == Xboom_2 && j == 1 ? 8 :
8009 i == Xboom_2 && j == 2 ? 10 :
8010 i == Xboom_2 && j == 3 ? 10 :
8011 i == Xboom_2 && j == 4 ? 10 :
8012 i == Xboom_2 && j == 5 ? 12 :
8013 i == Xboom_2 && j == 6 ? 12 :
8014 i == Xboom_2 && j == 7 ? 12 :
8015 special_animation && j == 4 ? 3 :
8016 effective_action != action ? 0 :
8020 Bitmap *debug_bitmap = g_em->bitmap;
8021 int debug_src_x = g_em->src_x;
8022 int debug_src_y = g_em->src_y;
8025 int frame = getAnimationFrame(g->anim_frames,
8028 g->anim_start_frame,
8031 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8032 g->double_movement && is_backside);
8034 g_em->bitmap = src_bitmap;
8035 g_em->src_x = src_x;
8036 g_em->src_y = src_y;
8037 g_em->src_offset_x = 0;
8038 g_em->src_offset_y = 0;
8039 g_em->dst_offset_x = 0;
8040 g_em->dst_offset_y = 0;
8041 g_em->width = TILEX;
8042 g_em->height = TILEY;
8044 g_em->preserve_background = FALSE;
8046 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8049 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8050 effective_action == ACTION_MOVING ||
8051 effective_action == ACTION_PUSHING ||
8052 effective_action == ACTION_EATING)) ||
8053 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8054 effective_action == ACTION_EMPTYING)))
8057 (effective_action == ACTION_FALLING ||
8058 effective_action == ACTION_FILLING ||
8059 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8060 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8061 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8062 int num_steps = (i == Ydrip_s1 ? 16 :
8063 i == Ydrip_s1B ? 16 :
8064 i == Ydrip_s2 ? 16 :
8065 i == Ydrip_s2B ? 16 :
8066 i == Xsand_stonein_1 ? 32 :
8067 i == Xsand_stonein_2 ? 32 :
8068 i == Xsand_stonein_3 ? 32 :
8069 i == Xsand_stonein_4 ? 32 :
8070 i == Xsand_stoneout_1 ? 16 :
8071 i == Xsand_stoneout_2 ? 16 : 8);
8072 int cx = ABS(dx) * (TILEX / num_steps);
8073 int cy = ABS(dy) * (TILEY / num_steps);
8074 int step_frame = (i == Ydrip_s2 ? j + 8 :
8075 i == Ydrip_s2B ? j + 8 :
8076 i == Xsand_stonein_2 ? j + 8 :
8077 i == Xsand_stonein_3 ? j + 16 :
8078 i == Xsand_stonein_4 ? j + 24 :
8079 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8080 int step = (is_backside ? step_frame : num_steps - step_frame);
8082 if (is_backside) /* tile where movement starts */
8084 if (dx < 0 || dy < 0)
8086 g_em->src_offset_x = cx * step;
8087 g_em->src_offset_y = cy * step;
8091 g_em->dst_offset_x = cx * step;
8092 g_em->dst_offset_y = cy * step;
8095 else /* tile where movement ends */
8097 if (dx < 0 || dy < 0)
8099 g_em->dst_offset_x = cx * step;
8100 g_em->dst_offset_y = cy * step;
8104 g_em->src_offset_x = cx * step;
8105 g_em->src_offset_y = cy * step;
8109 g_em->width = TILEX - cx * step;
8110 g_em->height = TILEY - cy * step;
8113 /* create unique graphic identifier to decide if tile must be redrawn */
8114 /* bit 31 - 16 (16 bit): EM style graphic
8115 bit 15 - 12 ( 4 bit): EM style frame
8116 bit 11 - 6 ( 6 bit): graphic width
8117 bit 5 - 0 ( 6 bit): graphic height */
8118 g_em->unique_identifier =
8119 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8123 /* skip check for EMC elements not contained in original EMC artwork */
8124 if (element == EL_EMC_FAKE_ACID)
8127 if (g_em->bitmap != debug_bitmap ||
8128 g_em->src_x != debug_src_x ||
8129 g_em->src_y != debug_src_y ||
8130 g_em->src_offset_x != 0 ||
8131 g_em->src_offset_y != 0 ||
8132 g_em->dst_offset_x != 0 ||
8133 g_em->dst_offset_y != 0 ||
8134 g_em->width != TILEX ||
8135 g_em->height != TILEY)
8137 static int last_i = -1;
8145 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8146 i, element, element_info[element].token_name,
8147 element_action_info[effective_action].suffix, direction);
8149 if (element != effective_element)
8150 printf(" [%d ('%s')]",
8152 element_info[effective_element].token_name);
8156 if (g_em->bitmap != debug_bitmap)
8157 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8158 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8160 if (g_em->src_x != debug_src_x ||
8161 g_em->src_y != debug_src_y)
8162 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8163 j, (is_backside ? 'B' : 'F'),
8164 g_em->src_x, g_em->src_y,
8165 g_em->src_x / 32, g_em->src_y / 32,
8166 debug_src_x, debug_src_y,
8167 debug_src_x / 32, debug_src_y / 32);
8169 if (g_em->src_offset_x != 0 ||
8170 g_em->src_offset_y != 0 ||
8171 g_em->dst_offset_x != 0 ||
8172 g_em->dst_offset_y != 0)
8173 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8175 g_em->src_offset_x, g_em->src_offset_y,
8176 g_em->dst_offset_x, g_em->dst_offset_y);
8178 if (g_em->width != TILEX ||
8179 g_em->height != TILEY)
8180 printf(" %d (%d): size %d,%d should be %d,%d\n",
8182 g_em->width, g_em->height, TILEX, TILEY);
8184 num_em_gfx_errors++;
8191 for (i = 0; i < TILE_MAX; i++)
8193 for (j = 0; j < 8; j++)
8195 int element = object_mapping[i].element_rnd;
8196 int action = object_mapping[i].action;
8197 int direction = object_mapping[i].direction;
8198 boolean is_backside = object_mapping[i].is_backside;
8199 int graphic_action = el_act_dir2img(element, action, direction);
8200 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8202 if ((action == ACTION_SMASHED_BY_ROCK ||
8203 action == ACTION_SMASHED_BY_SPRING ||
8204 action == ACTION_EATING) &&
8205 graphic_action == graphic_default)
8207 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8208 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8209 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8210 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8213 /* no separate animation for "smashed by rock" -- use rock instead */
8214 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8215 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8217 g_em->bitmap = g_xx->bitmap;
8218 g_em->src_x = g_xx->src_x;
8219 g_em->src_y = g_xx->src_y;
8220 g_em->src_offset_x = g_xx->src_offset_x;
8221 g_em->src_offset_y = g_xx->src_offset_y;
8222 g_em->dst_offset_x = g_xx->dst_offset_x;
8223 g_em->dst_offset_y = g_xx->dst_offset_y;
8224 g_em->width = g_xx->width;
8225 g_em->height = g_xx->height;
8226 g_em->unique_identifier = g_xx->unique_identifier;
8229 g_em->preserve_background = TRUE;
8234 for (p = 0; p < MAX_PLAYERS; p++)
8236 for (i = 0; i < SPR_MAX; i++)
8238 int element = player_mapping[p][i].element_rnd;
8239 int action = player_mapping[p][i].action;
8240 int direction = player_mapping[p][i].direction;
8242 for (j = 0; j < 8; j++)
8244 int effective_element = element;
8245 int effective_action = action;
8246 int graphic = (direction == MV_NONE ?
8247 el_act2img(effective_element, effective_action) :
8248 el_act_dir2img(effective_element, effective_action,
8250 struct GraphicInfo *g = &graphic_info[graphic];
8251 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8257 Bitmap *debug_bitmap = g_em->bitmap;
8258 int debug_src_x = g_em->src_x;
8259 int debug_src_y = g_em->src_y;
8262 int frame = getAnimationFrame(g->anim_frames,
8265 g->anim_start_frame,
8268 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8270 g_em->bitmap = src_bitmap;
8271 g_em->src_x = src_x;
8272 g_em->src_y = src_y;
8273 g_em->src_offset_x = 0;
8274 g_em->src_offset_y = 0;
8275 g_em->dst_offset_x = 0;
8276 g_em->dst_offset_y = 0;
8277 g_em->width = TILEX;
8278 g_em->height = TILEY;
8282 /* skip check for EMC elements not contained in original EMC artwork */
8283 if (element == EL_PLAYER_3 ||
8284 element == EL_PLAYER_4)
8287 if (g_em->bitmap != debug_bitmap ||
8288 g_em->src_x != debug_src_x ||
8289 g_em->src_y != debug_src_y)
8291 static int last_i = -1;
8299 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
8300 p, i, element, element_info[element].token_name,
8301 element_action_info[effective_action].suffix, direction);
8303 if (element != effective_element)
8304 printf(" [%d ('%s')]",
8306 element_info[effective_element].token_name);
8310 if (g_em->bitmap != debug_bitmap)
8311 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
8312 j, (int)(g_em->bitmap), (int)(debug_bitmap));
8314 if (g_em->src_x != debug_src_x ||
8315 g_em->src_y != debug_src_y)
8316 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8318 g_em->src_x, g_em->src_y,
8319 g_em->src_x / 32, g_em->src_y / 32,
8320 debug_src_x, debug_src_y,
8321 debug_src_x / 32, debug_src_y / 32);
8323 num_em_gfx_errors++;
8333 printf("::: [%d errors found]\n", num_em_gfx_errors);
8339 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
8340 boolean any_player_moving,
8341 boolean any_player_snapping,
8342 boolean any_player_dropping)
8344 if (frame == 0 && !any_player_dropping)
8346 if (!local_player->was_waiting)
8348 if (!CheckSaveEngineSnapshotToList())
8351 local_player->was_waiting = TRUE;
8354 else if (any_player_moving || any_player_snapping || any_player_dropping)
8356 local_player->was_waiting = FALSE;
8360 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8361 boolean murphy_is_dropping)
8363 if (murphy_is_waiting)
8365 if (!local_player->was_waiting)
8367 if (!CheckSaveEngineSnapshotToList())
8370 local_player->was_waiting = TRUE;
8375 local_player->was_waiting = FALSE;
8379 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8380 boolean any_player_moving,
8381 boolean any_player_snapping,
8382 boolean any_player_dropping)
8384 if (tape.single_step && tape.recording && !tape.pausing)
8385 if (frame == 0 && !any_player_dropping)
8386 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8388 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8389 any_player_snapping, any_player_dropping);
8392 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8393 boolean murphy_is_dropping)
8395 boolean murphy_starts_dropping = FALSE;
8398 for (i = 0; i < MAX_PLAYERS; i++)
8399 if (stored_player[i].force_dropping)
8400 murphy_starts_dropping = TRUE;
8402 if (tape.single_step && tape.recording && !tape.pausing)
8403 if (murphy_is_waiting && !murphy_starts_dropping)
8404 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8406 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8409 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8410 int graphic, int sync_frame, int x, int y)
8412 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8414 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8417 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8419 return (IS_NEXT_FRAME(sync_frame, graphic));
8422 int getGraphicInfo_Delay(int graphic)
8424 return graphic_info[graphic].anim_delay;
8427 void PlayMenuSoundExt(int sound)
8429 if (sound == SND_UNDEFINED)
8432 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8433 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8436 if (IS_LOOP_SOUND(sound))
8437 PlaySoundLoop(sound);
8442 void PlayMenuSound()
8444 PlayMenuSoundExt(menu.sound[game_status]);
8447 void PlayMenuSoundStereo(int sound, int stereo_position)
8449 if (sound == SND_UNDEFINED)
8452 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8453 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8456 if (IS_LOOP_SOUND(sound))
8457 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8459 PlaySoundStereo(sound, stereo_position);
8462 void PlayMenuSoundIfLoopExt(int sound)
8464 if (sound == SND_UNDEFINED)
8467 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8468 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8471 if (IS_LOOP_SOUND(sound))
8472 PlaySoundLoop(sound);
8475 void PlayMenuSoundIfLoop()
8477 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8480 void PlayMenuMusicExt(int music)
8482 if (music == MUS_UNDEFINED)
8485 if (!setup.sound_music)
8491 void PlayMenuMusic()
8493 char *curr_music = getCurrentlyPlayingMusicFilename();
8494 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8496 if (!strEqual(curr_music, next_music))
8497 PlayMenuMusicExt(menu.music[game_status]);
8500 void PlayMenuSoundsAndMusic()
8506 static void FadeMenuSounds()
8511 static void FadeMenuMusic()
8513 char *curr_music = getCurrentlyPlayingMusicFilename();
8514 char *next_music = getMusicInfoEntryFilename(menu.music[game_status]);
8516 if (!strEqual(curr_music, next_music))
8520 void FadeMenuSoundsAndMusic()
8526 void PlaySoundActivating()
8529 PlaySound(SND_MENU_ITEM_ACTIVATING);
8533 void PlaySoundSelecting()
8536 PlaySound(SND_MENU_ITEM_SELECTING);
8540 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8542 boolean change_fullscreen = (setup.fullscreen !=
8543 video.fullscreen_enabled);
8544 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8545 setup.window_scaling_percent !=
8546 video.window_scaling_percent);
8548 if (change_window_scaling_percent && video.fullscreen_enabled)
8551 if (!change_window_scaling_percent && !video.fullscreen_available)
8554 #if defined(TARGET_SDL2)
8555 if (change_window_scaling_percent)
8557 SDLSetWindowScaling(setup.window_scaling_percent);
8561 else if (change_fullscreen)
8563 SDLSetWindowFullscreen(setup.fullscreen);
8565 /* set setup value according to successfully changed fullscreen mode */
8566 setup.fullscreen = video.fullscreen_enabled;
8572 if (change_fullscreen ||
8573 change_window_scaling_percent)
8575 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8577 /* save backbuffer content which gets lost when toggling fullscreen mode */
8578 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8580 if (change_window_scaling_percent)
8582 /* keep window mode, but change window scaling */
8583 video.fullscreen_enabled = TRUE; /* force new window scaling */
8586 /* toggle fullscreen */
8587 ChangeVideoModeIfNeeded(setup.fullscreen);
8589 /* set setup value according to successfully changed fullscreen mode */
8590 setup.fullscreen = video.fullscreen_enabled;
8592 /* restore backbuffer content from temporary backbuffer backup bitmap */
8593 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8595 FreeBitmap(tmp_backbuffer);
8597 /* update visible window/screen */
8598 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8602 void JoinRectangles(int *x, int *y, int *width, int *height,
8603 int x2, int y2, int width2, int height2)
8605 // do not join with "off-screen" rectangle
8606 if (x2 == -1 || y2 == -1)
8611 *width = MAX(*width, width2);
8612 *height = MAX(*height, height2);
8615 void SetAnimStatus(int anim_status_new)
8617 if (anim_status_new == GAME_MODE_MAIN)
8618 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
8619 else if (anim_status_new == GAME_MODE_SCORES)
8620 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
8622 global.anim_status_next = anim_status_new;
8624 // directly set screen modes that are entered without fading
8625 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
8626 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
8627 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
8628 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
8629 global.anim_status = global.anim_status_next;
8632 void SetGameStatus(int game_status_new)
8634 if (game_status_new != game_status)
8635 game_status_last_screen = game_status;
8637 game_status = game_status_new;
8639 SetAnimStatus(game_status_new);
8642 void SetFontStatus(int game_status_new)
8644 static int last_game_status = -1;
8646 if (game_status_new != -1)
8648 // set game status for font use after storing last game status
8649 last_game_status = game_status;
8650 game_status = game_status_new;
8654 // reset game status after font use from last stored game status
8655 game_status = last_game_status;
8659 void ResetFontStatus()
8664 void ChangeViewportPropertiesIfNeeded()
8666 int gfx_game_mode = game_status;
8667 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8669 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
8670 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8671 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8672 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8673 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8674 int new_win_xsize = vp_window->width;
8675 int new_win_ysize = vp_window->height;
8676 int border_size = vp_playfield->border_size;
8677 int new_sx = vp_playfield->x + border_size;
8678 int new_sy = vp_playfield->y + border_size;
8679 int new_sxsize = vp_playfield->width - 2 * border_size;
8680 int new_sysize = vp_playfield->height - 2 * border_size;
8681 int new_real_sx = vp_playfield->x;
8682 int new_real_sy = vp_playfield->y;
8683 int new_full_sxsize = vp_playfield->width;
8684 int new_full_sysize = vp_playfield->height;
8685 int new_dx = vp_door_1->x;
8686 int new_dy = vp_door_1->y;
8687 int new_dxsize = vp_door_1->width;
8688 int new_dysize = vp_door_1->height;
8689 int new_vx = vp_door_2->x;
8690 int new_vy = vp_door_2->y;
8691 int new_vxsize = vp_door_2->width;
8692 int new_vysize = vp_door_2->height;
8693 int new_ex = vp_door_3->x;
8694 int new_ey = vp_door_3->y;
8695 int new_exsize = vp_door_3->width;
8696 int new_eysize = vp_door_3->height;
8697 int new_tilesize_var =
8698 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8700 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8701 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8702 int new_scr_fieldx = new_sxsize / tilesize;
8703 int new_scr_fieldy = new_sysize / tilesize;
8704 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8705 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8706 boolean init_gfx_buffers = FALSE;
8707 boolean init_video_buffer = FALSE;
8708 boolean init_gadgets_and_anims = FALSE;
8709 boolean init_em_graphics = FALSE;
8711 if (new_win_xsize != WIN_XSIZE ||
8712 new_win_ysize != WIN_YSIZE)
8714 WIN_XSIZE = new_win_xsize;
8715 WIN_YSIZE = new_win_ysize;
8717 init_video_buffer = TRUE;
8718 init_gfx_buffers = TRUE;
8719 init_gadgets_and_anims = TRUE;
8721 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8724 if (new_scr_fieldx != SCR_FIELDX ||
8725 new_scr_fieldy != SCR_FIELDY)
8727 /* this always toggles between MAIN and GAME when using small tile size */
8729 SCR_FIELDX = new_scr_fieldx;
8730 SCR_FIELDY = new_scr_fieldy;
8732 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8743 new_sxsize != SXSIZE ||
8744 new_sysize != SYSIZE ||
8745 new_dxsize != DXSIZE ||
8746 new_dysize != DYSIZE ||
8747 new_vxsize != VXSIZE ||
8748 new_vysize != VYSIZE ||
8749 new_exsize != EXSIZE ||
8750 new_eysize != EYSIZE ||
8751 new_real_sx != REAL_SX ||
8752 new_real_sy != REAL_SY ||
8753 new_full_sxsize != FULL_SXSIZE ||
8754 new_full_sysize != FULL_SYSIZE ||
8755 new_tilesize_var != TILESIZE_VAR
8758 // ------------------------------------------------------------------------
8759 // determine next fading area for changed viewport definitions
8760 // ------------------------------------------------------------------------
8762 // start with current playfield area (default fading area)
8765 FADE_SXSIZE = FULL_SXSIZE;
8766 FADE_SYSIZE = FULL_SYSIZE;
8768 // add new playfield area if position or size has changed
8769 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8770 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8772 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8773 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8776 // add current and new door 1 area if position or size has changed
8777 if (new_dx != DX || new_dy != DY ||
8778 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8780 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8781 DX, DY, DXSIZE, DYSIZE);
8782 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8783 new_dx, new_dy, new_dxsize, new_dysize);
8786 // add current and new door 2 area if position or size has changed
8787 if (new_dx != VX || new_dy != VY ||
8788 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8790 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8791 VX, VY, VXSIZE, VYSIZE);
8792 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8793 new_vx, new_vy, new_vxsize, new_vysize);
8796 // ------------------------------------------------------------------------
8797 // handle changed tile size
8798 // ------------------------------------------------------------------------
8800 if (new_tilesize_var != TILESIZE_VAR)
8802 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8804 // changing tile size invalidates scroll values of engine snapshots
8805 FreeEngineSnapshotSingle();
8807 // changing tile size requires update of graphic mapping for EM engine
8808 init_em_graphics = TRUE;
8819 SXSIZE = new_sxsize;
8820 SYSIZE = new_sysize;
8821 DXSIZE = new_dxsize;
8822 DYSIZE = new_dysize;
8823 VXSIZE = new_vxsize;
8824 VYSIZE = new_vysize;
8825 EXSIZE = new_exsize;
8826 EYSIZE = new_eysize;
8827 REAL_SX = new_real_sx;
8828 REAL_SY = new_real_sy;
8829 FULL_SXSIZE = new_full_sxsize;
8830 FULL_SYSIZE = new_full_sysize;
8831 TILESIZE_VAR = new_tilesize_var;
8833 init_gfx_buffers = TRUE;
8834 init_gadgets_and_anims = TRUE;
8836 // printf("::: viewports: init_gfx_buffers\n");
8837 // printf("::: viewports: init_gadgets_and_anims\n");
8840 if (init_gfx_buffers)
8842 // printf("::: init_gfx_buffers\n");
8844 SCR_FIELDX = new_scr_fieldx_buffers;
8845 SCR_FIELDY = new_scr_fieldy_buffers;
8849 SCR_FIELDX = new_scr_fieldx;
8850 SCR_FIELDY = new_scr_fieldy;
8852 SetDrawDeactivationMask(REDRAW_NONE);
8853 SetDrawBackgroundMask(REDRAW_FIELD);
8856 if (init_video_buffer)
8858 // printf("::: init_video_buffer\n");
8860 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8861 InitImageTextures();
8864 if (init_gadgets_and_anims)
8866 // printf("::: init_gadgets_and_anims\n");
8869 InitGlobalAnimations();
8872 if (init_em_graphics)
8874 InitGraphicInfo_EM();