1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
7 // http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
10 // ============================================================================
14 #include "libgame/libgame.h"
26 /* select level set with EMC X11 graphics before activating EM GFX debugging */
27 #define DEBUG_EM_GFX FALSE
28 #define DEBUG_FRAME_TIME FALSE
30 /* tool button identifiers */
31 #define TOOL_CTRL_ID_YES 0
32 #define TOOL_CTRL_ID_NO 1
33 #define TOOL_CTRL_ID_CONFIRM 2
34 #define TOOL_CTRL_ID_PLAYER_1 3
35 #define TOOL_CTRL_ID_PLAYER_2 4
36 #define TOOL_CTRL_ID_PLAYER_3 5
37 #define TOOL_CTRL_ID_PLAYER_4 6
39 #define NUM_TOOL_BUTTONS 7
41 /* constants for number of doors and door parts */
43 #define NUM_PANELS NUM_DOORS
44 // #define NUM_PANELS 0
45 #define MAX_PARTS_PER_DOOR 8
46 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
47 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
50 struct DoorPartOrderInfo
56 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
58 struct DoorPartControlInfo
62 struct DoorPartPosInfo *pos;
65 static struct DoorPartControlInfo door_part_controls[] =
69 IMG_GFX_DOOR_1_PART_1,
74 IMG_GFX_DOOR_1_PART_2,
79 IMG_GFX_DOOR_1_PART_3,
84 IMG_GFX_DOOR_1_PART_4,
89 IMG_GFX_DOOR_1_PART_5,
94 IMG_GFX_DOOR_1_PART_6,
99 IMG_GFX_DOOR_1_PART_7,
104 IMG_GFX_DOOR_1_PART_8,
110 IMG_GFX_DOOR_2_PART_1,
115 IMG_GFX_DOOR_2_PART_2,
120 IMG_GFX_DOOR_2_PART_3,
125 IMG_GFX_DOOR_2_PART_4,
130 IMG_GFX_DOOR_2_PART_5,
135 IMG_GFX_DOOR_2_PART_6,
140 IMG_GFX_DOOR_2_PART_7,
145 IMG_GFX_DOOR_2_PART_8,
151 IMG_BACKGROUND_PANEL,
168 /* forward declaration for internal use */
169 static void UnmapToolButtons();
170 static void HandleToolButtons(struct GadgetInfo *);
171 static int el_act_dir2crm(int, int, int);
172 static int el_act2crm(int, int);
174 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
175 static int request_gadget_id = -1;
177 static char *print_if_not_empty(int element)
179 static char *s = NULL;
180 char *token_name = element_info[element].token_name;
185 s = checked_malloc(strlen(token_name) + 10 + 1);
187 if (element != EL_EMPTY)
188 sprintf(s, "%d\t['%s']", element, token_name);
190 sprintf(s, "%d", element);
195 int correctLevelPosX_EM(int lx)
198 lx -= (BorderElement != EL_EMPTY ? 1 : 0);
203 int correctLevelPosY_EM(int ly)
206 ly -= (BorderElement != EL_EMPTY ? 1 : 0);
211 static int getFieldbufferOffsetX_RND()
213 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
214 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
215 int dx_var = dx * TILESIZE_VAR / TILESIZE;
218 if (EVEN(SCR_FIELDX))
220 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
222 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
223 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
225 fx += (dx_var > 0 ? TILEX_VAR : 0);
232 if (full_lev_fieldx <= SCR_FIELDX)
234 if (EVEN(SCR_FIELDX))
235 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
237 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
243 static int getFieldbufferOffsetY_RND()
245 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
246 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
247 int dy_var = dy * TILESIZE_VAR / TILESIZE;
250 if (EVEN(SCR_FIELDY))
252 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
254 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
255 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
257 fy += (dy_var > 0 ? TILEY_VAR : 0);
264 if (full_lev_fieldy <= SCR_FIELDY)
266 if (EVEN(SCR_FIELDY))
267 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
269 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
275 static int getLevelFromScreenX_RND(int sx)
277 int fx = getFieldbufferOffsetX_RND();
280 int lx = LEVELX((px + dx) / TILESIZE_VAR);
285 static int getLevelFromScreenY_RND(int sy)
287 int fy = getFieldbufferOffsetY_RND();
290 int ly = LEVELY((py + dy) / TILESIZE_VAR);
295 static int getLevelFromScreenX_EM(int sx)
297 int level_xsize = level.native_em_level->lev->width;
298 int full_xsize = level_xsize * TILESIZE_VAR;
300 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
302 int fx = getFieldbufferOffsetX_EM();
305 int lx = LEVELX((px + dx) / TILESIZE_VAR);
307 lx = correctLevelPosX_EM(lx);
312 static int getLevelFromScreenY_EM(int sy)
314 int level_ysize = level.native_em_level->lev->height;
315 int full_ysize = level_ysize * TILESIZE_VAR;
317 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
319 int fy = getFieldbufferOffsetY_EM();
322 int ly = LEVELY((py + dy) / TILESIZE_VAR);
324 ly = correctLevelPosY_EM(ly);
329 static int getLevelFromScreenX_SP(int sx)
331 int menBorder = setup.sp_show_border_elements;
332 int level_xsize = level.native_sp_level->width;
333 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
335 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
337 int fx = getFieldbufferOffsetX_SP();
340 int lx = LEVELX((px + dx) / TILESIZE_VAR);
345 static int getLevelFromScreenY_SP(int sy)
347 int menBorder = setup.sp_show_border_elements;
348 int level_ysize = level.native_sp_level->height;
349 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
351 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
353 int fy = getFieldbufferOffsetY_SP();
356 int ly = LEVELY((py + dy) / TILESIZE_VAR);
361 int getLevelFromScreenX(int x)
363 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
364 return getLevelFromScreenX_EM(x);
365 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
366 return getLevelFromScreenX_SP(x);
368 return getLevelFromScreenX_RND(x);
371 int getLevelFromScreenY(int y)
373 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
374 return getLevelFromScreenY_EM(y);
375 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
376 return getLevelFromScreenY_SP(y);
378 return getLevelFromScreenY_RND(y);
381 void DumpTile(int x, int y)
386 printf_line("-", 79);
387 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
388 printf_line("-", 79);
390 if (!IN_LEV_FIELD(x, y))
392 printf("(not in level field)\n");
398 printf(" Feld: %d\t['%s']\n", Feld[x][y],
399 element_info[Feld[x][y]].token_name);
400 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
401 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
402 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
403 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
404 printf(" MovPos: %d\n", MovPos[x][y]);
405 printf(" MovDir: %d\n", MovDir[x][y]);
406 printf(" MovDelay: %d\n", MovDelay[x][y]);
407 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
408 printf(" CustomValue: %d\n", CustomValue[x][y]);
409 printf(" GfxElement: %d\n", GfxElement[x][y]);
410 printf(" GfxAction: %d\n", GfxAction[x][y]);
411 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
412 printf(" Player x/y: %d, %d\n", local_player->jx, local_player->jy);
416 void DumpTileFromScreen(int sx, int sy)
418 int lx = getLevelFromScreenX(sx);
419 int ly = getLevelFromScreenY(sy);
424 void SetDrawtoField(int mode)
426 if (mode == DRAW_TO_FIELDBUFFER)
432 BX2 = SCR_FIELDX + 1;
433 BY2 = SCR_FIELDY + 1;
435 drawto_field = fieldbuffer;
437 else /* DRAW_TO_BACKBUFFER */
443 BX2 = SCR_FIELDX - 1;
444 BY2 = SCR_FIELDY - 1;
446 drawto_field = backbuffer;
450 static void RedrawPlayfield_RND()
452 if (game.envelope_active)
455 DrawLevel(REDRAW_ALL);
459 void RedrawPlayfield()
461 if (game_status != GAME_MODE_PLAYING)
464 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
465 RedrawPlayfield_EM(TRUE);
466 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
467 RedrawPlayfield_SP(TRUE);
468 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
469 RedrawPlayfield_RND();
471 BlitScreenToBitmap(backbuffer);
473 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
477 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
480 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
481 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
483 if (x == -1 && y == -1)
486 if (draw_target == DRAW_TO_SCREEN)
487 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
489 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
492 static void DrawMaskedBorderExt_FIELD(int draw_target)
494 if (global.border_status >= GAME_MODE_MAIN &&
495 global.border_status <= GAME_MODE_PLAYING &&
496 border.draw_masked[global.border_status])
497 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
501 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
503 // when drawing to backbuffer, never draw border over open doors
504 if (draw_target == DRAW_TO_BACKBUFFER &&
505 (GetDoorState() & DOOR_OPEN_1))
508 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
509 (global.border_status != GAME_MODE_EDITOR ||
510 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
511 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
514 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
516 // when drawing to backbuffer, never draw border over open doors
517 if (draw_target == DRAW_TO_BACKBUFFER &&
518 (GetDoorState() & DOOR_OPEN_2))
521 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
522 global.border_status != GAME_MODE_EDITOR)
523 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
526 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
528 /* currently not available */
531 static void DrawMaskedBorderExt_ALL(int draw_target)
533 DrawMaskedBorderExt_FIELD(draw_target);
534 DrawMaskedBorderExt_DOOR_1(draw_target);
535 DrawMaskedBorderExt_DOOR_2(draw_target);
536 DrawMaskedBorderExt_DOOR_3(draw_target);
539 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
541 /* never draw masked screen borders on borderless screens */
542 if (global.border_status == GAME_MODE_LOADING ||
543 global.border_status == GAME_MODE_TITLE)
546 if (redraw_mask & REDRAW_ALL)
547 DrawMaskedBorderExt_ALL(draw_target);
550 if (redraw_mask & REDRAW_FIELD)
551 DrawMaskedBorderExt_FIELD(draw_target);
552 if (redraw_mask & REDRAW_DOOR_1)
553 DrawMaskedBorderExt_DOOR_1(draw_target);
554 if (redraw_mask & REDRAW_DOOR_2)
555 DrawMaskedBorderExt_DOOR_2(draw_target);
556 if (redraw_mask & REDRAW_DOOR_3)
557 DrawMaskedBorderExt_DOOR_3(draw_target);
561 void DrawMaskedBorder_FIELD()
563 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
566 void DrawMaskedBorder(int redraw_mask)
568 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
571 void DrawMaskedBorderToTarget(int draw_target)
573 if (draw_target == DRAW_TO_BACKBUFFER ||
574 draw_target == DRAW_TO_SCREEN)
576 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
580 int last_border_status = global.border_status;
582 if (draw_target == DRAW_TO_FADE_SOURCE)
584 global.border_status = gfx.fade_border_source_status;
585 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
587 else if (draw_target == DRAW_TO_FADE_TARGET)
589 global.border_status = gfx.fade_border_target_status;
590 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
593 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
595 global.border_status = last_border_status;
596 gfx.masked_border_bitmap_ptr = backbuffer;
600 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
602 int fx = getFieldbufferOffsetX_RND();
603 int fy = getFieldbufferOffsetY_RND();
605 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
608 void BlitScreenToBitmap(Bitmap *target_bitmap)
610 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
611 BlitScreenToBitmap_EM(target_bitmap);
612 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
613 BlitScreenToBitmap_SP(target_bitmap);
614 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
615 BlitScreenToBitmap_RND(target_bitmap);
617 redraw_mask |= REDRAW_FIELD;
620 void DrawFramesPerSecond()
623 int font_nr = FONT_TEXT_2;
624 int font_width = getFontWidth(font_nr);
625 int draw_deactivation_mask = GetDrawDeactivationMask();
626 boolean draw_masked = (draw_deactivation_mask == REDRAW_NONE);
628 /* draw FPS with leading space (needed if field buffer deactivated) */
629 sprintf(text, " %04.1f fps", global.frames_per_second);
631 /* override draw deactivation mask (required for invisible warp mode) */
632 SetDrawDeactivationMask(REDRAW_NONE);
634 /* draw opaque FPS if field buffer deactivated, else draw masked FPS */
635 DrawTextExt(backbuffer, SX + SXSIZE - font_width * strlen(text), SY, text,
636 font_nr, (draw_masked ? BLIT_MASKED : BLIT_OPAQUE));
638 /* set draw deactivation mask to previous value */
639 SetDrawDeactivationMask(draw_deactivation_mask);
641 /* force full-screen redraw in this frame */
642 redraw_mask = REDRAW_ALL;
646 static void PrintFrameTimeDebugging()
648 static unsigned int last_counter = 0;
649 unsigned int counter = Counter();
650 int diff_1 = counter - last_counter;
651 int diff_2 = diff_1 - GAME_FRAME_DELAY;
653 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
654 char diff_bar[2 * diff_2_max + 5];
658 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
660 for (i = 0; i < diff_2_max; i++)
661 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
662 i >= diff_2_max - diff_2_cut ? '-' : ' ');
664 diff_bar[pos++] = '|';
666 for (i = 0; i < diff_2_max; i++)
667 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
669 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
671 diff_bar[pos++] = '\0';
673 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
676 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
679 last_counter = counter;
683 static int unifiedRedrawMask(int mask)
685 if (mask & REDRAW_ALL)
688 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
694 static boolean equalRedrawMasks(int mask_1, int mask_2)
696 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
701 static int last_redraw_mask = REDRAW_NONE;
703 // force screen redraw in every frame to continue drawing global animations
704 // (but always use the last redraw mask to prevent unwanted side effects)
705 if (redraw_mask == REDRAW_NONE)
706 redraw_mask = last_redraw_mask;
708 last_redraw_mask = redraw_mask;
711 // masked border now drawn immediately when blitting backbuffer to window
713 // draw masked border to all viewports, if defined
714 DrawMaskedBorder(redraw_mask);
717 // draw frames per second (only if debug mode is enabled)
718 if (redraw_mask & REDRAW_FPS)
719 DrawFramesPerSecond();
721 // remove playfield redraw before potentially merging with doors redraw
722 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
723 redraw_mask &= ~REDRAW_FIELD;
725 // redraw complete window if both playfield and (some) doors need redraw
726 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
727 redraw_mask = REDRAW_ALL;
729 /* although redrawing the whole window would be fine for normal gameplay,
730 being able to only redraw the playfield is required for deactivating
731 certain drawing areas (mainly playfield) to work, which is needed for
732 warp-forward to be fast enough (by skipping redraw of most frames) */
734 if (redraw_mask & REDRAW_ALL)
736 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
738 else if (redraw_mask & REDRAW_FIELD)
740 BlitBitmap(backbuffer, window,
741 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
743 else if (redraw_mask & REDRAW_DOORS)
745 // merge door areas to prevent calling screen redraw more than once
751 if (redraw_mask & REDRAW_DOOR_1)
755 x2 = MAX(x2, DX + DXSIZE);
756 y2 = MAX(y2, DY + DYSIZE);
759 if (redraw_mask & REDRAW_DOOR_2)
763 x2 = MAX(x2, VX + VXSIZE);
764 y2 = MAX(y2, VY + VYSIZE);
767 if (redraw_mask & REDRAW_DOOR_3)
771 x2 = MAX(x2, EX + EXSIZE);
772 y2 = MAX(y2, EY + EYSIZE);
775 // make sure that at least one pixel is blitted, and inside the screen
776 // (else nothing is blitted, causing the animations not to be updated)
777 x1 = MIN(MAX(0, x1), WIN_XSIZE - 1);
778 y1 = MIN(MAX(0, y1), WIN_YSIZE - 1);
779 x2 = MIN(MAX(1, x2), WIN_XSIZE);
780 y2 = MIN(MAX(1, y2), WIN_YSIZE);
782 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
785 redraw_mask = REDRAW_NONE;
788 PrintFrameTimeDebugging();
792 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
794 unsigned int frame_delay_value_old = GetVideoFrameDelay();
796 SetVideoFrameDelay(frame_delay_value);
800 SetVideoFrameDelay(frame_delay_value_old);
803 static int fade_type_skip = FADE_TYPE_NONE;
805 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
807 void (*draw_border_function)(void) = NULL;
808 int x, y, width, height;
809 int fade_delay, post_delay;
811 if (fade_type == FADE_TYPE_FADE_OUT)
813 if (fade_type_skip != FADE_TYPE_NONE)
815 /* skip all fade operations until specified fade operation */
816 if (fade_type & fade_type_skip)
817 fade_type_skip = FADE_TYPE_NONE;
822 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
826 redraw_mask |= fade_mask;
828 if (fade_type == FADE_TYPE_SKIP)
830 fade_type_skip = fade_mode;
835 fade_delay = fading.fade_delay;
836 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
838 if (fade_type_skip != FADE_TYPE_NONE)
840 /* skip all fade operations until specified fade operation */
841 if (fade_type & fade_type_skip)
842 fade_type_skip = FADE_TYPE_NONE;
847 if (global.autoplay_leveldir)
852 if (fade_mask == REDRAW_FIELD)
857 height = FADE_SYSIZE;
859 if (border.draw_masked_when_fading)
860 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
862 DrawMaskedBorder_FIELD(); /* draw once */
864 else /* REDRAW_ALL */
872 if (!setup.fade_screens ||
874 fading.fade_mode == FADE_MODE_NONE)
876 if (fade_mode == FADE_MODE_FADE_OUT)
879 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
881 redraw_mask &= ~fade_mask;
886 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
887 draw_border_function);
889 redraw_mask &= ~fade_mask;
892 static void SetScreenStates_BeforeFadingIn()
894 // temporarily set screen mode for animations to screen after fading in
895 global.anim_status = global.anim_status_next;
897 // store backbuffer with all animations that will be started after fading in
898 if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
899 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
901 // set screen mode for animations back to fading
902 global.anim_status = GAME_MODE_PSEUDO_FADING;
905 static void SetScreenStates_AfterFadingIn()
907 // store new source screen (to use correct masked border for fading)
908 gfx.fade_border_source_status = global.border_status;
910 global.anim_status = global.anim_status_next;
913 static void SetScreenStates_BeforeFadingOut()
915 // store new target screen (to use correct masked border for fading)
916 gfx.fade_border_target_status = game_status;
918 // set screen mode for animations to fading
919 global.anim_status = GAME_MODE_PSEUDO_FADING;
921 // store backbuffer with all animations that will be stopped for fading out
922 if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
923 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
926 static void SetScreenStates_AfterFadingOut()
928 global.border_status = game_status;
931 void FadeIn(int fade_mask)
933 SetScreenStates_BeforeFadingIn();
936 DrawMaskedBorder(REDRAW_ALL);
939 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
940 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
942 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
946 FADE_SXSIZE = FULL_SXSIZE;
947 FADE_SYSIZE = FULL_SYSIZE;
949 if (game_status == GAME_MODE_PLAYING &&
950 strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
951 SetOverlayActive(TRUE);
953 SetScreenStates_AfterFadingIn();
955 // force update of global animation status in case of rapid screen changes
956 redraw_mask = REDRAW_ALL;
960 void FadeOut(int fade_mask)
962 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
963 if (!equalRedrawMasks(fade_mask, redraw_mask))
966 SetScreenStates_BeforeFadingOut();
968 SetOverlayActive(FALSE);
971 DrawMaskedBorder(REDRAW_ALL);
974 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
975 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
977 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
979 SetScreenStates_AfterFadingOut();
982 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
984 static struct TitleFadingInfo fading_leave_stored;
987 fading_leave_stored = fading_leave;
989 fading = fading_leave_stored;
992 void FadeSetEnterMenu()
994 fading = menu.enter_menu;
996 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
999 void FadeSetLeaveMenu()
1001 fading = menu.leave_menu;
1003 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1006 void FadeSetEnterScreen()
1008 fading = menu.enter_screen[game_status];
1010 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
1013 void FadeSetNextScreen()
1015 fading = menu.next_screen[game_status];
1017 // (do not overwrite fade mode set by FadeSetEnterScreen)
1018 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1021 void FadeSetLeaveScreen()
1023 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
1026 void FadeSetFromType(int type)
1028 if (type & TYPE_ENTER_SCREEN)
1029 FadeSetEnterScreen();
1030 else if (type & TYPE_ENTER)
1032 else if (type & TYPE_LEAVE)
1036 void FadeSetDisabled()
1038 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1040 fading = fading_none;
1043 void FadeSkipNextFadeIn()
1045 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1048 void FadeSkipNextFadeOut()
1050 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1053 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1055 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1057 return (graphic == IMG_UNDEFINED ? NULL :
1058 graphic_info[graphic].bitmap != NULL || redefined ?
1059 graphic_info[graphic].bitmap :
1060 graphic_info[default_graphic].bitmap);
1063 Bitmap *getBackgroundBitmap(int graphic)
1065 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1068 Bitmap *getGlobalBorderBitmap(int graphic)
1070 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1073 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1076 (status == GAME_MODE_MAIN ||
1077 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1078 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1079 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1080 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1083 return getGlobalBorderBitmap(graphic);
1086 void SetWindowBackgroundImageIfDefined(int graphic)
1088 if (graphic_info[graphic].bitmap)
1089 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1092 void SetMainBackgroundImageIfDefined(int graphic)
1094 if (graphic_info[graphic].bitmap)
1095 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1098 void SetDoorBackgroundImageIfDefined(int graphic)
1100 if (graphic_info[graphic].bitmap)
1101 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1104 void SetWindowBackgroundImage(int graphic)
1106 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1109 void SetMainBackgroundImage(int graphic)
1111 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1114 void SetDoorBackgroundImage(int graphic)
1116 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1119 void SetPanelBackground()
1121 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1123 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1124 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1126 SetDoorBackgroundBitmap(bitmap_db_panel);
1129 void DrawBackground(int x, int y, int width, int height)
1131 /* "drawto" might still point to playfield buffer here (hall of fame) */
1132 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1134 if (IN_GFX_FIELD_FULL(x, y))
1135 redraw_mask |= REDRAW_FIELD;
1136 else if (IN_GFX_DOOR_1(x, y))
1137 redraw_mask |= REDRAW_DOOR_1;
1138 else if (IN_GFX_DOOR_2(x, y))
1139 redraw_mask |= REDRAW_DOOR_2;
1140 else if (IN_GFX_DOOR_3(x, y))
1141 redraw_mask |= REDRAW_DOOR_3;
1144 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1146 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1148 if (font->bitmap == NULL)
1151 DrawBackground(x, y, width, height);
1154 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1156 struct GraphicInfo *g = &graphic_info[graphic];
1158 if (g->bitmap == NULL)
1161 DrawBackground(x, y, width, height);
1164 static int game_status_last = -1;
1165 static Bitmap *global_border_bitmap_last = NULL;
1166 static Bitmap *global_border_bitmap = NULL;
1167 static int real_sx_last = -1, real_sy_last = -1;
1168 static int full_sxsize_last = -1, full_sysize_last = -1;
1169 static int dx_last = -1, dy_last = -1;
1170 static int dxsize_last = -1, dysize_last = -1;
1171 static int vx_last = -1, vy_last = -1;
1172 static int vxsize_last = -1, vysize_last = -1;
1174 boolean CheckIfGlobalBorderHasChanged()
1176 // if game status has not changed, global border has not changed either
1177 if (game_status == game_status_last)
1180 // determine and store new global border bitmap for current game status
1181 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1183 return (global_border_bitmap_last != global_border_bitmap);
1186 boolean CheckIfGlobalBorderRedrawIsNeeded()
1188 // if game status has not changed, nothing has to be redrawn
1189 if (game_status == game_status_last)
1192 // redraw if last screen was title screen
1193 if (game_status_last == GAME_MODE_TITLE)
1196 // redraw if global screen border has changed
1197 if (CheckIfGlobalBorderHasChanged())
1200 // redraw if position or size of playfield area has changed
1201 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1202 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1205 // redraw if position or size of door area has changed
1206 if (dx_last != DX || dy_last != DY ||
1207 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1210 // redraw if position or size of tape area has changed
1211 if (vx_last != VX || vy_last != VY ||
1212 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1218 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1221 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1223 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1226 void RedrawGlobalBorder()
1228 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1230 RedrawGlobalBorderFromBitmap(bitmap);
1232 redraw_mask = REDRAW_ALL;
1235 static void RedrawGlobalBorderIfNeeded()
1237 if (game_status == game_status_last)
1240 // copy current draw buffer to later copy back areas that have not changed
1241 if (game_status_last != GAME_MODE_TITLE)
1242 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1244 if (CheckIfGlobalBorderRedrawIsNeeded())
1246 // redraw global screen border (or clear, if defined to be empty)
1247 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1249 // copy previous playfield and door areas, if they are defined on both
1250 // previous and current screen and if they still have the same size
1252 if (real_sx_last != -1 && real_sy_last != -1 &&
1253 REAL_SX != -1 && REAL_SY != -1 &&
1254 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1255 BlitBitmap(bitmap_db_store_1, backbuffer,
1256 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1259 if (dx_last != -1 && dy_last != -1 &&
1260 DX != -1 && DY != -1 &&
1261 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1262 BlitBitmap(bitmap_db_store_1, backbuffer,
1263 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1265 if (vx_last != -1 && vy_last != -1 &&
1266 VX != -1 && VY != -1 &&
1267 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1268 BlitBitmap(bitmap_db_store_1, backbuffer,
1269 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1271 redraw_mask = REDRAW_ALL;
1274 game_status_last = game_status;
1276 global_border_bitmap_last = global_border_bitmap;
1278 real_sx_last = REAL_SX;
1279 real_sy_last = REAL_SY;
1280 full_sxsize_last = FULL_SXSIZE;
1281 full_sysize_last = FULL_SYSIZE;
1284 dxsize_last = DXSIZE;
1285 dysize_last = DYSIZE;
1288 vxsize_last = VXSIZE;
1289 vysize_last = VYSIZE;
1294 RedrawGlobalBorderIfNeeded();
1296 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1297 /* (when entering hall of fame after playing) */
1298 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1300 /* !!! maybe this should be done before clearing the background !!! */
1301 if (game_status == GAME_MODE_PLAYING)
1303 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1304 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1308 SetDrawtoField(DRAW_TO_BACKBUFFER);
1312 void MarkTileDirty(int x, int y)
1314 redraw_mask |= REDRAW_FIELD;
1317 void SetBorderElement()
1321 BorderElement = EL_EMPTY;
1323 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1325 for (x = 0; x < lev_fieldx; x++)
1327 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1328 BorderElement = EL_STEELWALL;
1330 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1336 void FloodFillLevel(int from_x, int from_y, int fill_element,
1337 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1338 int max_fieldx, int max_fieldy)
1342 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1343 static int safety = 0;
1345 /* check if starting field still has the desired content */
1346 if (field[from_x][from_y] == fill_element)
1351 if (safety > max_fieldx * max_fieldy)
1352 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1354 old_element = field[from_x][from_y];
1355 field[from_x][from_y] = fill_element;
1357 for (i = 0; i < 4; i++)
1359 x = from_x + check[i][0];
1360 y = from_y + check[i][1];
1362 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1363 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1369 void SetRandomAnimationValue(int x, int y)
1371 gfx.anim_random_frame = GfxRandom[x][y];
1374 int getGraphicAnimationFrame(int graphic, int sync_frame)
1376 /* animation synchronized with global frame counter, not move position */
1377 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1378 sync_frame = FrameCounter;
1380 return getAnimationFrame(graphic_info[graphic].anim_frames,
1381 graphic_info[graphic].anim_delay,
1382 graphic_info[graphic].anim_mode,
1383 graphic_info[graphic].anim_start_frame,
1387 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1389 struct GraphicInfo *g = &graphic_info[graphic];
1390 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1392 if (tilesize == gfx.standard_tile_size)
1393 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1394 else if (tilesize == game.tile_size)
1395 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1397 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1400 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1401 boolean get_backside)
1403 struct GraphicInfo *g = &graphic_info[graphic];
1404 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1405 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1407 if (g->offset_y == 0) /* frames are ordered horizontally */
1409 int max_width = g->anim_frames_per_line * g->width;
1410 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1412 *x = pos % max_width;
1413 *y = src_y % g->height + pos / max_width * g->height;
1415 else if (g->offset_x == 0) /* frames are ordered vertically */
1417 int max_height = g->anim_frames_per_line * g->height;
1418 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1420 *x = src_x % g->width + pos / max_height * g->width;
1421 *y = pos % max_height;
1423 else /* frames are ordered diagonally */
1425 *x = src_x + frame * g->offset_x;
1426 *y = src_y + frame * g->offset_y;
1430 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1431 Bitmap **bitmap, int *x, int *y,
1432 boolean get_backside)
1434 struct GraphicInfo *g = &graphic_info[graphic];
1436 // if no in-game graphics defined, always use standard graphic size
1437 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1438 tilesize = TILESIZE;
1440 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1441 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1443 *x = *x * tilesize / g->tile_size;
1444 *y = *y * tilesize / g->tile_size;
1447 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1448 int *x, int *y, boolean get_backside)
1450 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1454 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1455 Bitmap **bitmap, int *x, int *y)
1457 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1460 void getFixedGraphicSource(int graphic, int frame,
1461 Bitmap **bitmap, int *x, int *y)
1463 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1466 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1468 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1471 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1472 int *x, int *y, boolean get_backside)
1474 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1478 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1480 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1483 void DrawGraphic(int x, int y, int graphic, int frame)
1486 if (!IN_SCR_FIELD(x, y))
1488 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1489 printf("DrawGraphic(): This should never happen!\n");
1494 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1497 MarkTileDirty(x, y);
1500 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1503 if (!IN_SCR_FIELD(x, y))
1505 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1506 printf("DrawGraphic(): This should never happen!\n");
1511 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1513 MarkTileDirty(x, y);
1516 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1522 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1524 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1527 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1533 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1534 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1537 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1540 if (!IN_SCR_FIELD(x, y))
1542 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1543 printf("DrawGraphicThruMask(): This should never happen!\n");
1548 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1551 MarkTileDirty(x, y);
1554 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1557 if (!IN_SCR_FIELD(x, y))
1559 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1560 printf("DrawGraphicThruMask(): This should never happen!\n");
1565 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1567 MarkTileDirty(x, y);
1570 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1576 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1578 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1582 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1583 int graphic, int frame)
1588 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1590 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1594 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1596 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1598 MarkTileDirty(x / tilesize, y / tilesize);
1601 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1607 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1608 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1611 void DrawMiniGraphic(int x, int y, int graphic)
1613 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1614 MarkTileDirty(x / 2, y / 2);
1617 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1622 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1623 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1626 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1627 int graphic, int frame,
1628 int cut_mode, int mask_mode)
1633 int width = TILEX, height = TILEY;
1636 if (dx || dy) /* shifted graphic */
1638 if (x < BX1) /* object enters playfield from the left */
1645 else if (x > BX2) /* object enters playfield from the right */
1651 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1657 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1659 else if (dx) /* general horizontal movement */
1660 MarkTileDirty(x + SIGN(dx), y);
1662 if (y < BY1) /* object enters playfield from the top */
1664 if (cut_mode == CUT_BELOW) /* object completely above top border */
1672 else if (y > BY2) /* object enters playfield from the bottom */
1678 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1684 else if (dy > 0 && cut_mode == CUT_ABOVE)
1686 if (y == BY2) /* object completely above bottom border */
1692 MarkTileDirty(x, y + 1);
1693 } /* object leaves playfield to the bottom */
1694 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1696 else if (dy) /* general vertical movement */
1697 MarkTileDirty(x, y + SIGN(dy));
1701 if (!IN_SCR_FIELD(x, y))
1703 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1704 printf("DrawGraphicShifted(): This should never happen!\n");
1709 width = width * TILESIZE_VAR / TILESIZE;
1710 height = height * TILESIZE_VAR / TILESIZE;
1711 cx = cx * TILESIZE_VAR / TILESIZE;
1712 cy = cy * TILESIZE_VAR / TILESIZE;
1713 dx = dx * TILESIZE_VAR / TILESIZE;
1714 dy = dy * TILESIZE_VAR / TILESIZE;
1716 if (width > 0 && height > 0)
1718 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1723 dst_x = FX + x * TILEX_VAR + dx;
1724 dst_y = FY + y * TILEY_VAR + dy;
1726 if (mask_mode == USE_MASKING)
1727 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1730 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1733 MarkTileDirty(x, y);
1737 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1738 int graphic, int frame,
1739 int cut_mode, int mask_mode)
1744 int width = TILEX_VAR, height = TILEY_VAR;
1747 int x2 = x + SIGN(dx);
1748 int y2 = y + SIGN(dy);
1750 /* movement with two-tile animations must be sync'ed with movement position,
1751 not with current GfxFrame (which can be higher when using slow movement) */
1752 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1753 int anim_frames = graphic_info[graphic].anim_frames;
1755 /* (we also need anim_delay here for movement animations with less frames) */
1756 int anim_delay = graphic_info[graphic].anim_delay;
1757 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1759 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1760 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1762 /* re-calculate animation frame for two-tile movement animation */
1763 frame = getGraphicAnimationFrame(graphic, sync_frame);
1765 /* check if movement start graphic inside screen area and should be drawn */
1766 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1768 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1770 dst_x = FX + x1 * TILEX_VAR;
1771 dst_y = FY + y1 * TILEY_VAR;
1773 if (mask_mode == USE_MASKING)
1774 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1777 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1780 MarkTileDirty(x1, y1);
1783 /* check if movement end graphic inside screen area and should be drawn */
1784 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1786 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1788 dst_x = FX + x2 * TILEX_VAR;
1789 dst_y = FY + y2 * TILEY_VAR;
1791 if (mask_mode == USE_MASKING)
1792 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1795 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1798 MarkTileDirty(x2, y2);
1802 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1803 int graphic, int frame,
1804 int cut_mode, int mask_mode)
1808 DrawGraphic(x, y, graphic, frame);
1813 if (graphic_info[graphic].double_movement) /* EM style movement images */
1814 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1816 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1819 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1820 int frame, int cut_mode)
1822 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1825 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1826 int cut_mode, int mask_mode)
1828 int lx = LEVELX(x), ly = LEVELY(y);
1832 if (IN_LEV_FIELD(lx, ly))
1834 SetRandomAnimationValue(lx, ly);
1836 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1837 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1839 /* do not use double (EM style) movement graphic when not moving */
1840 if (graphic_info[graphic].double_movement && !dx && !dy)
1842 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1843 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1846 else /* border element */
1848 graphic = el2img(element);
1849 frame = getGraphicAnimationFrame(graphic, -1);
1852 if (element == EL_EXPANDABLE_WALL)
1854 boolean left_stopped = FALSE, right_stopped = FALSE;
1856 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1857 left_stopped = TRUE;
1858 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1859 right_stopped = TRUE;
1861 if (left_stopped && right_stopped)
1863 else if (left_stopped)
1865 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1866 frame = graphic_info[graphic].anim_frames - 1;
1868 else if (right_stopped)
1870 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1871 frame = graphic_info[graphic].anim_frames - 1;
1876 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1877 else if (mask_mode == USE_MASKING)
1878 DrawGraphicThruMask(x, y, graphic, frame);
1880 DrawGraphic(x, y, graphic, frame);
1883 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1884 int cut_mode, int mask_mode)
1886 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1887 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1888 cut_mode, mask_mode);
1891 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1894 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1897 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1900 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1903 void DrawLevelElementThruMask(int x, int y, int element)
1905 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1908 void DrawLevelFieldThruMask(int x, int y)
1910 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1913 /* !!! implementation of quicksand is totally broken !!! */
1914 #define IS_CRUMBLED_TILE(x, y, e) \
1915 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1916 !IS_MOVING(x, y) || \
1917 (e) == EL_QUICKSAND_EMPTYING || \
1918 (e) == EL_QUICKSAND_FAST_EMPTYING))
1920 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1925 int width, height, cx, cy;
1926 int sx = SCREENX(x), sy = SCREENY(y);
1927 int crumbled_border_size = graphic_info[graphic].border_size;
1928 int crumbled_tile_size = graphic_info[graphic].tile_size;
1929 int crumbled_border_size_var =
1930 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
1933 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1935 for (i = 1; i < 4; i++)
1937 int dxx = (i & 1 ? dx : 0);
1938 int dyy = (i & 2 ? dy : 0);
1941 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1944 /* check if neighbour field is of same crumble type */
1945 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1946 graphic_info[graphic].class ==
1947 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1949 /* return if check prevents inner corner */
1950 if (same == (dxx == dx && dyy == dy))
1954 /* if we reach this point, we have an inner corner */
1956 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1958 width = crumbled_border_size_var;
1959 height = crumbled_border_size_var;
1960 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1961 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1963 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1964 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1967 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1972 int width, height, bx, by, cx, cy;
1973 int sx = SCREENX(x), sy = SCREENY(y);
1974 int crumbled_border_size = graphic_info[graphic].border_size;
1975 int crumbled_tile_size = graphic_info[graphic].tile_size;
1976 int crumbled_border_size_var =
1977 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
1978 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1981 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1983 /* draw simple, sloppy, non-corner-accurate crumbled border */
1985 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1986 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1987 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1988 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1990 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1991 FX + sx * TILEX_VAR + cx,
1992 FY + sy * TILEY_VAR + cy);
1994 /* (remaining middle border part must be at least as big as corner part) */
1995 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1996 crumbled_border_size_var >= TILESIZE_VAR / 3)
1999 /* correct corners of crumbled border, if needed */
2001 for (i = -1; i <= 1; i += 2)
2003 int xx = x + (dir == 0 || dir == 3 ? i : 0);
2004 int yy = y + (dir == 1 || dir == 2 ? i : 0);
2005 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2008 /* check if neighbour field is of same crumble type */
2009 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2010 graphic_info[graphic].class ==
2011 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2013 /* no crumbled corner, but continued crumbled border */
2015 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2016 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2017 int b1 = (i == 1 ? crumbled_border_size_var :
2018 TILESIZE_VAR - 2 * crumbled_border_size_var);
2020 width = crumbled_border_size_var;
2021 height = crumbled_border_size_var;
2023 if (dir == 1 || dir == 2)
2038 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2040 FX + sx * TILEX_VAR + cx,
2041 FY + sy * TILEY_VAR + cy);
2046 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2048 int sx = SCREENX(x), sy = SCREENY(y);
2051 static int xy[4][2] =
2059 if (!IN_LEV_FIELD(x, y))
2062 element = TILE_GFX_ELEMENT(x, y);
2064 if (IS_CRUMBLED_TILE(x, y, element)) /* crumble field itself */
2066 if (!IN_SCR_FIELD(sx, sy))
2069 /* crumble field borders towards direct neighbour fields */
2070 for (i = 0; i < 4; i++)
2072 int xx = x + xy[i][0];
2073 int yy = y + xy[i][1];
2075 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2078 /* check if neighbour field is of same crumble type */
2079 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2080 graphic_info[graphic].class ==
2081 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2084 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2087 /* crumble inner field corners towards corner neighbour fields */
2088 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2089 graphic_info[graphic].anim_frames == 2)
2091 for (i = 0; i < 4; i++)
2093 int dx = (i & 1 ? +1 : -1);
2094 int dy = (i & 2 ? +1 : -1);
2096 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2100 MarkTileDirty(sx, sy);
2102 else /* center field is not crumbled -- crumble neighbour fields */
2104 /* crumble field borders of direct neighbour fields */
2105 for (i = 0; i < 4; i++)
2107 int xx = x + xy[i][0];
2108 int yy = y + xy[i][1];
2109 int sxx = sx + xy[i][0];
2110 int syy = sy + xy[i][1];
2112 if (!IN_LEV_FIELD(xx, yy) ||
2113 !IN_SCR_FIELD(sxx, syy))
2116 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2119 element = TILE_GFX_ELEMENT(xx, yy);
2121 if (!IS_CRUMBLED_TILE(xx, yy, element))
2124 graphic = el_act2crm(element, ACTION_DEFAULT);
2126 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2128 MarkTileDirty(sxx, syy);
2131 /* crumble inner field corners of corner neighbour fields */
2132 for (i = 0; i < 4; i++)
2134 int dx = (i & 1 ? +1 : -1);
2135 int dy = (i & 2 ? +1 : -1);
2141 if (!IN_LEV_FIELD(xx, yy) ||
2142 !IN_SCR_FIELD(sxx, syy))
2145 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2148 element = TILE_GFX_ELEMENT(xx, yy);
2150 if (!IS_CRUMBLED_TILE(xx, yy, element))
2153 graphic = el_act2crm(element, ACTION_DEFAULT);
2155 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2156 graphic_info[graphic].anim_frames == 2)
2157 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2159 MarkTileDirty(sxx, syy);
2164 void DrawLevelFieldCrumbled(int x, int y)
2168 if (!IN_LEV_FIELD(x, y))
2171 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2172 GfxElement[x][y] != EL_UNDEFINED &&
2173 GFX_CRUMBLED(GfxElement[x][y]))
2175 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2180 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2182 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2185 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2188 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2189 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2190 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2191 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2192 int sx = SCREENX(x), sy = SCREENY(y);
2194 DrawGraphic(sx, sy, graphic1, frame1);
2195 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2198 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2200 int sx = SCREENX(x), sy = SCREENY(y);
2201 static int xy[4][2] =
2210 /* crumble direct neighbour fields (required for field borders) */
2211 for (i = 0; i < 4; i++)
2213 int xx = x + xy[i][0];
2214 int yy = y + xy[i][1];
2215 int sxx = sx + xy[i][0];
2216 int syy = sy + xy[i][1];
2218 if (!IN_LEV_FIELD(xx, yy) ||
2219 !IN_SCR_FIELD(sxx, syy) ||
2220 !GFX_CRUMBLED(Feld[xx][yy]) ||
2224 DrawLevelField(xx, yy);
2227 /* crumble corner neighbour fields (required for inner field corners) */
2228 for (i = 0; i < 4; i++)
2230 int dx = (i & 1 ? +1 : -1);
2231 int dy = (i & 2 ? +1 : -1);
2237 if (!IN_LEV_FIELD(xx, yy) ||
2238 !IN_SCR_FIELD(sxx, syy) ||
2239 !GFX_CRUMBLED(Feld[xx][yy]) ||
2243 int element = TILE_GFX_ELEMENT(xx, yy);
2244 int graphic = el_act2crm(element, ACTION_DEFAULT);
2246 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2247 graphic_info[graphic].anim_frames == 2)
2248 DrawLevelField(xx, yy);
2252 static int getBorderElement(int x, int y)
2256 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2257 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2258 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2259 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2260 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2261 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2262 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2264 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2265 int steel_position = (x == -1 && y == -1 ? 0 :
2266 x == lev_fieldx && y == -1 ? 1 :
2267 x == -1 && y == lev_fieldy ? 2 :
2268 x == lev_fieldx && y == lev_fieldy ? 3 :
2269 x == -1 || x == lev_fieldx ? 4 :
2270 y == -1 || y == lev_fieldy ? 5 : 6);
2272 return border[steel_position][steel_type];
2275 void DrawScreenElement(int x, int y, int element)
2277 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2278 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2281 void DrawLevelElement(int x, int y, int element)
2283 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2284 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2287 void DrawScreenField(int x, int y)
2289 int lx = LEVELX(x), ly = LEVELY(y);
2290 int element, content;
2292 if (!IN_LEV_FIELD(lx, ly))
2294 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2297 element = getBorderElement(lx, ly);
2299 DrawScreenElement(x, y, element);
2304 element = Feld[lx][ly];
2305 content = Store[lx][ly];
2307 if (IS_MOVING(lx, ly))
2309 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2310 boolean cut_mode = NO_CUTTING;
2312 if (element == EL_QUICKSAND_EMPTYING ||
2313 element == EL_QUICKSAND_FAST_EMPTYING ||
2314 element == EL_MAGIC_WALL_EMPTYING ||
2315 element == EL_BD_MAGIC_WALL_EMPTYING ||
2316 element == EL_DC_MAGIC_WALL_EMPTYING ||
2317 element == EL_AMOEBA_DROPPING)
2318 cut_mode = CUT_ABOVE;
2319 else if (element == EL_QUICKSAND_FILLING ||
2320 element == EL_QUICKSAND_FAST_FILLING ||
2321 element == EL_MAGIC_WALL_FILLING ||
2322 element == EL_BD_MAGIC_WALL_FILLING ||
2323 element == EL_DC_MAGIC_WALL_FILLING)
2324 cut_mode = CUT_BELOW;
2326 if (cut_mode == CUT_ABOVE)
2327 DrawScreenElement(x, y, element);
2329 DrawScreenElement(x, y, EL_EMPTY);
2332 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2333 else if (cut_mode == NO_CUTTING)
2334 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2337 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2339 if (cut_mode == CUT_BELOW &&
2340 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2341 DrawLevelElement(lx, ly + 1, element);
2344 if (content == EL_ACID)
2346 int dir = MovDir[lx][ly];
2347 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2348 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2350 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2352 // prevent target field from being drawn again (but without masking)
2353 // (this would happen if target field is scanned after moving element)
2354 Stop[newlx][newly] = TRUE;
2357 else if (IS_BLOCKED(lx, ly))
2362 boolean cut_mode = NO_CUTTING;
2363 int element_old, content_old;
2365 Blocked2Moving(lx, ly, &oldx, &oldy);
2368 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2369 MovDir[oldx][oldy] == MV_RIGHT);
2371 element_old = Feld[oldx][oldy];
2372 content_old = Store[oldx][oldy];
2374 if (element_old == EL_QUICKSAND_EMPTYING ||
2375 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2376 element_old == EL_MAGIC_WALL_EMPTYING ||
2377 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2378 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2379 element_old == EL_AMOEBA_DROPPING)
2380 cut_mode = CUT_ABOVE;
2382 DrawScreenElement(x, y, EL_EMPTY);
2385 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2387 else if (cut_mode == NO_CUTTING)
2388 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2391 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2394 else if (IS_DRAWABLE(element))
2395 DrawScreenElement(x, y, element);
2397 DrawScreenElement(x, y, EL_EMPTY);
2400 void DrawLevelField(int x, int y)
2402 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2403 DrawScreenField(SCREENX(x), SCREENY(y));
2404 else if (IS_MOVING(x, y))
2408 Moving2Blocked(x, y, &newx, &newy);
2409 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2410 DrawScreenField(SCREENX(newx), SCREENY(newy));
2412 else if (IS_BLOCKED(x, y))
2416 Blocked2Moving(x, y, &oldx, &oldy);
2417 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2418 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2422 void DrawSizedElement(int x, int y, int element, int tilesize)
2426 graphic = el2edimg(element);
2427 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2430 void DrawMiniElement(int x, int y, int element)
2434 graphic = el2edimg(element);
2435 DrawMiniGraphic(x, y, graphic);
2438 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2441 int x = sx + scroll_x, y = sy + scroll_y;
2443 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2444 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2445 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2446 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2448 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2451 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2453 int x = sx + scroll_x, y = sy + scroll_y;
2455 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2456 DrawMiniElement(sx, sy, EL_EMPTY);
2457 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2458 DrawMiniElement(sx, sy, Feld[x][y]);
2460 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2463 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2464 int x, int y, int xsize, int ysize,
2465 int tile_width, int tile_height)
2469 int dst_x = startx + x * tile_width;
2470 int dst_y = starty + y * tile_height;
2471 int width = graphic_info[graphic].width;
2472 int height = graphic_info[graphic].height;
2473 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2474 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2475 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2476 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2477 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2478 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2479 boolean draw_masked = graphic_info[graphic].draw_masked;
2481 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2483 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2485 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2489 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2490 inner_sx + (x - 1) * tile_width % inner_width);
2491 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2492 inner_sy + (y - 1) * tile_height % inner_height);
2495 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2498 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2502 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2503 int x, int y, int xsize, int ysize, int font_nr)
2505 int font_width = getFontWidth(font_nr);
2506 int font_height = getFontHeight(font_nr);
2508 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2509 font_width, font_height);
2512 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2514 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2515 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2516 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2517 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2518 boolean no_delay = (tape.warp_forward);
2519 unsigned int anim_delay = 0;
2520 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2521 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2522 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2523 int font_width = getFontWidth(font_nr);
2524 int font_height = getFontHeight(font_nr);
2525 int max_xsize = level.envelope[envelope_nr].xsize;
2526 int max_ysize = level.envelope[envelope_nr].ysize;
2527 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2528 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2529 int xend = max_xsize;
2530 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2531 int xstep = (xstart < xend ? 1 : 0);
2532 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2534 int end = MAX(xend - xstart, yend - ystart);
2537 for (i = start; i <= end; i++)
2539 int last_frame = end; // last frame of this "for" loop
2540 int x = xstart + i * xstep;
2541 int y = ystart + i * ystep;
2542 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2543 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2544 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2545 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2548 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2550 BlitScreenToBitmap(backbuffer);
2552 SetDrawtoField(DRAW_TO_BACKBUFFER);
2554 for (yy = 0; yy < ysize; yy++)
2555 for (xx = 0; xx < xsize; xx++)
2556 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2558 DrawTextBuffer(sx + font_width, sy + font_height,
2559 level.envelope[envelope_nr].text, font_nr, max_xsize,
2560 xsize - 2, ysize - 2, 0, mask_mode,
2561 level.envelope[envelope_nr].autowrap,
2562 level.envelope[envelope_nr].centered, FALSE);
2564 redraw_mask |= REDRAW_FIELD;
2567 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2571 void ShowEnvelope(int envelope_nr)
2573 int element = EL_ENVELOPE_1 + envelope_nr;
2574 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2575 int sound_opening = element_info[element].sound[ACTION_OPENING];
2576 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2577 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2578 boolean no_delay = (tape.warp_forward);
2579 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2580 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2581 int anim_mode = graphic_info[graphic].anim_mode;
2582 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2583 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2585 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2587 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2589 if (anim_mode == ANIM_DEFAULT)
2590 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2592 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2595 Delay(wait_delay_value);
2597 WaitForEventToContinue();
2599 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2601 if (anim_mode != ANIM_NONE)
2602 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2604 if (anim_mode == ANIM_DEFAULT)
2605 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2607 game.envelope_active = FALSE;
2609 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2611 redraw_mask |= REDRAW_FIELD;
2615 static void setRequestBasePosition(int *x, int *y)
2617 int sx_base, sy_base;
2619 if (request.x != -1)
2620 sx_base = request.x;
2621 else if (request.align == ALIGN_LEFT)
2623 else if (request.align == ALIGN_RIGHT)
2624 sx_base = SX + SXSIZE;
2626 sx_base = SX + SXSIZE / 2;
2628 if (request.y != -1)
2629 sy_base = request.y;
2630 else if (request.valign == VALIGN_TOP)
2632 else if (request.valign == VALIGN_BOTTOM)
2633 sy_base = SY + SYSIZE;
2635 sy_base = SY + SYSIZE / 2;
2641 static void setRequestPositionExt(int *x, int *y, int width, int height,
2642 boolean add_border_size)
2644 int border_size = request.border_size;
2645 int sx_base, sy_base;
2648 setRequestBasePosition(&sx_base, &sy_base);
2650 if (request.align == ALIGN_LEFT)
2652 else if (request.align == ALIGN_RIGHT)
2653 sx = sx_base - width;
2655 sx = sx_base - width / 2;
2657 if (request.valign == VALIGN_TOP)
2659 else if (request.valign == VALIGN_BOTTOM)
2660 sy = sy_base - height;
2662 sy = sy_base - height / 2;
2664 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2665 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2667 if (add_border_size)
2677 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2679 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2682 void DrawEnvelopeRequest(char *text)
2684 char *text_final = text;
2685 char *text_door_style = NULL;
2686 int graphic = IMG_BACKGROUND_REQUEST;
2687 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2688 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2689 int font_nr = FONT_REQUEST;
2690 int font_width = getFontWidth(font_nr);
2691 int font_height = getFontHeight(font_nr);
2692 int border_size = request.border_size;
2693 int line_spacing = request.line_spacing;
2694 int line_height = font_height + line_spacing;
2695 int max_text_width = request.width - 2 * border_size;
2696 int max_text_height = request.height - 2 * border_size;
2697 int line_length = max_text_width / font_width;
2698 int max_lines = max_text_height / line_height;
2699 int text_width = line_length * font_width;
2700 int width = request.width;
2701 int height = request.height;
2702 int tile_size = MAX(request.step_offset, 1);
2703 int x_steps = width / tile_size;
2704 int y_steps = height / tile_size;
2705 int sx_offset = border_size;
2706 int sy_offset = border_size;
2710 if (request.centered)
2711 sx_offset = (request.width - text_width) / 2;
2713 if (request.wrap_single_words && !request.autowrap)
2715 char *src_text_ptr, *dst_text_ptr;
2717 text_door_style = checked_malloc(2 * strlen(text) + 1);
2719 src_text_ptr = text;
2720 dst_text_ptr = text_door_style;
2722 while (*src_text_ptr)
2724 if (*src_text_ptr == ' ' ||
2725 *src_text_ptr == '?' ||
2726 *src_text_ptr == '!')
2727 *dst_text_ptr++ = '\n';
2729 if (*src_text_ptr != ' ')
2730 *dst_text_ptr++ = *src_text_ptr;
2735 *dst_text_ptr = '\0';
2737 text_final = text_door_style;
2740 setRequestPosition(&sx, &sy, FALSE);
2742 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2744 for (y = 0; y < y_steps; y++)
2745 for (x = 0; x < x_steps; x++)
2746 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2747 x, y, x_steps, y_steps,
2748 tile_size, tile_size);
2750 /* force DOOR font inside door area */
2751 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2753 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2754 line_length, -1, max_lines, line_spacing, mask_mode,
2755 request.autowrap, request.centered, FALSE);
2759 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2760 RedrawGadget(tool_gadget[i]);
2762 // store readily prepared envelope request for later use when animating
2763 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2765 if (text_door_style)
2766 free(text_door_style);
2769 void AnimateEnvelopeRequest(int anim_mode, int action)
2771 int graphic = IMG_BACKGROUND_REQUEST;
2772 boolean draw_masked = graphic_info[graphic].draw_masked;
2773 int delay_value_normal = request.step_delay;
2774 int delay_value_fast = delay_value_normal / 2;
2775 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2776 boolean no_delay = (tape.warp_forward);
2777 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2778 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2779 unsigned int anim_delay = 0;
2781 int tile_size = MAX(request.step_offset, 1);
2782 int max_xsize = request.width / tile_size;
2783 int max_ysize = request.height / tile_size;
2784 int max_xsize_inner = max_xsize - 2;
2785 int max_ysize_inner = max_ysize - 2;
2787 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2788 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2789 int xend = max_xsize_inner;
2790 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2791 int xstep = (xstart < xend ? 1 : 0);
2792 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2794 int end = MAX(xend - xstart, yend - ystart);
2797 if (setup.quick_doors)
2804 for (i = start; i <= end; i++)
2806 int last_frame = end; // last frame of this "for" loop
2807 int x = xstart + i * xstep;
2808 int y = ystart + i * ystep;
2809 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2810 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2811 int xsize_size_left = (xsize - 1) * tile_size;
2812 int ysize_size_top = (ysize - 1) * tile_size;
2813 int max_xsize_pos = (max_xsize - 1) * tile_size;
2814 int max_ysize_pos = (max_ysize - 1) * tile_size;
2815 int width = xsize * tile_size;
2816 int height = ysize * tile_size;
2821 setRequestPosition(&src_x, &src_y, FALSE);
2822 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2824 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2826 for (yy = 0; yy < 2; yy++)
2828 for (xx = 0; xx < 2; xx++)
2830 int src_xx = src_x + xx * max_xsize_pos;
2831 int src_yy = src_y + yy * max_ysize_pos;
2832 int dst_xx = dst_x + xx * xsize_size_left;
2833 int dst_yy = dst_y + yy * ysize_size_top;
2834 int xx_size = (xx ? tile_size : xsize_size_left);
2835 int yy_size = (yy ? tile_size : ysize_size_top);
2838 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
2839 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2841 BlitBitmap(bitmap_db_store_2, backbuffer,
2842 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2846 redraw_mask |= REDRAW_FIELD;
2850 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2854 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2856 int graphic = IMG_BACKGROUND_REQUEST;
2857 int sound_opening = SND_REQUEST_OPENING;
2858 int sound_closing = SND_REQUEST_CLOSING;
2859 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2860 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2861 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2862 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2863 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2865 if (game_status == GAME_MODE_PLAYING)
2866 BlitScreenToBitmap(backbuffer);
2868 SetDrawtoField(DRAW_TO_BACKBUFFER);
2870 // SetDrawBackgroundMask(REDRAW_NONE);
2872 if (action == ACTION_OPENING)
2874 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2876 if (req_state & REQ_ASK)
2878 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2879 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2881 else if (req_state & REQ_CONFIRM)
2883 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2885 else if (req_state & REQ_PLAYER)
2887 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2888 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2889 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2890 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2893 DrawEnvelopeRequest(text);
2896 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2898 if (action == ACTION_OPENING)
2900 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2902 if (anim_mode == ANIM_DEFAULT)
2903 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2905 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2909 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2911 if (anim_mode != ANIM_NONE)
2912 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2914 if (anim_mode == ANIM_DEFAULT)
2915 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2918 game.envelope_active = FALSE;
2920 if (action == ACTION_CLOSING)
2921 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2923 // SetDrawBackgroundMask(last_draw_background_mask);
2925 redraw_mask |= REDRAW_FIELD;
2929 if (action == ACTION_CLOSING &&
2930 game_status == GAME_MODE_PLAYING &&
2931 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2932 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2935 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2939 int graphic = el2preimg(element);
2941 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2942 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2945 void DrawLevel(int draw_background_mask)
2949 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2950 SetDrawBackgroundMask(draw_background_mask);
2954 for (x = BX1; x <= BX2; x++)
2955 for (y = BY1; y <= BY2; y++)
2956 DrawScreenField(x, y);
2958 redraw_mask |= REDRAW_FIELD;
2961 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2966 for (x = 0; x < size_x; x++)
2967 for (y = 0; y < size_y; y++)
2968 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2970 redraw_mask |= REDRAW_FIELD;
2973 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2977 for (x = 0; x < size_x; x++)
2978 for (y = 0; y < size_y; y++)
2979 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2981 redraw_mask |= REDRAW_FIELD;
2984 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2986 boolean show_level_border = (BorderElement != EL_EMPTY);
2987 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2988 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2989 int tile_size = preview.tile_size;
2990 int preview_width = preview.xsize * tile_size;
2991 int preview_height = preview.ysize * tile_size;
2992 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2993 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2994 int real_preview_width = real_preview_xsize * tile_size;
2995 int real_preview_height = real_preview_ysize * tile_size;
2996 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2997 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
3000 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
3003 DrawBackground(dst_x, dst_y, preview_width, preview_height);
3005 dst_x += (preview_width - real_preview_width) / 2;
3006 dst_y += (preview_height - real_preview_height) / 2;
3008 for (x = 0; x < real_preview_xsize; x++)
3010 for (y = 0; y < real_preview_ysize; y++)
3012 int lx = from_x + x + (show_level_border ? -1 : 0);
3013 int ly = from_y + y + (show_level_border ? -1 : 0);
3014 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
3015 getBorderElement(lx, ly));
3017 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
3018 element, tile_size);
3022 redraw_mask |= REDRAW_FIELD;
3025 #define MICROLABEL_EMPTY 0
3026 #define MICROLABEL_LEVEL_NAME 1
3027 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3028 #define MICROLABEL_LEVEL_AUTHOR 3
3029 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3030 #define MICROLABEL_IMPORTED_FROM 5
3031 #define MICROLABEL_IMPORTED_BY_HEAD 6
3032 #define MICROLABEL_IMPORTED_BY 7
3034 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3036 int max_text_width = SXSIZE;
3037 int font_width = getFontWidth(font_nr);
3039 if (pos->align == ALIGN_CENTER)
3040 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3041 else if (pos->align == ALIGN_RIGHT)
3042 max_text_width = pos->x;
3044 max_text_width = SXSIZE - pos->x;
3046 return max_text_width / font_width;
3049 static void DrawPreviewLevelLabelExt(int mode)
3051 struct TextPosInfo *pos = &menu.main.text.level_info_2;
3052 char label_text[MAX_OUTPUT_LINESIZE + 1];
3053 int max_len_label_text;
3054 int font_nr = pos->font;
3057 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3060 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3061 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3062 mode == MICROLABEL_IMPORTED_BY_HEAD)
3063 font_nr = pos->font_alt;
3065 max_len_label_text = getMaxTextLength(pos, font_nr);
3067 if (pos->size != -1)
3068 max_len_label_text = pos->size;
3070 for (i = 0; i < max_len_label_text; i++)
3071 label_text[i] = ' ';
3072 label_text[max_len_label_text] = '\0';
3074 if (strlen(label_text) > 0)
3075 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3078 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3079 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3080 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3081 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3082 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3083 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3084 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3085 max_len_label_text);
3086 label_text[max_len_label_text] = '\0';
3088 if (strlen(label_text) > 0)
3089 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3091 redraw_mask |= REDRAW_FIELD;
3094 static void DrawPreviewLevelExt(boolean restart)
3096 static unsigned int scroll_delay = 0;
3097 static unsigned int label_delay = 0;
3098 static int from_x, from_y, scroll_direction;
3099 static int label_state, label_counter;
3100 unsigned int scroll_delay_value = preview.step_delay;
3101 boolean show_level_border = (BorderElement != EL_EMPTY);
3102 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3103 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3110 if (preview.anim_mode == ANIM_CENTERED)
3112 if (level_xsize > preview.xsize)
3113 from_x = (level_xsize - preview.xsize) / 2;
3114 if (level_ysize > preview.ysize)
3115 from_y = (level_ysize - preview.ysize) / 2;
3118 from_x += preview.xoffset;
3119 from_y += preview.yoffset;
3121 scroll_direction = MV_RIGHT;
3125 DrawPreviewLevelPlayfieldExt(from_x, from_y);
3126 DrawPreviewLevelLabelExt(label_state);
3128 /* initialize delay counters */
3129 DelayReached(&scroll_delay, 0);
3130 DelayReached(&label_delay, 0);
3132 if (leveldir_current->name)
3134 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3135 char label_text[MAX_OUTPUT_LINESIZE + 1];
3136 int font_nr = pos->font;
3137 int max_len_label_text = getMaxTextLength(pos, font_nr);
3139 if (pos->size != -1)
3140 max_len_label_text = pos->size;
3142 strncpy(label_text, leveldir_current->name, max_len_label_text);
3143 label_text[max_len_label_text] = '\0';
3145 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3146 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3152 /* scroll preview level, if needed */
3153 if (preview.anim_mode != ANIM_NONE &&
3154 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3155 DelayReached(&scroll_delay, scroll_delay_value))
3157 switch (scroll_direction)
3162 from_x -= preview.step_offset;
3163 from_x = (from_x < 0 ? 0 : from_x);
3166 scroll_direction = MV_UP;
3170 if (from_x < level_xsize - preview.xsize)
3172 from_x += preview.step_offset;
3173 from_x = (from_x > level_xsize - preview.xsize ?
3174 level_xsize - preview.xsize : from_x);
3177 scroll_direction = MV_DOWN;
3183 from_y -= preview.step_offset;
3184 from_y = (from_y < 0 ? 0 : from_y);
3187 scroll_direction = MV_RIGHT;
3191 if (from_y < level_ysize - preview.ysize)
3193 from_y += preview.step_offset;
3194 from_y = (from_y > level_ysize - preview.ysize ?
3195 level_ysize - preview.ysize : from_y);
3198 scroll_direction = MV_LEFT;
3205 DrawPreviewLevelPlayfieldExt(from_x, from_y);
3208 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
3209 /* redraw micro level label, if needed */
3210 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3211 !strEqual(level.author, ANONYMOUS_NAME) &&
3212 !strEqual(level.author, leveldir_current->name) &&
3213 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3215 int max_label_counter = 23;
3217 if (leveldir_current->imported_from != NULL &&
3218 strlen(leveldir_current->imported_from) > 0)
3219 max_label_counter += 14;
3220 if (leveldir_current->imported_by != NULL &&
3221 strlen(leveldir_current->imported_by) > 0)
3222 max_label_counter += 14;
3224 label_counter = (label_counter + 1) % max_label_counter;
3225 label_state = (label_counter >= 0 && label_counter <= 7 ?
3226 MICROLABEL_LEVEL_NAME :
3227 label_counter >= 9 && label_counter <= 12 ?
3228 MICROLABEL_LEVEL_AUTHOR_HEAD :
3229 label_counter >= 14 && label_counter <= 21 ?
3230 MICROLABEL_LEVEL_AUTHOR :
3231 label_counter >= 23 && label_counter <= 26 ?
3232 MICROLABEL_IMPORTED_FROM_HEAD :
3233 label_counter >= 28 && label_counter <= 35 ?
3234 MICROLABEL_IMPORTED_FROM :
3235 label_counter >= 37 && label_counter <= 40 ?
3236 MICROLABEL_IMPORTED_BY_HEAD :
3237 label_counter >= 42 && label_counter <= 49 ?
3238 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3240 if (leveldir_current->imported_from == NULL &&
3241 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3242 label_state == MICROLABEL_IMPORTED_FROM))
3243 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3244 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3246 DrawPreviewLevelLabelExt(label_state);
3250 void DrawPreviewLevelInitial()
3252 DrawPreviewLevelExt(TRUE);
3255 void DrawPreviewLevelAnimation()
3257 DrawPreviewLevelExt(FALSE);
3260 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3261 int graphic, int sync_frame,
3264 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3266 if (mask_mode == USE_MASKING)
3267 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3269 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3272 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3273 int graphic, int sync_frame, int mask_mode)
3275 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3277 if (mask_mode == USE_MASKING)
3278 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3280 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3283 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3285 int lx = LEVELX(x), ly = LEVELY(y);
3287 if (!IN_SCR_FIELD(x, y))
3290 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3291 graphic, GfxFrame[lx][ly], NO_MASKING);
3293 MarkTileDirty(x, y);
3296 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3298 int lx = LEVELX(x), ly = LEVELY(y);
3300 if (!IN_SCR_FIELD(x, y))
3303 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3304 graphic, GfxFrame[lx][ly], NO_MASKING);
3305 MarkTileDirty(x, y);
3308 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3310 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3313 void DrawLevelElementAnimation(int x, int y, int element)
3315 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3317 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3320 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3322 int sx = SCREENX(x), sy = SCREENY(y);
3324 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3327 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3330 DrawGraphicAnimation(sx, sy, graphic);
3333 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3334 DrawLevelFieldCrumbled(x, y);
3336 if (GFX_CRUMBLED(Feld[x][y]))
3337 DrawLevelFieldCrumbled(x, y);
3341 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3343 int sx = SCREENX(x), sy = SCREENY(y);
3346 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3349 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3351 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3354 DrawGraphicAnimation(sx, sy, graphic);
3356 if (GFX_CRUMBLED(element))
3357 DrawLevelFieldCrumbled(x, y);
3360 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3362 if (player->use_murphy)
3364 /* this works only because currently only one player can be "murphy" ... */
3365 static int last_horizontal_dir = MV_LEFT;
3366 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3368 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3369 last_horizontal_dir = move_dir;
3371 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3373 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3375 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3381 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3384 static boolean equalGraphics(int graphic1, int graphic2)
3386 struct GraphicInfo *g1 = &graphic_info[graphic1];
3387 struct GraphicInfo *g2 = &graphic_info[graphic2];
3389 return (g1->bitmap == g2->bitmap &&
3390 g1->src_x == g2->src_x &&
3391 g1->src_y == g2->src_y &&
3392 g1->anim_frames == g2->anim_frames &&
3393 g1->anim_delay == g2->anim_delay &&
3394 g1->anim_mode == g2->anim_mode);
3397 void DrawAllPlayers()
3401 for (i = 0; i < MAX_PLAYERS; i++)
3402 if (stored_player[i].active)
3403 DrawPlayer(&stored_player[i]);
3406 void DrawPlayerField(int x, int y)
3408 if (!IS_PLAYER(x, y))
3411 DrawPlayer(PLAYERINFO(x, y));
3414 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3416 void DrawPlayer(struct PlayerInfo *player)
3418 int jx = player->jx;
3419 int jy = player->jy;
3420 int move_dir = player->MovDir;
3421 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3422 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3423 int last_jx = (player->is_moving ? jx - dx : jx);
3424 int last_jy = (player->is_moving ? jy - dy : jy);
3425 int next_jx = jx + dx;
3426 int next_jy = jy + dy;
3427 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3428 boolean player_is_opaque = FALSE;
3429 int sx = SCREENX(jx), sy = SCREENY(jy);
3430 int sxx = 0, syy = 0;
3431 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3433 int action = ACTION_DEFAULT;
3434 int last_player_graphic = getPlayerGraphic(player, move_dir);
3435 int last_player_frame = player->Frame;
3438 /* GfxElement[][] is set to the element the player is digging or collecting;
3439 remove also for off-screen player if the player is not moving anymore */
3440 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3441 GfxElement[jx][jy] = EL_UNDEFINED;
3443 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3447 if (!IN_LEV_FIELD(jx, jy))
3449 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3450 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3451 printf("DrawPlayerField(): This should never happen!\n");
3456 if (element == EL_EXPLOSION)
3459 action = (player->is_pushing ? ACTION_PUSHING :
3460 player->is_digging ? ACTION_DIGGING :
3461 player->is_collecting ? ACTION_COLLECTING :
3462 player->is_moving ? ACTION_MOVING :
3463 player->is_snapping ? ACTION_SNAPPING :
3464 player->is_dropping ? ACTION_DROPPING :
3465 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3467 if (player->is_waiting)
3468 move_dir = player->dir_waiting;
3470 InitPlayerGfxAnimation(player, action, move_dir);
3472 /* ----------------------------------------------------------------------- */
3473 /* draw things in the field the player is leaving, if needed */
3474 /* ----------------------------------------------------------------------- */
3476 if (player->is_moving)
3478 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3480 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3482 if (last_element == EL_DYNAMITE_ACTIVE ||
3483 last_element == EL_EM_DYNAMITE_ACTIVE ||
3484 last_element == EL_SP_DISK_RED_ACTIVE)
3485 DrawDynamite(last_jx, last_jy);
3487 DrawLevelFieldThruMask(last_jx, last_jy);
3489 else if (last_element == EL_DYNAMITE_ACTIVE ||
3490 last_element == EL_EM_DYNAMITE_ACTIVE ||
3491 last_element == EL_SP_DISK_RED_ACTIVE)
3492 DrawDynamite(last_jx, last_jy);
3494 /* !!! this is not enough to prevent flickering of players which are
3495 moving next to each others without a free tile between them -- this
3496 can only be solved by drawing all players layer by layer (first the
3497 background, then the foreground etc.) !!! => TODO */
3498 else if (!IS_PLAYER(last_jx, last_jy))
3499 DrawLevelField(last_jx, last_jy);
3502 DrawLevelField(last_jx, last_jy);
3505 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3506 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3509 if (!IN_SCR_FIELD(sx, sy))
3512 /* ----------------------------------------------------------------------- */
3513 /* draw things behind the player, if needed */
3514 /* ----------------------------------------------------------------------- */
3517 DrawLevelElement(jx, jy, Back[jx][jy]);
3518 else if (IS_ACTIVE_BOMB(element))
3519 DrawLevelElement(jx, jy, EL_EMPTY);
3522 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3524 int old_element = GfxElement[jx][jy];
3525 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3526 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3528 if (GFX_CRUMBLED(old_element))
3529 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3531 DrawGraphic(sx, sy, old_graphic, frame);
3533 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3534 player_is_opaque = TRUE;
3538 GfxElement[jx][jy] = EL_UNDEFINED;
3540 /* make sure that pushed elements are drawn with correct frame rate */
3541 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3543 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3544 GfxFrame[jx][jy] = player->StepFrame;
3546 DrawLevelField(jx, jy);
3550 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3551 /* ----------------------------------------------------------------------- */
3552 /* draw player himself */
3553 /* ----------------------------------------------------------------------- */
3555 graphic = getPlayerGraphic(player, move_dir);
3557 /* in the case of changed player action or direction, prevent the current
3558 animation frame from being restarted for identical animations */
3559 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3560 player->Frame = last_player_frame;
3562 frame = getGraphicAnimationFrame(graphic, player->Frame);
3566 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3567 sxx = player->GfxPos;
3569 syy = player->GfxPos;
3572 if (player_is_opaque)
3573 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3575 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3577 if (SHIELD_ON(player))
3579 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3580 IMG_SHIELD_NORMAL_ACTIVE);
3581 int frame = getGraphicAnimationFrame(graphic, -1);
3583 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3587 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3590 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3591 sxx = player->GfxPos;
3593 syy = player->GfxPos;
3597 /* ----------------------------------------------------------------------- */
3598 /* draw things the player is pushing, if needed */
3599 /* ----------------------------------------------------------------------- */
3601 if (player->is_pushing && player->is_moving)
3603 int px = SCREENX(jx), py = SCREENY(jy);
3604 int pxx = (TILEX - ABS(sxx)) * dx;
3605 int pyy = (TILEY - ABS(syy)) * dy;
3606 int gfx_frame = GfxFrame[jx][jy];
3612 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3614 element = Feld[next_jx][next_jy];
3615 gfx_frame = GfxFrame[next_jx][next_jy];
3618 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3620 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3621 frame = getGraphicAnimationFrame(graphic, sync_frame);
3623 /* draw background element under pushed element (like the Sokoban field) */
3624 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3626 /* this allows transparent pushing animation over non-black background */
3629 DrawLevelElement(jx, jy, Back[jx][jy]);
3631 DrawLevelElement(jx, jy, EL_EMPTY);
3633 if (Back[next_jx][next_jy])
3634 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3636 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3638 else if (Back[next_jx][next_jy])
3639 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3642 /* do not draw (EM style) pushing animation when pushing is finished */
3643 /* (two-tile animations usually do not contain start and end frame) */
3644 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3645 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3647 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3649 /* masked drawing is needed for EMC style (double) movement graphics */
3650 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3651 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3655 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3656 /* ----------------------------------------------------------------------- */
3657 /* draw player himself */
3658 /* ----------------------------------------------------------------------- */
3660 graphic = getPlayerGraphic(player, move_dir);
3662 /* in the case of changed player action or direction, prevent the current
3663 animation frame from being restarted for identical animations */
3664 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3665 player->Frame = last_player_frame;
3667 frame = getGraphicAnimationFrame(graphic, player->Frame);
3671 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3672 sxx = player->GfxPos;
3674 syy = player->GfxPos;
3677 if (player_is_opaque)
3678 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3680 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3682 if (SHIELD_ON(player))
3684 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3685 IMG_SHIELD_NORMAL_ACTIVE);
3686 int frame = getGraphicAnimationFrame(graphic, -1);
3688 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3692 /* ----------------------------------------------------------------------- */
3693 /* draw things in front of player (active dynamite or dynabombs) */
3694 /* ----------------------------------------------------------------------- */
3696 if (IS_ACTIVE_BOMB(element))
3698 graphic = el2img(element);
3699 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3701 if (game.emulation == EMU_SUPAPLEX)
3702 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3704 DrawGraphicThruMask(sx, sy, graphic, frame);
3707 if (player_is_moving && last_element == EL_EXPLOSION)
3709 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3710 GfxElement[last_jx][last_jy] : EL_EMPTY);
3711 int graphic = el_act2img(element, ACTION_EXPLODING);
3712 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3713 int phase = ExplodePhase[last_jx][last_jy] - 1;
3714 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3717 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3720 /* ----------------------------------------------------------------------- */
3721 /* draw elements the player is just walking/passing through/under */
3722 /* ----------------------------------------------------------------------- */
3724 if (player_is_moving)
3726 /* handle the field the player is leaving ... */
3727 if (IS_ACCESSIBLE_INSIDE(last_element))
3728 DrawLevelField(last_jx, last_jy);
3729 else if (IS_ACCESSIBLE_UNDER(last_element))
3730 DrawLevelFieldThruMask(last_jx, last_jy);
3733 /* do not redraw accessible elements if the player is just pushing them */
3734 if (!player_is_moving || !player->is_pushing)
3736 /* ... and the field the player is entering */
3737 if (IS_ACCESSIBLE_INSIDE(element))
3738 DrawLevelField(jx, jy);
3739 else if (IS_ACCESSIBLE_UNDER(element))
3740 DrawLevelFieldThruMask(jx, jy);
3743 MarkTileDirty(sx, sy);
3746 /* ------------------------------------------------------------------------- */
3748 void WaitForEventToContinue()
3750 boolean still_wait = TRUE;
3752 if (program.headless)
3755 /* simulate releasing mouse button over last gadget, if still pressed */
3757 HandleGadgets(-1, -1, 0);
3759 button_status = MB_RELEASED;
3773 case EVENT_BUTTONPRESS:
3774 case EVENT_KEYPRESS:
3775 #if defined(TARGET_SDL2)
3776 case SDL_CONTROLLERBUTTONDOWN:
3778 case SDL_JOYBUTTONDOWN:
3782 case EVENT_KEYRELEASE:
3783 ClearPlayerAction();
3787 HandleOtherEvents(&event);
3791 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3800 #define MAX_REQUEST_LINES 13
3801 #define MAX_REQUEST_LINE_FONT1_LEN 7
3802 #define MAX_REQUEST_LINE_FONT2_LEN 10
3804 static int RequestHandleEvents(unsigned int req_state)
3806 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3807 local_player->LevelSolved_GameEnd);
3808 int width = request.width;
3809 int height = request.height;
3813 setRequestPosition(&sx, &sy, FALSE);
3815 button_status = MB_RELEASED;
3817 request_gadget_id = -1;
3824 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3826 HandleGameActions();
3828 SetDrawtoField(DRAW_TO_BACKBUFFER);
3830 if (global.use_envelope_request)
3832 /* copy current state of request area to middle of playfield area */
3833 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
3841 while (NextValidEvent(&event))
3845 case EVENT_BUTTONPRESS:
3846 case EVENT_BUTTONRELEASE:
3847 case EVENT_MOTIONNOTIFY:
3851 if (event.type == EVENT_MOTIONNOTIFY)
3856 motion_status = TRUE;
3857 mx = ((MotionEvent *) &event)->x;
3858 my = ((MotionEvent *) &event)->y;
3862 motion_status = FALSE;
3863 mx = ((ButtonEvent *) &event)->x;
3864 my = ((ButtonEvent *) &event)->y;
3865 if (event.type == EVENT_BUTTONPRESS)
3866 button_status = ((ButtonEvent *) &event)->button;
3868 button_status = MB_RELEASED;
3871 /* this sets 'request_gadget_id' */
3872 HandleGadgets(mx, my, button_status);
3874 switch (request_gadget_id)
3876 case TOOL_CTRL_ID_YES:
3879 case TOOL_CTRL_ID_NO:
3882 case TOOL_CTRL_ID_CONFIRM:
3883 result = TRUE | FALSE;
3886 case TOOL_CTRL_ID_PLAYER_1:
3889 case TOOL_CTRL_ID_PLAYER_2:
3892 case TOOL_CTRL_ID_PLAYER_3:
3895 case TOOL_CTRL_ID_PLAYER_4:
3906 #if defined(TARGET_SDL2)
3907 case SDL_WINDOWEVENT:
3908 HandleWindowEvent((WindowEvent *) &event);
3911 case SDL_APP_WILLENTERBACKGROUND:
3912 case SDL_APP_DIDENTERBACKGROUND:
3913 case SDL_APP_WILLENTERFOREGROUND:
3914 case SDL_APP_DIDENTERFOREGROUND:
3915 HandlePauseResumeEvent((PauseResumeEvent *) &event);
3919 case EVENT_KEYPRESS:
3921 Key key = GetEventKey((KeyEvent *)&event, TRUE);
3926 if (req_state & REQ_CONFIRM)
3931 #if defined(TARGET_SDL2)
3934 #if defined(KSYM_Rewind)
3935 case KSYM_Rewind: /* for Amazon Fire TV remote */
3942 #if defined(TARGET_SDL2)
3944 #if defined(KSYM_FastForward)
3945 case KSYM_FastForward: /* for Amazon Fire TV remote */
3952 HandleKeysDebug(key);
3956 if (req_state & REQ_PLAYER)
3962 case EVENT_KEYRELEASE:
3963 ClearPlayerAction();
3966 #if defined(TARGET_SDL2)
3967 case SDL_CONTROLLERBUTTONDOWN:
3968 switch (event.cbutton.button)
3970 case SDL_CONTROLLER_BUTTON_A:
3971 case SDL_CONTROLLER_BUTTON_X:
3972 case SDL_CONTROLLER_BUTTON_LEFTSHOULDER:
3976 case SDL_CONTROLLER_BUTTON_B:
3977 case SDL_CONTROLLER_BUTTON_Y:
3978 case SDL_CONTROLLER_BUTTON_RIGHTSHOULDER:
3979 case SDL_CONTROLLER_BUTTON_BACK:
3984 if (req_state & REQ_PLAYER)
3989 case SDL_CONTROLLERBUTTONUP:
3990 HandleJoystickEvent(&event);
3991 ClearPlayerAction();
3996 HandleOtherEvents(&event);
4001 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
4003 int joy = AnyJoystick();
4005 if (joy & JOY_BUTTON_1)
4007 else if (joy & JOY_BUTTON_2)
4013 if (global.use_envelope_request)
4015 /* copy back current state of pressed buttons inside request area */
4016 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
4026 static boolean RequestDoor(char *text, unsigned int req_state)
4028 unsigned int old_door_state;
4029 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
4030 int font_nr = FONT_TEXT_2;
4035 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
4037 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
4038 font_nr = FONT_TEXT_1;
4041 if (game_status == GAME_MODE_PLAYING)
4042 BlitScreenToBitmap(backbuffer);
4044 /* disable deactivated drawing when quick-loading level tape recording */
4045 if (tape.playing && tape.deactivate_display)
4046 TapeDeactivateDisplayOff(TRUE);
4048 SetMouseCursor(CURSOR_DEFAULT);
4050 #if defined(NETWORK_AVALIABLE)
4051 /* pause network game while waiting for request to answer */
4052 if (options.network &&
4053 game_status == GAME_MODE_PLAYING &&
4054 req_state & REQUEST_WAIT_FOR_INPUT)
4055 SendToServer_PausePlaying();
4058 old_door_state = GetDoorState();
4060 /* simulate releasing mouse button over last gadget, if still pressed */
4062 HandleGadgets(-1, -1, 0);
4066 /* draw released gadget before proceeding */
4069 if (old_door_state & DOOR_OPEN_1)
4071 CloseDoor(DOOR_CLOSE_1);
4073 /* save old door content */
4074 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4075 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4078 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4079 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4081 /* clear door drawing field */
4082 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4084 /* force DOOR font inside door area */
4085 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4087 /* write text for request */
4088 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4090 char text_line[max_request_line_len + 1];
4096 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4098 tc = *(text_ptr + tx);
4099 // if (!tc || tc == ' ')
4100 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4104 if ((tc == '?' || tc == '!') && tl == 0)
4114 strncpy(text_line, text_ptr, tl);
4117 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4118 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4119 text_line, font_nr);
4121 text_ptr += tl + (tc == ' ' ? 1 : 0);
4122 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4127 if (req_state & REQ_ASK)
4129 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4130 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4132 else if (req_state & REQ_CONFIRM)
4134 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4136 else if (req_state & REQ_PLAYER)
4138 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4139 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4140 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4141 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4144 /* copy request gadgets to door backbuffer */
4145 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4147 OpenDoor(DOOR_OPEN_1);
4149 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4151 if (game_status == GAME_MODE_PLAYING)
4153 SetPanelBackground();
4154 SetDrawBackgroundMask(REDRAW_DOOR_1);
4158 SetDrawBackgroundMask(REDRAW_FIELD);
4164 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4166 // ---------- handle request buttons ----------
4167 result = RequestHandleEvents(req_state);
4171 if (!(req_state & REQ_STAY_OPEN))
4173 CloseDoor(DOOR_CLOSE_1);
4175 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4176 (req_state & REQ_REOPEN))
4177 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4182 if (game_status == GAME_MODE_PLAYING)
4184 SetPanelBackground();
4185 SetDrawBackgroundMask(REDRAW_DOOR_1);
4189 SetDrawBackgroundMask(REDRAW_FIELD);
4192 #if defined(NETWORK_AVALIABLE)
4193 /* continue network game after request */
4194 if (options.network &&
4195 game_status == GAME_MODE_PLAYING &&
4196 req_state & REQUEST_WAIT_FOR_INPUT)
4197 SendToServer_ContinuePlaying();
4200 /* restore deactivated drawing when quick-loading level tape recording */
4201 if (tape.playing && tape.deactivate_display)
4202 TapeDeactivateDisplayOn();
4207 static boolean RequestEnvelope(char *text, unsigned int req_state)
4211 if (game_status == GAME_MODE_PLAYING)
4212 BlitScreenToBitmap(backbuffer);
4214 /* disable deactivated drawing when quick-loading level tape recording */
4215 if (tape.playing && tape.deactivate_display)
4216 TapeDeactivateDisplayOff(TRUE);
4218 SetMouseCursor(CURSOR_DEFAULT);
4220 #if defined(NETWORK_AVALIABLE)
4221 /* pause network game while waiting for request to answer */
4222 if (options.network &&
4223 game_status == GAME_MODE_PLAYING &&
4224 req_state & REQUEST_WAIT_FOR_INPUT)
4225 SendToServer_PausePlaying();
4228 /* simulate releasing mouse button over last gadget, if still pressed */
4230 HandleGadgets(-1, -1, 0);
4234 // (replace with setting corresponding request background)
4235 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4236 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4238 /* clear door drawing field */
4239 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4241 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4243 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4245 if (game_status == GAME_MODE_PLAYING)
4247 SetPanelBackground();
4248 SetDrawBackgroundMask(REDRAW_DOOR_1);
4252 SetDrawBackgroundMask(REDRAW_FIELD);
4258 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4260 // ---------- handle request buttons ----------
4261 result = RequestHandleEvents(req_state);
4265 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4269 if (game_status == GAME_MODE_PLAYING)
4271 SetPanelBackground();
4272 SetDrawBackgroundMask(REDRAW_DOOR_1);
4276 SetDrawBackgroundMask(REDRAW_FIELD);
4279 #if defined(NETWORK_AVALIABLE)
4280 /* continue network game after request */
4281 if (options.network &&
4282 game_status == GAME_MODE_PLAYING &&
4283 req_state & REQUEST_WAIT_FOR_INPUT)
4284 SendToServer_ContinuePlaying();
4287 /* restore deactivated drawing when quick-loading level tape recording */
4288 if (tape.playing && tape.deactivate_display)
4289 TapeDeactivateDisplayOn();
4294 boolean Request(char *text, unsigned int req_state)
4296 boolean overlay_active = GetOverlayActive();
4299 SetOverlayActive(FALSE);
4301 if (global.use_envelope_request)
4302 result = RequestEnvelope(text, req_state);
4304 result = RequestDoor(text, req_state);
4306 SetOverlayActive(overlay_active);
4311 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4313 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4314 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4317 if (dpo1->sort_priority != dpo2->sort_priority)
4318 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4320 compare_result = dpo1->nr - dpo2->nr;
4322 return compare_result;
4325 void InitGraphicCompatibilityInfo_Doors()
4331 struct DoorInfo *door;
4335 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4336 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4338 { -1, -1, -1, NULL }
4340 struct Rect door_rect_list[] =
4342 { DX, DY, DXSIZE, DYSIZE },
4343 { VX, VY, VXSIZE, VYSIZE }
4347 for (i = 0; doors[i].door_token != -1; i++)
4349 int door_token = doors[i].door_token;
4350 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4351 int part_1 = doors[i].part_1;
4352 int part_8 = doors[i].part_8;
4353 int part_2 = part_1 + 1;
4354 int part_3 = part_1 + 2;
4355 struct DoorInfo *door = doors[i].door;
4356 struct Rect *door_rect = &door_rect_list[door_index];
4357 boolean door_gfx_redefined = FALSE;
4359 /* check if any door part graphic definitions have been redefined */
4361 for (j = 0; door_part_controls[j].door_token != -1; j++)
4363 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4364 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4366 if (dpc->door_token == door_token && fi->redefined)
4367 door_gfx_redefined = TRUE;
4370 /* check for old-style door graphic/animation modifications */
4372 if (!door_gfx_redefined)
4374 if (door->anim_mode & ANIM_STATIC_PANEL)
4376 door->panel.step_xoffset = 0;
4377 door->panel.step_yoffset = 0;
4380 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4382 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4383 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4384 int num_door_steps, num_panel_steps;
4386 /* remove door part graphics other than the two default wings */
4388 for (j = 0; door_part_controls[j].door_token != -1; j++)
4390 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4391 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4393 if (dpc->graphic >= part_3 &&
4394 dpc->graphic <= part_8)
4398 /* set graphics and screen positions of the default wings */
4400 g_part_1->width = door_rect->width;
4401 g_part_1->height = door_rect->height;
4402 g_part_2->width = door_rect->width;
4403 g_part_2->height = door_rect->height;
4404 g_part_2->src_x = door_rect->width;
4405 g_part_2->src_y = g_part_1->src_y;
4407 door->part_2.x = door->part_1.x;
4408 door->part_2.y = door->part_1.y;
4410 if (door->width != -1)
4412 g_part_1->width = door->width;
4413 g_part_2->width = door->width;
4415 // special treatment for graphics and screen position of right wing
4416 g_part_2->src_x += door_rect->width - door->width;
4417 door->part_2.x += door_rect->width - door->width;
4420 if (door->height != -1)
4422 g_part_1->height = door->height;
4423 g_part_2->height = door->height;
4425 // special treatment for graphics and screen position of bottom wing
4426 g_part_2->src_y += door_rect->height - door->height;
4427 door->part_2.y += door_rect->height - door->height;
4430 /* set animation delays for the default wings and panels */
4432 door->part_1.step_delay = door->step_delay;
4433 door->part_2.step_delay = door->step_delay;
4434 door->panel.step_delay = door->step_delay;
4436 /* set animation draw order for the default wings */
4438 door->part_1.sort_priority = 2; /* draw left wing over ... */
4439 door->part_2.sort_priority = 1; /* ... right wing */
4441 /* set animation draw offset for the default wings */
4443 if (door->anim_mode & ANIM_HORIZONTAL)
4445 door->part_1.step_xoffset = door->step_offset;
4446 door->part_1.step_yoffset = 0;
4447 door->part_2.step_xoffset = door->step_offset * -1;
4448 door->part_2.step_yoffset = 0;
4450 num_door_steps = g_part_1->width / door->step_offset;
4452 else // ANIM_VERTICAL
4454 door->part_1.step_xoffset = 0;
4455 door->part_1.step_yoffset = door->step_offset;
4456 door->part_2.step_xoffset = 0;
4457 door->part_2.step_yoffset = door->step_offset * -1;
4459 num_door_steps = g_part_1->height / door->step_offset;
4462 /* set animation draw offset for the default panels */
4464 if (door->step_offset > 1)
4466 num_panel_steps = 2 * door_rect->height / door->step_offset;
4467 door->panel.start_step = num_panel_steps - num_door_steps;
4468 door->panel.start_step_closing = door->panel.start_step;
4472 num_panel_steps = door_rect->height / door->step_offset;
4473 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4474 door->panel.start_step_closing = door->panel.start_step;
4475 door->panel.step_delay *= 2;
4486 for (i = 0; door_part_controls[i].door_token != -1; i++)
4488 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4489 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4491 /* initialize "start_step_opening" and "start_step_closing", if needed */
4492 if (dpc->pos->start_step_opening == 0 &&
4493 dpc->pos->start_step_closing == 0)
4495 // dpc->pos->start_step_opening = dpc->pos->start_step;
4496 dpc->pos->start_step_closing = dpc->pos->start_step;
4499 /* fill structure for door part draw order (sorted below) */
4501 dpo->sort_priority = dpc->pos->sort_priority;
4504 /* sort door part controls according to sort_priority and graphic number */
4505 qsort(door_part_order, MAX_DOOR_PARTS,
4506 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4509 unsigned int OpenDoor(unsigned int door_state)
4511 if (door_state & DOOR_COPY_BACK)
4513 if (door_state & DOOR_OPEN_1)
4514 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4515 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4517 if (door_state & DOOR_OPEN_2)
4518 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4519 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4521 door_state &= ~DOOR_COPY_BACK;
4524 return MoveDoor(door_state);
4527 unsigned int CloseDoor(unsigned int door_state)
4529 unsigned int old_door_state = GetDoorState();
4531 if (!(door_state & DOOR_NO_COPY_BACK))
4533 if (old_door_state & DOOR_OPEN_1)
4534 BlitBitmap(backbuffer, bitmap_db_door_1,
4535 DX, DY, DXSIZE, DYSIZE, 0, 0);
4537 if (old_door_state & DOOR_OPEN_2)
4538 BlitBitmap(backbuffer, bitmap_db_door_2,
4539 VX, VY, VXSIZE, VYSIZE, 0, 0);
4541 door_state &= ~DOOR_NO_COPY_BACK;
4544 return MoveDoor(door_state);
4547 unsigned int GetDoorState()
4549 return MoveDoor(DOOR_GET_STATE);
4552 unsigned int SetDoorState(unsigned int door_state)
4554 return MoveDoor(door_state | DOOR_SET_STATE);
4557 int euclid(int a, int b)
4559 return (b ? euclid(b, a % b) : a);
4562 unsigned int MoveDoor(unsigned int door_state)
4564 struct Rect door_rect_list[] =
4566 { DX, DY, DXSIZE, DYSIZE },
4567 { VX, VY, VXSIZE, VYSIZE }
4569 static int door1 = DOOR_CLOSE_1;
4570 static int door2 = DOOR_CLOSE_2;
4571 unsigned int door_delay = 0;
4572 unsigned int door_delay_value;
4575 if (door_state == DOOR_GET_STATE)
4576 return (door1 | door2);
4578 if (door_state & DOOR_SET_STATE)
4580 if (door_state & DOOR_ACTION_1)
4581 door1 = door_state & DOOR_ACTION_1;
4582 if (door_state & DOOR_ACTION_2)
4583 door2 = door_state & DOOR_ACTION_2;
4585 return (door1 | door2);
4588 if (!(door_state & DOOR_FORCE_REDRAW))
4590 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4591 door_state &= ~DOOR_OPEN_1;
4592 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4593 door_state &= ~DOOR_CLOSE_1;
4594 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4595 door_state &= ~DOOR_OPEN_2;
4596 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4597 door_state &= ~DOOR_CLOSE_2;
4600 if (global.autoplay_leveldir)
4602 door_state |= DOOR_NO_DELAY;
4603 door_state &= ~DOOR_CLOSE_ALL;
4606 if (game_status == GAME_MODE_EDITOR)
4607 door_state |= DOOR_NO_DELAY;
4609 if (door_state & DOOR_ACTION)
4611 boolean door_panel_drawn[NUM_DOORS];
4612 boolean panel_has_doors[NUM_DOORS];
4613 boolean door_part_skip[MAX_DOOR_PARTS];
4614 boolean door_part_done[MAX_DOOR_PARTS];
4615 boolean door_part_done_all;
4616 int num_steps[MAX_DOOR_PARTS];
4617 int max_move_delay = 0; // delay for complete animations of all doors
4618 int max_step_delay = 0; // delay (ms) between two animation frames
4619 int num_move_steps = 0; // number of animation steps for all doors
4620 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4621 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4622 int current_move_delay = 0;
4626 for (i = 0; i < NUM_DOORS; i++)
4627 panel_has_doors[i] = FALSE;
4629 for (i = 0; i < MAX_DOOR_PARTS; i++)
4631 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4632 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4633 int door_token = dpc->door_token;
4635 door_part_done[i] = FALSE;
4636 door_part_skip[i] = (!(door_state & door_token) ||
4640 for (i = 0; i < MAX_DOOR_PARTS; i++)
4642 int nr = door_part_order[i].nr;
4643 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4644 struct DoorPartPosInfo *pos = dpc->pos;
4645 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4646 int door_token = dpc->door_token;
4647 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4648 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4649 int step_xoffset = ABS(pos->step_xoffset);
4650 int step_yoffset = ABS(pos->step_yoffset);
4651 int step_delay = pos->step_delay;
4652 int current_door_state = door_state & door_token;
4653 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4654 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4655 boolean part_opening = (is_panel ? door_closing : door_opening);
4656 int start_step = (part_opening ? pos->start_step_opening :
4657 pos->start_step_closing);
4658 float move_xsize = (step_xoffset ? g->width : 0);
4659 float move_ysize = (step_yoffset ? g->height : 0);
4660 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4661 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4662 int move_steps = (move_xsteps && move_ysteps ?
4663 MIN(move_xsteps, move_ysteps) :
4664 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4665 int move_delay = move_steps * step_delay;
4667 if (door_part_skip[nr])
4670 max_move_delay = MAX(max_move_delay, move_delay);
4671 max_step_delay = (max_step_delay == 0 ? step_delay :
4672 euclid(max_step_delay, step_delay));
4673 num_steps[nr] = move_steps;
4677 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4679 panel_has_doors[door_index] = TRUE;
4683 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4685 num_move_steps = max_move_delay / max_step_delay;
4686 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4688 door_delay_value = max_step_delay;
4690 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4692 start = num_move_steps - 1;
4696 /* opening door sound has priority over simultaneously closing door */
4697 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4698 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4699 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4700 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4703 for (k = start; k < num_move_steps; k++)
4705 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4707 door_part_done_all = TRUE;
4709 for (i = 0; i < NUM_DOORS; i++)
4710 door_panel_drawn[i] = FALSE;
4712 for (i = 0; i < MAX_DOOR_PARTS; i++)
4714 int nr = door_part_order[i].nr;
4715 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4716 struct DoorPartPosInfo *pos = dpc->pos;
4717 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4718 int door_token = dpc->door_token;
4719 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4720 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4721 boolean is_panel_and_door_has_closed = FALSE;
4722 struct Rect *door_rect = &door_rect_list[door_index];
4723 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4725 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4726 int current_door_state = door_state & door_token;
4727 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4728 boolean door_closing = !door_opening;
4729 boolean part_opening = (is_panel ? door_closing : door_opening);
4730 boolean part_closing = !part_opening;
4731 int start_step = (part_opening ? pos->start_step_opening :
4732 pos->start_step_closing);
4733 int step_delay = pos->step_delay;
4734 int step_factor = step_delay / max_step_delay;
4735 int k1 = (step_factor ? k / step_factor + 1 : k);
4736 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4737 int kk = MAX(0, k2);
4740 int src_x, src_y, src_xx, src_yy;
4741 int dst_x, dst_y, dst_xx, dst_yy;
4744 if (door_part_skip[nr])
4747 if (!(door_state & door_token))
4755 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4756 int kk_door = MAX(0, k2_door);
4757 int sync_frame = kk_door * door_delay_value;
4758 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4760 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
4761 &g_src_x, &g_src_y);
4766 if (!door_panel_drawn[door_index])
4768 ClearRectangle(drawto, door_rect->x, door_rect->y,
4769 door_rect->width, door_rect->height);
4771 door_panel_drawn[door_index] = TRUE;
4774 // draw opening or closing door parts
4776 if (pos->step_xoffset < 0) // door part on right side
4779 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4782 if (dst_xx + width > door_rect->width)
4783 width = door_rect->width - dst_xx;
4785 else // door part on left side
4788 dst_xx = pos->x - kk * pos->step_xoffset;
4792 src_xx = ABS(dst_xx);
4796 width = g->width - src_xx;
4798 if (width > door_rect->width)
4799 width = door_rect->width;
4801 // printf("::: k == %d [%d] \n", k, start_step);
4804 if (pos->step_yoffset < 0) // door part on bottom side
4807 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4810 if (dst_yy + height > door_rect->height)
4811 height = door_rect->height - dst_yy;
4813 else // door part on top side
4816 dst_yy = pos->y - kk * pos->step_yoffset;
4820 src_yy = ABS(dst_yy);
4824 height = g->height - src_yy;
4827 src_x = g_src_x + src_xx;
4828 src_y = g_src_y + src_yy;
4830 dst_x = door_rect->x + dst_xx;
4831 dst_y = door_rect->y + dst_yy;
4833 is_panel_and_door_has_closed =
4836 panel_has_doors[door_index] &&
4837 k >= num_move_steps_doors_only - 1);
4839 if (width >= 0 && width <= g->width &&
4840 height >= 0 && height <= g->height &&
4841 !is_panel_and_door_has_closed)
4843 if (is_panel || !pos->draw_masked)
4844 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4847 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4851 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4853 if ((part_opening && (width < 0 || height < 0)) ||
4854 (part_closing && (width >= g->width && height >= g->height)))
4855 door_part_done[nr] = TRUE;
4857 // continue door part animations, but not panel after door has closed
4858 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4859 door_part_done_all = FALSE;
4862 if (!(door_state & DOOR_NO_DELAY))
4866 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4868 current_move_delay += max_step_delay;
4870 /* prevent OS (Windows) from complaining about program not responding */
4874 if (door_part_done_all)
4879 if (door_state & DOOR_ACTION_1)
4880 door1 = door_state & DOOR_ACTION_1;
4881 if (door_state & DOOR_ACTION_2)
4882 door2 = door_state & DOOR_ACTION_2;
4884 // draw masked border over door area
4885 DrawMaskedBorder(REDRAW_DOOR_1);
4886 DrawMaskedBorder(REDRAW_DOOR_2);
4888 return (door1 | door2);
4891 static boolean useSpecialEditorDoor()
4893 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4894 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4896 // do not draw special editor door if editor border defined or redefined
4897 if (graphic_info[graphic].bitmap != NULL || redefined)
4900 // do not draw special editor door if global border defined to be empty
4901 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4904 // do not draw special editor door if viewport definitions do not match
4908 EY + EYSIZE != VY + VYSIZE)
4914 void DrawSpecialEditorDoor()
4916 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4917 int top_border_width = gfx1->width;
4918 int top_border_height = gfx1->height;
4919 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4920 int ex = EX - outer_border;
4921 int ey = EY - outer_border;
4922 int vy = VY - outer_border;
4923 int exsize = EXSIZE + 2 * outer_border;
4925 if (!useSpecialEditorDoor())
4928 /* draw bigger level editor toolbox window */
4929 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4930 top_border_width, top_border_height, ex, ey - top_border_height);
4931 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4932 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4934 redraw_mask |= REDRAW_ALL;
4937 void UndrawSpecialEditorDoor()
4939 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4940 int top_border_width = gfx1->width;
4941 int top_border_height = gfx1->height;
4942 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4943 int ex = EX - outer_border;
4944 int ey = EY - outer_border;
4945 int ey_top = ey - top_border_height;
4946 int exsize = EXSIZE + 2 * outer_border;
4947 int eysize = EYSIZE + 2 * outer_border;
4949 if (!useSpecialEditorDoor())
4952 /* draw normal tape recorder window */
4953 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4955 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4956 ex, ey_top, top_border_width, top_border_height,
4958 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4959 ex, ey, exsize, eysize, ex, ey);
4963 // if screen background is set to "[NONE]", clear editor toolbox window
4964 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4965 ClearRectangle(drawto, ex, ey, exsize, eysize);
4968 redraw_mask |= REDRAW_ALL;
4972 /* ---------- new tool button stuff ---------------------------------------- */
4977 struct TextPosInfo *pos;
4980 } toolbutton_info[NUM_TOOL_BUTTONS] =
4983 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
4984 TOOL_CTRL_ID_YES, "yes"
4987 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
4988 TOOL_CTRL_ID_NO, "no"
4991 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
4992 TOOL_CTRL_ID_CONFIRM, "confirm"
4995 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
4996 TOOL_CTRL_ID_PLAYER_1, "player 1"
4999 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
5000 TOOL_CTRL_ID_PLAYER_2, "player 2"
5003 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
5004 TOOL_CTRL_ID_PLAYER_3, "player 3"
5007 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
5008 TOOL_CTRL_ID_PLAYER_4, "player 4"
5012 void CreateToolButtons()
5016 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5018 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
5019 struct TextPosInfo *pos = toolbutton_info[i].pos;
5020 struct GadgetInfo *gi;
5021 Bitmap *deco_bitmap = None;
5022 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
5023 unsigned int event_mask = GD_EVENT_RELEASED;
5026 int gd_x = gfx->src_x;
5027 int gd_y = gfx->src_y;
5028 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
5029 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
5032 if (global.use_envelope_request)
5033 setRequestPosition(&dx, &dy, TRUE);
5035 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
5037 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
5039 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
5040 pos->size, &deco_bitmap, &deco_x, &deco_y);
5041 deco_xpos = (gfx->width - pos->size) / 2;
5042 deco_ypos = (gfx->height - pos->size) / 2;
5045 gi = CreateGadget(GDI_CUSTOM_ID, id,
5046 GDI_INFO_TEXT, toolbutton_info[i].infotext,
5047 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
5048 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
5049 GDI_WIDTH, gfx->width,
5050 GDI_HEIGHT, gfx->height,
5051 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
5052 GDI_STATE, GD_BUTTON_UNPRESSED,
5053 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
5054 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
5055 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
5056 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
5057 GDI_DECORATION_SIZE, pos->size, pos->size,
5058 GDI_DECORATION_SHIFTING, 1, 1,
5059 GDI_DIRECT_DRAW, FALSE,
5060 GDI_EVENT_MASK, event_mask,
5061 GDI_CALLBACK_ACTION, HandleToolButtons,
5065 Error(ERR_EXIT, "cannot create gadget");
5067 tool_gadget[id] = gi;
5071 void FreeToolButtons()
5075 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5076 FreeGadget(tool_gadget[i]);
5079 static void UnmapToolButtons()
5083 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
5084 UnmapGadget(tool_gadget[i]);
5087 static void HandleToolButtons(struct GadgetInfo *gi)
5089 request_gadget_id = gi->custom_id;
5092 static struct Mapping_EM_to_RND_object
5095 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
5096 boolean is_backside; /* backside of moving element */
5102 em_object_mapping_list[] =
5105 Xblank, TRUE, FALSE,
5109 Yacid_splash_eB, FALSE, FALSE,
5110 EL_ACID_SPLASH_RIGHT, -1, -1
5113 Yacid_splash_wB, FALSE, FALSE,
5114 EL_ACID_SPLASH_LEFT, -1, -1
5117 #ifdef EM_ENGINE_BAD_ROLL
5119 Xstone_force_e, FALSE, FALSE,
5120 EL_ROCK, -1, MV_BIT_RIGHT
5123 Xstone_force_w, FALSE, FALSE,
5124 EL_ROCK, -1, MV_BIT_LEFT
5127 Xnut_force_e, FALSE, FALSE,
5128 EL_NUT, -1, MV_BIT_RIGHT
5131 Xnut_force_w, FALSE, FALSE,
5132 EL_NUT, -1, MV_BIT_LEFT
5135 Xspring_force_e, FALSE, FALSE,
5136 EL_SPRING, -1, MV_BIT_RIGHT
5139 Xspring_force_w, FALSE, FALSE,
5140 EL_SPRING, -1, MV_BIT_LEFT
5143 Xemerald_force_e, FALSE, FALSE,
5144 EL_EMERALD, -1, MV_BIT_RIGHT
5147 Xemerald_force_w, FALSE, FALSE,
5148 EL_EMERALD, -1, MV_BIT_LEFT
5151 Xdiamond_force_e, FALSE, FALSE,
5152 EL_DIAMOND, -1, MV_BIT_RIGHT
5155 Xdiamond_force_w, FALSE, FALSE,
5156 EL_DIAMOND, -1, MV_BIT_LEFT
5159 Xbomb_force_e, FALSE, FALSE,
5160 EL_BOMB, -1, MV_BIT_RIGHT
5163 Xbomb_force_w, FALSE, FALSE,
5164 EL_BOMB, -1, MV_BIT_LEFT
5166 #endif /* EM_ENGINE_BAD_ROLL */
5169 Xstone, TRUE, FALSE,
5173 Xstone_pause, FALSE, FALSE,
5177 Xstone_fall, FALSE, FALSE,
5181 Ystone_s, FALSE, FALSE,
5182 EL_ROCK, ACTION_FALLING, -1
5185 Ystone_sB, FALSE, TRUE,
5186 EL_ROCK, ACTION_FALLING, -1
5189 Ystone_e, FALSE, FALSE,
5190 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5193 Ystone_eB, FALSE, TRUE,
5194 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5197 Ystone_w, FALSE, FALSE,
5198 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5201 Ystone_wB, FALSE, TRUE,
5202 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5209 Xnut_pause, FALSE, FALSE,
5213 Xnut_fall, FALSE, FALSE,
5217 Ynut_s, FALSE, FALSE,
5218 EL_NUT, ACTION_FALLING, -1
5221 Ynut_sB, FALSE, TRUE,
5222 EL_NUT, ACTION_FALLING, -1
5225 Ynut_e, FALSE, FALSE,
5226 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5229 Ynut_eB, FALSE, TRUE,
5230 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5233 Ynut_w, FALSE, FALSE,
5234 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5237 Ynut_wB, FALSE, TRUE,
5238 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5241 Xbug_n, TRUE, FALSE,
5245 Xbug_e, TRUE, FALSE,
5246 EL_BUG_RIGHT, -1, -1
5249 Xbug_s, TRUE, FALSE,
5253 Xbug_w, TRUE, FALSE,
5257 Xbug_gon, FALSE, FALSE,
5261 Xbug_goe, FALSE, FALSE,
5262 EL_BUG_RIGHT, -1, -1
5265 Xbug_gos, FALSE, FALSE,
5269 Xbug_gow, FALSE, FALSE,
5273 Ybug_n, FALSE, FALSE,
5274 EL_BUG, ACTION_MOVING, MV_BIT_UP
5277 Ybug_nB, FALSE, TRUE,
5278 EL_BUG, ACTION_MOVING, MV_BIT_UP
5281 Ybug_e, FALSE, FALSE,
5282 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5285 Ybug_eB, FALSE, TRUE,
5286 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5289 Ybug_s, FALSE, FALSE,
5290 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5293 Ybug_sB, FALSE, TRUE,
5294 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5297 Ybug_w, FALSE, FALSE,
5298 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5301 Ybug_wB, FALSE, TRUE,
5302 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5305 Ybug_w_n, FALSE, FALSE,
5306 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5309 Ybug_n_e, FALSE, FALSE,
5310 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5313 Ybug_e_s, FALSE, FALSE,
5314 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5317 Ybug_s_w, FALSE, FALSE,
5318 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5321 Ybug_e_n, FALSE, FALSE,
5322 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5325 Ybug_s_e, FALSE, FALSE,
5326 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5329 Ybug_w_s, FALSE, FALSE,
5330 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5333 Ybug_n_w, FALSE, FALSE,
5334 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5337 Ybug_stone, FALSE, FALSE,
5338 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5341 Ybug_spring, FALSE, FALSE,
5342 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5345 Xtank_n, TRUE, FALSE,
5346 EL_SPACESHIP_UP, -1, -1
5349 Xtank_e, TRUE, FALSE,
5350 EL_SPACESHIP_RIGHT, -1, -1
5353 Xtank_s, TRUE, FALSE,
5354 EL_SPACESHIP_DOWN, -1, -1
5357 Xtank_w, TRUE, FALSE,
5358 EL_SPACESHIP_LEFT, -1, -1
5361 Xtank_gon, FALSE, FALSE,
5362 EL_SPACESHIP_UP, -1, -1
5365 Xtank_goe, FALSE, FALSE,
5366 EL_SPACESHIP_RIGHT, -1, -1
5369 Xtank_gos, FALSE, FALSE,
5370 EL_SPACESHIP_DOWN, -1, -1
5373 Xtank_gow, FALSE, FALSE,
5374 EL_SPACESHIP_LEFT, -1, -1
5377 Ytank_n, FALSE, FALSE,
5378 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5381 Ytank_nB, FALSE, TRUE,
5382 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5385 Ytank_e, FALSE, FALSE,
5386 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5389 Ytank_eB, FALSE, TRUE,
5390 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5393 Ytank_s, FALSE, FALSE,
5394 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5397 Ytank_sB, FALSE, TRUE,
5398 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5401 Ytank_w, FALSE, FALSE,
5402 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5405 Ytank_wB, FALSE, TRUE,
5406 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5409 Ytank_w_n, FALSE, FALSE,
5410 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5413 Ytank_n_e, FALSE, FALSE,
5414 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5417 Ytank_e_s, FALSE, FALSE,
5418 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5421 Ytank_s_w, FALSE, FALSE,
5422 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5425 Ytank_e_n, FALSE, FALSE,
5426 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5429 Ytank_s_e, FALSE, FALSE,
5430 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5433 Ytank_w_s, FALSE, FALSE,
5434 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5437 Ytank_n_w, FALSE, FALSE,
5438 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5441 Ytank_stone, FALSE, FALSE,
5442 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5445 Ytank_spring, FALSE, FALSE,
5446 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5449 Xandroid, TRUE, FALSE,
5450 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5453 Xandroid_1_n, FALSE, FALSE,
5454 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5457 Xandroid_2_n, FALSE, FALSE,
5458 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5461 Xandroid_1_e, FALSE, FALSE,
5462 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5465 Xandroid_2_e, FALSE, FALSE,
5466 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5469 Xandroid_1_w, FALSE, FALSE,
5470 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5473 Xandroid_2_w, FALSE, FALSE,
5474 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5477 Xandroid_1_s, FALSE, FALSE,
5478 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5481 Xandroid_2_s, FALSE, FALSE,
5482 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5485 Yandroid_n, FALSE, FALSE,
5486 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5489 Yandroid_nB, FALSE, TRUE,
5490 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5493 Yandroid_ne, FALSE, FALSE,
5494 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5497 Yandroid_neB, FALSE, TRUE,
5498 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5501 Yandroid_e, FALSE, FALSE,
5502 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5505 Yandroid_eB, FALSE, TRUE,
5506 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5509 Yandroid_se, FALSE, FALSE,
5510 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5513 Yandroid_seB, FALSE, TRUE,
5514 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5517 Yandroid_s, FALSE, FALSE,
5518 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5521 Yandroid_sB, FALSE, TRUE,
5522 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5525 Yandroid_sw, FALSE, FALSE,
5526 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5529 Yandroid_swB, FALSE, TRUE,
5530 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5533 Yandroid_w, FALSE, FALSE,
5534 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5537 Yandroid_wB, FALSE, TRUE,
5538 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5541 Yandroid_nw, FALSE, FALSE,
5542 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5545 Yandroid_nwB, FALSE, TRUE,
5546 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5549 Xspring, TRUE, FALSE,
5553 Xspring_pause, FALSE, FALSE,
5557 Xspring_e, FALSE, FALSE,
5561 Xspring_w, FALSE, FALSE,
5565 Xspring_fall, FALSE, FALSE,
5569 Yspring_s, FALSE, FALSE,
5570 EL_SPRING, ACTION_FALLING, -1
5573 Yspring_sB, FALSE, TRUE,
5574 EL_SPRING, ACTION_FALLING, -1
5577 Yspring_e, FALSE, FALSE,
5578 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5581 Yspring_eB, FALSE, TRUE,
5582 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5585 Yspring_w, FALSE, FALSE,
5586 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5589 Yspring_wB, FALSE, TRUE,
5590 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5593 Yspring_kill_e, FALSE, FALSE,
5594 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5597 Yspring_kill_eB, FALSE, TRUE,
5598 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5601 Yspring_kill_w, FALSE, FALSE,
5602 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5605 Yspring_kill_wB, FALSE, TRUE,
5606 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5609 Xeater_n, TRUE, FALSE,
5610 EL_YAMYAM_UP, -1, -1
5613 Xeater_e, TRUE, FALSE,
5614 EL_YAMYAM_RIGHT, -1, -1
5617 Xeater_w, TRUE, FALSE,
5618 EL_YAMYAM_LEFT, -1, -1
5621 Xeater_s, TRUE, FALSE,
5622 EL_YAMYAM_DOWN, -1, -1
5625 Yeater_n, FALSE, FALSE,
5626 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5629 Yeater_nB, FALSE, TRUE,
5630 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5633 Yeater_e, FALSE, FALSE,
5634 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5637 Yeater_eB, FALSE, TRUE,
5638 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5641 Yeater_s, FALSE, FALSE,
5642 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5645 Yeater_sB, FALSE, TRUE,
5646 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5649 Yeater_w, FALSE, FALSE,
5650 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5653 Yeater_wB, FALSE, TRUE,
5654 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5657 Yeater_stone, FALSE, FALSE,
5658 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5661 Yeater_spring, FALSE, FALSE,
5662 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5665 Xalien, TRUE, FALSE,
5669 Xalien_pause, FALSE, FALSE,
5673 Yalien_n, FALSE, FALSE,
5674 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5677 Yalien_nB, FALSE, TRUE,
5678 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5681 Yalien_e, FALSE, FALSE,
5682 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5685 Yalien_eB, FALSE, TRUE,
5686 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5689 Yalien_s, FALSE, FALSE,
5690 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5693 Yalien_sB, FALSE, TRUE,
5694 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5697 Yalien_w, FALSE, FALSE,
5698 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5701 Yalien_wB, FALSE, TRUE,
5702 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5705 Yalien_stone, FALSE, FALSE,
5706 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5709 Yalien_spring, FALSE, FALSE,
5710 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5713 Xemerald, TRUE, FALSE,
5717 Xemerald_pause, FALSE, FALSE,
5721 Xemerald_fall, FALSE, FALSE,
5725 Xemerald_shine, FALSE, FALSE,
5726 EL_EMERALD, ACTION_TWINKLING, -1
5729 Yemerald_s, FALSE, FALSE,
5730 EL_EMERALD, ACTION_FALLING, -1
5733 Yemerald_sB, FALSE, TRUE,
5734 EL_EMERALD, ACTION_FALLING, -1
5737 Yemerald_e, FALSE, FALSE,
5738 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5741 Yemerald_eB, FALSE, TRUE,
5742 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5745 Yemerald_w, FALSE, FALSE,
5746 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5749 Yemerald_wB, FALSE, TRUE,
5750 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5753 Yemerald_eat, FALSE, FALSE,
5754 EL_EMERALD, ACTION_COLLECTING, -1
5757 Yemerald_stone, FALSE, FALSE,
5758 EL_NUT, ACTION_BREAKING, -1
5761 Xdiamond, TRUE, FALSE,
5765 Xdiamond_pause, FALSE, FALSE,
5769 Xdiamond_fall, FALSE, FALSE,
5773 Xdiamond_shine, FALSE, FALSE,
5774 EL_DIAMOND, ACTION_TWINKLING, -1
5777 Ydiamond_s, FALSE, FALSE,
5778 EL_DIAMOND, ACTION_FALLING, -1
5781 Ydiamond_sB, FALSE, TRUE,
5782 EL_DIAMOND, ACTION_FALLING, -1
5785 Ydiamond_e, FALSE, FALSE,
5786 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5789 Ydiamond_eB, FALSE, TRUE,
5790 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5793 Ydiamond_w, FALSE, FALSE,
5794 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5797 Ydiamond_wB, FALSE, TRUE,
5798 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5801 Ydiamond_eat, FALSE, FALSE,
5802 EL_DIAMOND, ACTION_COLLECTING, -1
5805 Ydiamond_stone, FALSE, FALSE,
5806 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5809 Xdrip_fall, TRUE, FALSE,
5810 EL_AMOEBA_DROP, -1, -1
5813 Xdrip_stretch, FALSE, FALSE,
5814 EL_AMOEBA_DROP, ACTION_FALLING, -1
5817 Xdrip_stretchB, FALSE, TRUE,
5818 EL_AMOEBA_DROP, ACTION_FALLING, -1
5821 Xdrip_eat, FALSE, FALSE,
5822 EL_AMOEBA_DROP, ACTION_GROWING, -1
5825 Ydrip_s1, FALSE, FALSE,
5826 EL_AMOEBA_DROP, ACTION_FALLING, -1
5829 Ydrip_s1B, FALSE, TRUE,
5830 EL_AMOEBA_DROP, ACTION_FALLING, -1
5833 Ydrip_s2, FALSE, FALSE,
5834 EL_AMOEBA_DROP, ACTION_FALLING, -1
5837 Ydrip_s2B, FALSE, TRUE,
5838 EL_AMOEBA_DROP, ACTION_FALLING, -1
5845 Xbomb_pause, FALSE, FALSE,
5849 Xbomb_fall, FALSE, FALSE,
5853 Ybomb_s, FALSE, FALSE,
5854 EL_BOMB, ACTION_FALLING, -1
5857 Ybomb_sB, FALSE, TRUE,
5858 EL_BOMB, ACTION_FALLING, -1
5861 Ybomb_e, FALSE, FALSE,
5862 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5865 Ybomb_eB, FALSE, TRUE,
5866 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5869 Ybomb_w, FALSE, FALSE,
5870 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5873 Ybomb_wB, FALSE, TRUE,
5874 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5877 Ybomb_eat, FALSE, FALSE,
5878 EL_BOMB, ACTION_ACTIVATING, -1
5881 Xballoon, TRUE, FALSE,
5885 Yballoon_n, FALSE, FALSE,
5886 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5889 Yballoon_nB, FALSE, TRUE,
5890 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5893 Yballoon_e, FALSE, FALSE,
5894 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5897 Yballoon_eB, FALSE, TRUE,
5898 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5901 Yballoon_s, FALSE, FALSE,
5902 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5905 Yballoon_sB, FALSE, TRUE,
5906 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5909 Yballoon_w, FALSE, FALSE,
5910 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5913 Yballoon_wB, FALSE, TRUE,
5914 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5917 Xgrass, TRUE, FALSE,
5918 EL_EMC_GRASS, -1, -1
5921 Ygrass_nB, FALSE, FALSE,
5922 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5925 Ygrass_eB, FALSE, FALSE,
5926 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5929 Ygrass_sB, FALSE, FALSE,
5930 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5933 Ygrass_wB, FALSE, FALSE,
5934 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5941 Ydirt_nB, FALSE, FALSE,
5942 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5945 Ydirt_eB, FALSE, FALSE,
5946 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5949 Ydirt_sB, FALSE, FALSE,
5950 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5953 Ydirt_wB, FALSE, FALSE,
5954 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5957 Xacid_ne, TRUE, FALSE,
5958 EL_ACID_POOL_TOPRIGHT, -1, -1
5961 Xacid_se, TRUE, FALSE,
5962 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5965 Xacid_s, TRUE, FALSE,
5966 EL_ACID_POOL_BOTTOM, -1, -1
5969 Xacid_sw, TRUE, FALSE,
5970 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5973 Xacid_nw, TRUE, FALSE,
5974 EL_ACID_POOL_TOPLEFT, -1, -1
5977 Xacid_1, TRUE, FALSE,
5981 Xacid_2, FALSE, FALSE,
5985 Xacid_3, FALSE, FALSE,
5989 Xacid_4, FALSE, FALSE,
5993 Xacid_5, FALSE, FALSE,
5997 Xacid_6, FALSE, FALSE,
6001 Xacid_7, FALSE, FALSE,
6005 Xacid_8, FALSE, FALSE,
6009 Xball_1, TRUE, FALSE,
6010 EL_EMC_MAGIC_BALL, -1, -1
6013 Xball_1B, FALSE, FALSE,
6014 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6017 Xball_2, FALSE, FALSE,
6018 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6021 Xball_2B, FALSE, FALSE,
6022 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
6025 Yball_eat, FALSE, FALSE,
6026 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
6029 Ykey_1_eat, FALSE, FALSE,
6030 EL_EM_KEY_1, ACTION_COLLECTING, -1
6033 Ykey_2_eat, FALSE, FALSE,
6034 EL_EM_KEY_2, ACTION_COLLECTING, -1
6037 Ykey_3_eat, FALSE, FALSE,
6038 EL_EM_KEY_3, ACTION_COLLECTING, -1
6041 Ykey_4_eat, FALSE, FALSE,
6042 EL_EM_KEY_4, ACTION_COLLECTING, -1
6045 Ykey_5_eat, FALSE, FALSE,
6046 EL_EMC_KEY_5, ACTION_COLLECTING, -1
6049 Ykey_6_eat, FALSE, FALSE,
6050 EL_EMC_KEY_6, ACTION_COLLECTING, -1
6053 Ykey_7_eat, FALSE, FALSE,
6054 EL_EMC_KEY_7, ACTION_COLLECTING, -1
6057 Ykey_8_eat, FALSE, FALSE,
6058 EL_EMC_KEY_8, ACTION_COLLECTING, -1
6061 Ylenses_eat, FALSE, FALSE,
6062 EL_EMC_LENSES, ACTION_COLLECTING, -1
6065 Ymagnify_eat, FALSE, FALSE,
6066 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
6069 Ygrass_eat, FALSE, FALSE,
6070 EL_EMC_GRASS, ACTION_SNAPPING, -1
6073 Ydirt_eat, FALSE, FALSE,
6074 EL_SAND, ACTION_SNAPPING, -1
6077 Xgrow_ns, TRUE, FALSE,
6078 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
6081 Ygrow_ns_eat, FALSE, FALSE,
6082 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
6085 Xgrow_ew, TRUE, FALSE,
6086 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6089 Ygrow_ew_eat, FALSE, FALSE,
6090 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6093 Xwonderwall, TRUE, FALSE,
6094 EL_MAGIC_WALL, -1, -1
6097 XwonderwallB, FALSE, FALSE,
6098 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6101 Xamoeba_1, TRUE, FALSE,
6102 EL_AMOEBA_DRY, ACTION_OTHER, -1
6105 Xamoeba_2, FALSE, FALSE,
6106 EL_AMOEBA_DRY, ACTION_OTHER, -1
6109 Xamoeba_3, FALSE, FALSE,
6110 EL_AMOEBA_DRY, ACTION_OTHER, -1
6113 Xamoeba_4, FALSE, FALSE,
6114 EL_AMOEBA_DRY, ACTION_OTHER, -1
6117 Xamoeba_5, TRUE, FALSE,
6118 EL_AMOEBA_WET, ACTION_OTHER, -1
6121 Xamoeba_6, FALSE, FALSE,
6122 EL_AMOEBA_WET, ACTION_OTHER, -1
6125 Xamoeba_7, FALSE, FALSE,
6126 EL_AMOEBA_WET, ACTION_OTHER, -1
6129 Xamoeba_8, FALSE, FALSE,
6130 EL_AMOEBA_WET, ACTION_OTHER, -1
6133 Xdoor_1, TRUE, FALSE,
6134 EL_EM_GATE_1, -1, -1
6137 Xdoor_2, TRUE, FALSE,
6138 EL_EM_GATE_2, -1, -1
6141 Xdoor_3, TRUE, FALSE,
6142 EL_EM_GATE_3, -1, -1
6145 Xdoor_4, TRUE, FALSE,
6146 EL_EM_GATE_4, -1, -1
6149 Xdoor_5, TRUE, FALSE,
6150 EL_EMC_GATE_5, -1, -1
6153 Xdoor_6, TRUE, FALSE,
6154 EL_EMC_GATE_6, -1, -1
6157 Xdoor_7, TRUE, FALSE,
6158 EL_EMC_GATE_7, -1, -1
6161 Xdoor_8, TRUE, FALSE,
6162 EL_EMC_GATE_8, -1, -1
6165 Xkey_1, TRUE, FALSE,
6169 Xkey_2, TRUE, FALSE,
6173 Xkey_3, TRUE, FALSE,
6177 Xkey_4, TRUE, FALSE,
6181 Xkey_5, TRUE, FALSE,
6182 EL_EMC_KEY_5, -1, -1
6185 Xkey_6, TRUE, FALSE,
6186 EL_EMC_KEY_6, -1, -1
6189 Xkey_7, TRUE, FALSE,
6190 EL_EMC_KEY_7, -1, -1
6193 Xkey_8, TRUE, FALSE,
6194 EL_EMC_KEY_8, -1, -1
6197 Xwind_n, TRUE, FALSE,
6198 EL_BALLOON_SWITCH_UP, -1, -1
6201 Xwind_e, TRUE, FALSE,
6202 EL_BALLOON_SWITCH_RIGHT, -1, -1
6205 Xwind_s, TRUE, FALSE,
6206 EL_BALLOON_SWITCH_DOWN, -1, -1
6209 Xwind_w, TRUE, FALSE,
6210 EL_BALLOON_SWITCH_LEFT, -1, -1
6213 Xwind_nesw, TRUE, FALSE,
6214 EL_BALLOON_SWITCH_ANY, -1, -1
6217 Xwind_stop, TRUE, FALSE,
6218 EL_BALLOON_SWITCH_NONE, -1, -1
6222 EL_EM_EXIT_CLOSED, -1, -1
6225 Xexit_1, TRUE, FALSE,
6226 EL_EM_EXIT_OPEN, -1, -1
6229 Xexit_2, FALSE, FALSE,
6230 EL_EM_EXIT_OPEN, -1, -1
6233 Xexit_3, FALSE, FALSE,
6234 EL_EM_EXIT_OPEN, -1, -1
6237 Xdynamite, TRUE, FALSE,
6238 EL_EM_DYNAMITE, -1, -1
6241 Ydynamite_eat, FALSE, FALSE,
6242 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6245 Xdynamite_1, TRUE, FALSE,
6246 EL_EM_DYNAMITE_ACTIVE, -1, -1
6249 Xdynamite_2, FALSE, FALSE,
6250 EL_EM_DYNAMITE_ACTIVE, -1, -1
6253 Xdynamite_3, FALSE, FALSE,
6254 EL_EM_DYNAMITE_ACTIVE, -1, -1
6257 Xdynamite_4, FALSE, FALSE,
6258 EL_EM_DYNAMITE_ACTIVE, -1, -1
6261 Xbumper, TRUE, FALSE,
6262 EL_EMC_SPRING_BUMPER, -1, -1
6265 XbumperB, FALSE, FALSE,
6266 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6269 Xwheel, TRUE, FALSE,
6270 EL_ROBOT_WHEEL, -1, -1
6273 XwheelB, FALSE, FALSE,
6274 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6277 Xswitch, TRUE, FALSE,
6278 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6281 XswitchB, FALSE, FALSE,
6282 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6286 EL_QUICKSAND_EMPTY, -1, -1
6289 Xsand_stone, TRUE, FALSE,
6290 EL_QUICKSAND_FULL, -1, -1
6293 Xsand_stonein_1, FALSE, TRUE,
6294 EL_ROCK, ACTION_FILLING, -1
6297 Xsand_stonein_2, FALSE, TRUE,
6298 EL_ROCK, ACTION_FILLING, -1
6301 Xsand_stonein_3, FALSE, TRUE,
6302 EL_ROCK, ACTION_FILLING, -1
6305 Xsand_stonein_4, FALSE, TRUE,
6306 EL_ROCK, ACTION_FILLING, -1
6309 Xsand_stonesand_1, FALSE, FALSE,
6310 EL_QUICKSAND_EMPTYING, -1, -1
6313 Xsand_stonesand_2, FALSE, FALSE,
6314 EL_QUICKSAND_EMPTYING, -1, -1
6317 Xsand_stonesand_3, FALSE, FALSE,
6318 EL_QUICKSAND_EMPTYING, -1, -1
6321 Xsand_stonesand_4, FALSE, FALSE,
6322 EL_QUICKSAND_EMPTYING, -1, -1
6325 Xsand_stonesand_quickout_1, FALSE, FALSE,
6326 EL_QUICKSAND_EMPTYING, -1, -1
6329 Xsand_stonesand_quickout_2, FALSE, FALSE,
6330 EL_QUICKSAND_EMPTYING, -1, -1
6333 Xsand_stoneout_1, FALSE, FALSE,
6334 EL_ROCK, ACTION_EMPTYING, -1
6337 Xsand_stoneout_2, FALSE, FALSE,
6338 EL_ROCK, ACTION_EMPTYING, -1
6341 Xsand_sandstone_1, FALSE, FALSE,
6342 EL_QUICKSAND_FILLING, -1, -1
6345 Xsand_sandstone_2, FALSE, FALSE,
6346 EL_QUICKSAND_FILLING, -1, -1
6349 Xsand_sandstone_3, FALSE, FALSE,
6350 EL_QUICKSAND_FILLING, -1, -1
6353 Xsand_sandstone_4, FALSE, FALSE,
6354 EL_QUICKSAND_FILLING, -1, -1
6357 Xplant, TRUE, FALSE,
6358 EL_EMC_PLANT, -1, -1
6361 Yplant, FALSE, FALSE,
6362 EL_EMC_PLANT, -1, -1
6365 Xlenses, TRUE, FALSE,
6366 EL_EMC_LENSES, -1, -1
6369 Xmagnify, TRUE, FALSE,
6370 EL_EMC_MAGNIFIER, -1, -1
6373 Xdripper, TRUE, FALSE,
6374 EL_EMC_DRIPPER, -1, -1
6377 XdripperB, FALSE, FALSE,
6378 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6381 Xfake_blank, TRUE, FALSE,
6382 EL_INVISIBLE_WALL, -1, -1
6385 Xfake_blankB, FALSE, FALSE,
6386 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6389 Xfake_grass, TRUE, FALSE,
6390 EL_EMC_FAKE_GRASS, -1, -1
6393 Xfake_grassB, FALSE, FALSE,
6394 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6397 Xfake_door_1, TRUE, FALSE,
6398 EL_EM_GATE_1_GRAY, -1, -1
6401 Xfake_door_2, TRUE, FALSE,
6402 EL_EM_GATE_2_GRAY, -1, -1
6405 Xfake_door_3, TRUE, FALSE,
6406 EL_EM_GATE_3_GRAY, -1, -1
6409 Xfake_door_4, TRUE, FALSE,
6410 EL_EM_GATE_4_GRAY, -1, -1
6413 Xfake_door_5, TRUE, FALSE,
6414 EL_EMC_GATE_5_GRAY, -1, -1
6417 Xfake_door_6, TRUE, FALSE,
6418 EL_EMC_GATE_6_GRAY, -1, -1
6421 Xfake_door_7, TRUE, FALSE,
6422 EL_EMC_GATE_7_GRAY, -1, -1
6425 Xfake_door_8, TRUE, FALSE,
6426 EL_EMC_GATE_8_GRAY, -1, -1
6429 Xfake_acid_1, TRUE, FALSE,
6430 EL_EMC_FAKE_ACID, -1, -1
6433 Xfake_acid_2, FALSE, FALSE,
6434 EL_EMC_FAKE_ACID, -1, -1
6437 Xfake_acid_3, FALSE, FALSE,
6438 EL_EMC_FAKE_ACID, -1, -1
6441 Xfake_acid_4, FALSE, FALSE,
6442 EL_EMC_FAKE_ACID, -1, -1
6445 Xfake_acid_5, FALSE, FALSE,
6446 EL_EMC_FAKE_ACID, -1, -1
6449 Xfake_acid_6, FALSE, FALSE,
6450 EL_EMC_FAKE_ACID, -1, -1
6453 Xfake_acid_7, FALSE, FALSE,
6454 EL_EMC_FAKE_ACID, -1, -1
6457 Xfake_acid_8, FALSE, FALSE,
6458 EL_EMC_FAKE_ACID, -1, -1
6461 Xsteel_1, TRUE, FALSE,
6462 EL_STEELWALL, -1, -1
6465 Xsteel_2, TRUE, FALSE,
6466 EL_EMC_STEELWALL_2, -1, -1
6469 Xsteel_3, TRUE, FALSE,
6470 EL_EMC_STEELWALL_3, -1, -1
6473 Xsteel_4, TRUE, FALSE,
6474 EL_EMC_STEELWALL_4, -1, -1
6477 Xwall_1, TRUE, FALSE,
6481 Xwall_2, TRUE, FALSE,
6482 EL_EMC_WALL_14, -1, -1
6485 Xwall_3, TRUE, FALSE,
6486 EL_EMC_WALL_15, -1, -1
6489 Xwall_4, TRUE, FALSE,
6490 EL_EMC_WALL_16, -1, -1
6493 Xround_wall_1, TRUE, FALSE,
6494 EL_WALL_SLIPPERY, -1, -1
6497 Xround_wall_2, TRUE, FALSE,
6498 EL_EMC_WALL_SLIPPERY_2, -1, -1
6501 Xround_wall_3, TRUE, FALSE,
6502 EL_EMC_WALL_SLIPPERY_3, -1, -1
6505 Xround_wall_4, TRUE, FALSE,
6506 EL_EMC_WALL_SLIPPERY_4, -1, -1
6509 Xdecor_1, TRUE, FALSE,
6510 EL_EMC_WALL_8, -1, -1
6513 Xdecor_2, TRUE, FALSE,
6514 EL_EMC_WALL_6, -1, -1
6517 Xdecor_3, TRUE, FALSE,
6518 EL_EMC_WALL_4, -1, -1
6521 Xdecor_4, TRUE, FALSE,
6522 EL_EMC_WALL_7, -1, -1
6525 Xdecor_5, TRUE, FALSE,
6526 EL_EMC_WALL_5, -1, -1
6529 Xdecor_6, TRUE, FALSE,
6530 EL_EMC_WALL_9, -1, -1
6533 Xdecor_7, TRUE, FALSE,
6534 EL_EMC_WALL_10, -1, -1
6537 Xdecor_8, TRUE, FALSE,
6538 EL_EMC_WALL_1, -1, -1
6541 Xdecor_9, TRUE, FALSE,
6542 EL_EMC_WALL_2, -1, -1
6545 Xdecor_10, TRUE, FALSE,
6546 EL_EMC_WALL_3, -1, -1
6549 Xdecor_11, TRUE, FALSE,
6550 EL_EMC_WALL_11, -1, -1
6553 Xdecor_12, TRUE, FALSE,
6554 EL_EMC_WALL_12, -1, -1
6557 Xalpha_0, TRUE, FALSE,
6558 EL_CHAR('0'), -1, -1
6561 Xalpha_1, TRUE, FALSE,
6562 EL_CHAR('1'), -1, -1
6565 Xalpha_2, TRUE, FALSE,
6566 EL_CHAR('2'), -1, -1
6569 Xalpha_3, TRUE, FALSE,
6570 EL_CHAR('3'), -1, -1
6573 Xalpha_4, TRUE, FALSE,
6574 EL_CHAR('4'), -1, -1
6577 Xalpha_5, TRUE, FALSE,
6578 EL_CHAR('5'), -1, -1
6581 Xalpha_6, TRUE, FALSE,
6582 EL_CHAR('6'), -1, -1
6585 Xalpha_7, TRUE, FALSE,
6586 EL_CHAR('7'), -1, -1
6589 Xalpha_8, TRUE, FALSE,
6590 EL_CHAR('8'), -1, -1
6593 Xalpha_9, TRUE, FALSE,
6594 EL_CHAR('9'), -1, -1
6597 Xalpha_excla, TRUE, FALSE,
6598 EL_CHAR('!'), -1, -1
6601 Xalpha_quote, TRUE, FALSE,
6602 EL_CHAR('"'), -1, -1
6605 Xalpha_comma, TRUE, FALSE,
6606 EL_CHAR(','), -1, -1
6609 Xalpha_minus, TRUE, FALSE,
6610 EL_CHAR('-'), -1, -1
6613 Xalpha_perio, TRUE, FALSE,
6614 EL_CHAR('.'), -1, -1
6617 Xalpha_colon, TRUE, FALSE,
6618 EL_CHAR(':'), -1, -1
6621 Xalpha_quest, TRUE, FALSE,
6622 EL_CHAR('?'), -1, -1
6625 Xalpha_a, TRUE, FALSE,
6626 EL_CHAR('A'), -1, -1
6629 Xalpha_b, TRUE, FALSE,
6630 EL_CHAR('B'), -1, -1
6633 Xalpha_c, TRUE, FALSE,
6634 EL_CHAR('C'), -1, -1
6637 Xalpha_d, TRUE, FALSE,
6638 EL_CHAR('D'), -1, -1
6641 Xalpha_e, TRUE, FALSE,
6642 EL_CHAR('E'), -1, -1
6645 Xalpha_f, TRUE, FALSE,
6646 EL_CHAR('F'), -1, -1
6649 Xalpha_g, TRUE, FALSE,
6650 EL_CHAR('G'), -1, -1
6653 Xalpha_h, TRUE, FALSE,
6654 EL_CHAR('H'), -1, -1
6657 Xalpha_i, TRUE, FALSE,
6658 EL_CHAR('I'), -1, -1
6661 Xalpha_j, TRUE, FALSE,
6662 EL_CHAR('J'), -1, -1
6665 Xalpha_k, TRUE, FALSE,
6666 EL_CHAR('K'), -1, -1
6669 Xalpha_l, TRUE, FALSE,
6670 EL_CHAR('L'), -1, -1
6673 Xalpha_m, TRUE, FALSE,
6674 EL_CHAR('M'), -1, -1
6677 Xalpha_n, TRUE, FALSE,
6678 EL_CHAR('N'), -1, -1
6681 Xalpha_o, TRUE, FALSE,
6682 EL_CHAR('O'), -1, -1
6685 Xalpha_p, TRUE, FALSE,
6686 EL_CHAR('P'), -1, -1
6689 Xalpha_q, TRUE, FALSE,
6690 EL_CHAR('Q'), -1, -1
6693 Xalpha_r, TRUE, FALSE,
6694 EL_CHAR('R'), -1, -1
6697 Xalpha_s, TRUE, FALSE,
6698 EL_CHAR('S'), -1, -1
6701 Xalpha_t, TRUE, FALSE,
6702 EL_CHAR('T'), -1, -1
6705 Xalpha_u, TRUE, FALSE,
6706 EL_CHAR('U'), -1, -1
6709 Xalpha_v, TRUE, FALSE,
6710 EL_CHAR('V'), -1, -1
6713 Xalpha_w, TRUE, FALSE,
6714 EL_CHAR('W'), -1, -1
6717 Xalpha_x, TRUE, FALSE,
6718 EL_CHAR('X'), -1, -1
6721 Xalpha_y, TRUE, FALSE,
6722 EL_CHAR('Y'), -1, -1
6725 Xalpha_z, TRUE, FALSE,
6726 EL_CHAR('Z'), -1, -1
6729 Xalpha_arrow_e, TRUE, FALSE,
6730 EL_CHAR('>'), -1, -1
6733 Xalpha_arrow_w, TRUE, FALSE,
6734 EL_CHAR('<'), -1, -1
6737 Xalpha_copyr, TRUE, FALSE,
6738 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6742 Xboom_bug, FALSE, FALSE,
6743 EL_BUG, ACTION_EXPLODING, -1
6746 Xboom_bomb, FALSE, FALSE,
6747 EL_BOMB, ACTION_EXPLODING, -1
6750 Xboom_android, FALSE, FALSE,
6751 EL_EMC_ANDROID, ACTION_OTHER, -1
6754 Xboom_1, FALSE, FALSE,
6755 EL_DEFAULT, ACTION_EXPLODING, -1
6758 Xboom_2, FALSE, FALSE,
6759 EL_DEFAULT, ACTION_EXPLODING, -1
6762 Znormal, FALSE, FALSE,
6766 Zdynamite, FALSE, FALSE,
6770 Zplayer, FALSE, FALSE,
6774 ZBORDER, FALSE, FALSE,
6784 static struct Mapping_EM_to_RND_player
6793 em_player_mapping_list[] =
6797 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6801 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6805 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6809 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6813 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6817 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6821 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6825 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6829 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6833 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6837 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6841 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6845 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6849 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6853 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6857 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6861 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6865 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6869 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6873 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6877 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6881 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6885 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6889 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6893 EL_PLAYER_1, ACTION_DEFAULT, -1,
6897 EL_PLAYER_2, ACTION_DEFAULT, -1,
6901 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6905 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6909 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6913 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6917 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6921 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6925 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6929 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6933 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6937 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6941 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6945 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6949 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6953 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6957 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6961 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6965 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6969 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6973 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6977 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6981 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6985 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6989 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6993 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6997 EL_PLAYER_3, ACTION_DEFAULT, -1,
7001 EL_PLAYER_4, ACTION_DEFAULT, -1,
7010 int map_element_RND_to_EM(int element_rnd)
7012 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
7013 static boolean mapping_initialized = FALSE;
7015 if (!mapping_initialized)
7019 /* return "Xalpha_quest" for all undefined elements in mapping array */
7020 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
7021 mapping_RND_to_EM[i] = Xalpha_quest;
7023 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7024 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
7025 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
7026 em_object_mapping_list[i].element_em;
7028 mapping_initialized = TRUE;
7031 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
7032 return mapping_RND_to_EM[element_rnd];
7034 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
7039 int map_element_EM_to_RND(int element_em)
7041 static unsigned short mapping_EM_to_RND[TILE_MAX];
7042 static boolean mapping_initialized = FALSE;
7044 if (!mapping_initialized)
7048 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
7049 for (i = 0; i < TILE_MAX; i++)
7050 mapping_EM_to_RND[i] = EL_UNKNOWN;
7052 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7053 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
7054 em_object_mapping_list[i].element_rnd;
7056 mapping_initialized = TRUE;
7059 if (element_em >= 0 && element_em < TILE_MAX)
7060 return mapping_EM_to_RND[element_em];
7062 Error(ERR_WARN, "invalid EM level element %d", element_em);
7067 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
7069 struct LevelInfo_EM *level_em = level->native_em_level;
7070 struct LEVEL *lev = level_em->lev;
7073 for (i = 0; i < TILE_MAX; i++)
7074 lev->android_array[i] = Xblank;
7076 for (i = 0; i < level->num_android_clone_elements; i++)
7078 int element_rnd = level->android_clone_element[i];
7079 int element_em = map_element_RND_to_EM(element_rnd);
7081 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
7082 if (em_object_mapping_list[j].element_rnd == element_rnd)
7083 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7087 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7089 struct LevelInfo_EM *level_em = level->native_em_level;
7090 struct LEVEL *lev = level_em->lev;
7093 level->num_android_clone_elements = 0;
7095 for (i = 0; i < TILE_MAX; i++)
7097 int element_em = lev->android_array[i];
7099 boolean element_found = FALSE;
7101 if (element_em == Xblank)
7104 element_rnd = map_element_EM_to_RND(element_em);
7106 for (j = 0; j < level->num_android_clone_elements; j++)
7107 if (level->android_clone_element[j] == element_rnd)
7108 element_found = TRUE;
7112 level->android_clone_element[level->num_android_clone_elements++] =
7115 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7120 if (level->num_android_clone_elements == 0)
7122 level->num_android_clone_elements = 1;
7123 level->android_clone_element[0] = EL_EMPTY;
7127 int map_direction_RND_to_EM(int direction)
7129 return (direction == MV_UP ? 0 :
7130 direction == MV_RIGHT ? 1 :
7131 direction == MV_DOWN ? 2 :
7132 direction == MV_LEFT ? 3 :
7136 int map_direction_EM_to_RND(int direction)
7138 return (direction == 0 ? MV_UP :
7139 direction == 1 ? MV_RIGHT :
7140 direction == 2 ? MV_DOWN :
7141 direction == 3 ? MV_LEFT :
7145 int map_element_RND_to_SP(int element_rnd)
7147 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
7149 if (element_rnd >= EL_SP_START &&
7150 element_rnd <= EL_SP_END)
7151 element_sp = element_rnd - EL_SP_START;
7152 else if (element_rnd == EL_EMPTY_SPACE)
7154 else if (element_rnd == EL_INVISIBLE_WALL)
7160 int map_element_SP_to_RND(int element_sp)
7162 int element_rnd = EL_UNKNOWN;
7164 if (element_sp >= 0x00 &&
7166 element_rnd = EL_SP_START + element_sp;
7167 else if (element_sp == 0x28)
7168 element_rnd = EL_INVISIBLE_WALL;
7173 int map_action_SP_to_RND(int action_sp)
7177 case actActive: return ACTION_ACTIVE;
7178 case actImpact: return ACTION_IMPACT;
7179 case actExploding: return ACTION_EXPLODING;
7180 case actDigging: return ACTION_DIGGING;
7181 case actSnapping: return ACTION_SNAPPING;
7182 case actCollecting: return ACTION_COLLECTING;
7183 case actPassing: return ACTION_PASSING;
7184 case actPushing: return ACTION_PUSHING;
7185 case actDropping: return ACTION_DROPPING;
7187 default: return ACTION_DEFAULT;
7191 int get_next_element(int element)
7195 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7196 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7197 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7198 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7199 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7200 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7201 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7202 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7203 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7204 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7205 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7207 default: return element;
7211 int el_act_dir2img(int element, int action, int direction)
7213 element = GFX_ELEMENT(element);
7214 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7216 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7217 return element_info[element].direction_graphic[action][direction];
7220 static int el_act_dir2crm(int element, int action, int direction)
7222 element = GFX_ELEMENT(element);
7223 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7225 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7226 return element_info[element].direction_crumbled[action][direction];
7229 int el_act2img(int element, int action)
7231 element = GFX_ELEMENT(element);
7233 return element_info[element].graphic[action];
7236 int el_act2crm(int element, int action)
7238 element = GFX_ELEMENT(element);
7240 return element_info[element].crumbled[action];
7243 int el_dir2img(int element, int direction)
7245 element = GFX_ELEMENT(element);
7247 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7250 int el2baseimg(int element)
7252 return element_info[element].graphic[ACTION_DEFAULT];
7255 int el2img(int element)
7257 element = GFX_ELEMENT(element);
7259 return element_info[element].graphic[ACTION_DEFAULT];
7262 int el2edimg(int element)
7264 element = GFX_ELEMENT(element);
7266 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7269 int el2preimg(int element)
7271 element = GFX_ELEMENT(element);
7273 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7276 int el2panelimg(int element)
7278 element = GFX_ELEMENT(element);
7280 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7283 int font2baseimg(int font_nr)
7285 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7288 int getBeltNrFromBeltElement(int element)
7290 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7291 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7292 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
7295 int getBeltNrFromBeltActiveElement(int element)
7297 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
7298 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
7299 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
7302 int getBeltNrFromBeltSwitchElement(int element)
7304 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
7305 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
7306 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
7309 int getBeltDirNrFromBeltElement(int element)
7311 static int belt_base_element[4] =
7313 EL_CONVEYOR_BELT_1_LEFT,
7314 EL_CONVEYOR_BELT_2_LEFT,
7315 EL_CONVEYOR_BELT_3_LEFT,
7316 EL_CONVEYOR_BELT_4_LEFT
7319 int belt_nr = getBeltNrFromBeltElement(element);
7320 int belt_dir_nr = element - belt_base_element[belt_nr];
7322 return (belt_dir_nr % 3);
7325 int getBeltDirNrFromBeltSwitchElement(int element)
7327 static int belt_base_element[4] =
7329 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7330 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7331 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7332 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7335 int belt_nr = getBeltNrFromBeltSwitchElement(element);
7336 int belt_dir_nr = element - belt_base_element[belt_nr];
7338 return (belt_dir_nr % 3);
7341 int getBeltDirFromBeltElement(int element)
7343 static int belt_move_dir[3] =
7350 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7352 return belt_move_dir[belt_dir_nr];
7355 int getBeltDirFromBeltSwitchElement(int element)
7357 static int belt_move_dir[3] =
7364 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7366 return belt_move_dir[belt_dir_nr];
7369 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7371 static int belt_base_element[4] =
7373 EL_CONVEYOR_BELT_1_LEFT,
7374 EL_CONVEYOR_BELT_2_LEFT,
7375 EL_CONVEYOR_BELT_3_LEFT,
7376 EL_CONVEYOR_BELT_4_LEFT
7379 return belt_base_element[belt_nr] + belt_dir_nr;
7382 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7384 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7386 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7389 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7391 static int belt_base_element[4] =
7393 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7394 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7395 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7396 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7399 return belt_base_element[belt_nr] + belt_dir_nr;
7402 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7404 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7406 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7409 boolean getTeamMode_EM()
7411 return game.team_mode;
7414 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7416 int game_frame_delay_value;
7418 game_frame_delay_value =
7419 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7420 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7423 if (tape.playing && tape.warp_forward && !tape.pausing)
7424 game_frame_delay_value = 0;
7426 return game_frame_delay_value;
7429 unsigned int InitRND(int seed)
7431 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7432 return InitEngineRandom_EM(seed);
7433 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7434 return InitEngineRandom_SP(seed);
7436 return InitEngineRandom_RND(seed);
7439 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7440 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7442 inline static int get_effective_element_EM(int tile, int frame_em)
7444 int element = object_mapping[tile].element_rnd;
7445 int action = object_mapping[tile].action;
7446 boolean is_backside = object_mapping[tile].is_backside;
7447 boolean action_removing = (action == ACTION_DIGGING ||
7448 action == ACTION_SNAPPING ||
7449 action == ACTION_COLLECTING);
7455 case Yacid_splash_eB:
7456 case Yacid_splash_wB:
7457 return (frame_em > 5 ? EL_EMPTY : element);
7463 else /* frame_em == 7 */
7467 case Yacid_splash_eB:
7468 case Yacid_splash_wB:
7471 case Yemerald_stone:
7474 case Ydiamond_stone:
7478 case Xdrip_stretchB:
7497 case Xsand_stonein_1:
7498 case Xsand_stonein_2:
7499 case Xsand_stonein_3:
7500 case Xsand_stonein_4:
7504 return (is_backside || action_removing ? EL_EMPTY : element);
7509 inline static boolean check_linear_animation_EM(int tile)
7513 case Xsand_stonesand_1:
7514 case Xsand_stonesand_quickout_1:
7515 case Xsand_sandstone_1:
7516 case Xsand_stonein_1:
7517 case Xsand_stoneout_1:
7536 case Yacid_splash_eB:
7537 case Yacid_splash_wB:
7538 case Yemerald_stone:
7545 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7546 boolean has_crumbled_graphics,
7547 int crumbled, int sync_frame)
7549 /* if element can be crumbled, but certain action graphics are just empty
7550 space (like instantly snapping sand to empty space in 1 frame), do not
7551 treat these empty space graphics as crumbled graphics in EMC engine */
7552 if (crumbled == IMG_EMPTY_SPACE)
7553 has_crumbled_graphics = FALSE;
7555 if (has_crumbled_graphics)
7557 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7558 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7559 g_crumbled->anim_delay,
7560 g_crumbled->anim_mode,
7561 g_crumbled->anim_start_frame,
7564 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7565 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7567 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7568 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
7570 g_em->has_crumbled_graphics = TRUE;
7574 g_em->crumbled_bitmap = NULL;
7575 g_em->crumbled_src_x = 0;
7576 g_em->crumbled_src_y = 0;
7577 g_em->crumbled_border_size = 0;
7578 g_em->crumbled_tile_size = 0;
7580 g_em->has_crumbled_graphics = FALSE;
7584 void ResetGfxAnimation_EM(int x, int y, int tile)
7589 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7590 int tile, int frame_em, int x, int y)
7592 int action = object_mapping[tile].action;
7593 int direction = object_mapping[tile].direction;
7594 int effective_element = get_effective_element_EM(tile, frame_em);
7595 int graphic = (direction == MV_NONE ?
7596 el_act2img(effective_element, action) :
7597 el_act_dir2img(effective_element, action, direction));
7598 struct GraphicInfo *g = &graphic_info[graphic];
7600 boolean action_removing = (action == ACTION_DIGGING ||
7601 action == ACTION_SNAPPING ||
7602 action == ACTION_COLLECTING);
7603 boolean action_moving = (action == ACTION_FALLING ||
7604 action == ACTION_MOVING ||
7605 action == ACTION_PUSHING ||
7606 action == ACTION_EATING ||
7607 action == ACTION_FILLING ||
7608 action == ACTION_EMPTYING);
7609 boolean action_falling = (action == ACTION_FALLING ||
7610 action == ACTION_FILLING ||
7611 action == ACTION_EMPTYING);
7613 /* special case: graphic uses "2nd movement tile" and has defined
7614 7 frames for movement animation (or less) => use default graphic
7615 for last (8th) frame which ends the movement animation */
7616 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7618 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7619 graphic = (direction == MV_NONE ?
7620 el_act2img(effective_element, action) :
7621 el_act_dir2img(effective_element, action, direction));
7623 g = &graphic_info[graphic];
7626 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7630 else if (action_moving)
7632 boolean is_backside = object_mapping[tile].is_backside;
7636 int direction = object_mapping[tile].direction;
7637 int move_dir = (action_falling ? MV_DOWN : direction);
7642 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7643 if (g->double_movement && frame_em == 0)
7647 if (move_dir == MV_LEFT)
7648 GfxFrame[x - 1][y] = GfxFrame[x][y];
7649 else if (move_dir == MV_RIGHT)
7650 GfxFrame[x + 1][y] = GfxFrame[x][y];
7651 else if (move_dir == MV_UP)
7652 GfxFrame[x][y - 1] = GfxFrame[x][y];
7653 else if (move_dir == MV_DOWN)
7654 GfxFrame[x][y + 1] = GfxFrame[x][y];
7661 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7662 if (tile == Xsand_stonesand_quickout_1 ||
7663 tile == Xsand_stonesand_quickout_2)
7667 if (graphic_info[graphic].anim_global_sync)
7668 sync_frame = FrameCounter;
7669 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7670 sync_frame = GfxFrame[x][y];
7672 sync_frame = 0; /* playfield border (pseudo steel) */
7674 SetRandomAnimationValue(x, y);
7676 int frame = getAnimationFrame(g->anim_frames,
7679 g->anim_start_frame,
7682 g_em->unique_identifier =
7683 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7686 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7687 int tile, int frame_em, int x, int y)
7689 int action = object_mapping[tile].action;
7690 int direction = object_mapping[tile].direction;
7691 boolean is_backside = object_mapping[tile].is_backside;
7692 int effective_element = get_effective_element_EM(tile, frame_em);
7693 int effective_action = action;
7694 int graphic = (direction == MV_NONE ?
7695 el_act2img(effective_element, effective_action) :
7696 el_act_dir2img(effective_element, effective_action,
7698 int crumbled = (direction == MV_NONE ?
7699 el_act2crm(effective_element, effective_action) :
7700 el_act_dir2crm(effective_element, effective_action,
7702 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7703 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7704 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7705 struct GraphicInfo *g = &graphic_info[graphic];
7708 /* special case: graphic uses "2nd movement tile" and has defined
7709 7 frames for movement animation (or less) => use default graphic
7710 for last (8th) frame which ends the movement animation */
7711 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7713 effective_action = ACTION_DEFAULT;
7714 graphic = (direction == MV_NONE ?
7715 el_act2img(effective_element, effective_action) :
7716 el_act_dir2img(effective_element, effective_action,
7718 crumbled = (direction == MV_NONE ?
7719 el_act2crm(effective_element, effective_action) :
7720 el_act_dir2crm(effective_element, effective_action,
7723 g = &graphic_info[graphic];
7726 if (graphic_info[graphic].anim_global_sync)
7727 sync_frame = FrameCounter;
7728 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7729 sync_frame = GfxFrame[x][y];
7731 sync_frame = 0; /* playfield border (pseudo steel) */
7733 SetRandomAnimationValue(x, y);
7735 int frame = getAnimationFrame(g->anim_frames,
7738 g->anim_start_frame,
7741 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7742 g->double_movement && is_backside);
7744 /* (updating the "crumbled" graphic definitions is probably not really needed,
7745 as animations for crumbled graphics can't be longer than one EMC cycle) */
7746 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7750 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7751 int player_nr, int anim, int frame_em)
7753 int element = player_mapping[player_nr][anim].element_rnd;
7754 int action = player_mapping[player_nr][anim].action;
7755 int direction = player_mapping[player_nr][anim].direction;
7756 int graphic = (direction == MV_NONE ?
7757 el_act2img(element, action) :
7758 el_act_dir2img(element, action, direction));
7759 struct GraphicInfo *g = &graphic_info[graphic];
7762 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7764 stored_player[player_nr].StepFrame = frame_em;
7766 sync_frame = stored_player[player_nr].Frame;
7768 int frame = getAnimationFrame(g->anim_frames,
7771 g->anim_start_frame,
7774 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7775 &g_em->src_x, &g_em->src_y, FALSE);
7778 void InitGraphicInfo_EM(void)
7783 int num_em_gfx_errors = 0;
7785 if (graphic_info_em_object[0][0].bitmap == NULL)
7787 /* EM graphics not yet initialized in em_open_all() */
7792 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7795 /* always start with reliable default values */
7796 for (i = 0; i < TILE_MAX; i++)
7798 object_mapping[i].element_rnd = EL_UNKNOWN;
7799 object_mapping[i].is_backside = FALSE;
7800 object_mapping[i].action = ACTION_DEFAULT;
7801 object_mapping[i].direction = MV_NONE;
7804 /* always start with reliable default values */
7805 for (p = 0; p < MAX_PLAYERS; p++)
7807 for (i = 0; i < SPR_MAX; i++)
7809 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7810 player_mapping[p][i].action = ACTION_DEFAULT;
7811 player_mapping[p][i].direction = MV_NONE;
7815 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7817 int e = em_object_mapping_list[i].element_em;
7819 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7820 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7822 if (em_object_mapping_list[i].action != -1)
7823 object_mapping[e].action = em_object_mapping_list[i].action;
7825 if (em_object_mapping_list[i].direction != -1)
7826 object_mapping[e].direction =
7827 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7830 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7832 int a = em_player_mapping_list[i].action_em;
7833 int p = em_player_mapping_list[i].player_nr;
7835 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7837 if (em_player_mapping_list[i].action != -1)
7838 player_mapping[p][a].action = em_player_mapping_list[i].action;
7840 if (em_player_mapping_list[i].direction != -1)
7841 player_mapping[p][a].direction =
7842 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7845 for (i = 0; i < TILE_MAX; i++)
7847 int element = object_mapping[i].element_rnd;
7848 int action = object_mapping[i].action;
7849 int direction = object_mapping[i].direction;
7850 boolean is_backside = object_mapping[i].is_backside;
7851 boolean action_exploding = ((action == ACTION_EXPLODING ||
7852 action == ACTION_SMASHED_BY_ROCK ||
7853 action == ACTION_SMASHED_BY_SPRING) &&
7854 element != EL_DIAMOND);
7855 boolean action_active = (action == ACTION_ACTIVE);
7856 boolean action_other = (action == ACTION_OTHER);
7858 for (j = 0; j < 8; j++)
7860 int effective_element = get_effective_element_EM(i, j);
7861 int effective_action = (j < 7 ? action :
7862 i == Xdrip_stretch ? action :
7863 i == Xdrip_stretchB ? action :
7864 i == Ydrip_s1 ? action :
7865 i == Ydrip_s1B ? action :
7866 i == Xball_1B ? action :
7867 i == Xball_2 ? action :
7868 i == Xball_2B ? action :
7869 i == Yball_eat ? action :
7870 i == Ykey_1_eat ? action :
7871 i == Ykey_2_eat ? action :
7872 i == Ykey_3_eat ? action :
7873 i == Ykey_4_eat ? action :
7874 i == Ykey_5_eat ? action :
7875 i == Ykey_6_eat ? action :
7876 i == Ykey_7_eat ? action :
7877 i == Ykey_8_eat ? action :
7878 i == Ylenses_eat ? action :
7879 i == Ymagnify_eat ? action :
7880 i == Ygrass_eat ? action :
7881 i == Ydirt_eat ? action :
7882 i == Xsand_stonein_1 ? action :
7883 i == Xsand_stonein_2 ? action :
7884 i == Xsand_stonein_3 ? action :
7885 i == Xsand_stonein_4 ? action :
7886 i == Xsand_stoneout_1 ? action :
7887 i == Xsand_stoneout_2 ? action :
7888 i == Xboom_android ? ACTION_EXPLODING :
7889 action_exploding ? ACTION_EXPLODING :
7890 action_active ? action :
7891 action_other ? action :
7893 int graphic = (el_act_dir2img(effective_element, effective_action,
7895 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7897 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7898 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7899 boolean has_action_graphics = (graphic != base_graphic);
7900 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7901 struct GraphicInfo *g = &graphic_info[graphic];
7902 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7905 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7906 boolean special_animation = (action != ACTION_DEFAULT &&
7907 g->anim_frames == 3 &&
7908 g->anim_delay == 2 &&
7909 g->anim_mode & ANIM_LINEAR);
7910 int sync_frame = (i == Xdrip_stretch ? 7 :
7911 i == Xdrip_stretchB ? 7 :
7912 i == Ydrip_s2 ? j + 8 :
7913 i == Ydrip_s2B ? j + 8 :
7922 i == Xfake_acid_1 ? 0 :
7923 i == Xfake_acid_2 ? 10 :
7924 i == Xfake_acid_3 ? 20 :
7925 i == Xfake_acid_4 ? 30 :
7926 i == Xfake_acid_5 ? 40 :
7927 i == Xfake_acid_6 ? 50 :
7928 i == Xfake_acid_7 ? 60 :
7929 i == Xfake_acid_8 ? 70 :
7931 i == Xball_2B ? j + 8 :
7932 i == Yball_eat ? j + 1 :
7933 i == Ykey_1_eat ? j + 1 :
7934 i == Ykey_2_eat ? j + 1 :
7935 i == Ykey_3_eat ? j + 1 :
7936 i == Ykey_4_eat ? j + 1 :
7937 i == Ykey_5_eat ? j + 1 :
7938 i == Ykey_6_eat ? j + 1 :
7939 i == Ykey_7_eat ? j + 1 :
7940 i == Ykey_8_eat ? j + 1 :
7941 i == Ylenses_eat ? j + 1 :
7942 i == Ymagnify_eat ? j + 1 :
7943 i == Ygrass_eat ? j + 1 :
7944 i == Ydirt_eat ? j + 1 :
7945 i == Xamoeba_1 ? 0 :
7946 i == Xamoeba_2 ? 1 :
7947 i == Xamoeba_3 ? 2 :
7948 i == Xamoeba_4 ? 3 :
7949 i == Xamoeba_5 ? 0 :
7950 i == Xamoeba_6 ? 1 :
7951 i == Xamoeba_7 ? 2 :
7952 i == Xamoeba_8 ? 3 :
7953 i == Xexit_2 ? j + 8 :
7954 i == Xexit_3 ? j + 16 :
7955 i == Xdynamite_1 ? 0 :
7956 i == Xdynamite_2 ? 8 :
7957 i == Xdynamite_3 ? 16 :
7958 i == Xdynamite_4 ? 24 :
7959 i == Xsand_stonein_1 ? j + 1 :
7960 i == Xsand_stonein_2 ? j + 9 :
7961 i == Xsand_stonein_3 ? j + 17 :
7962 i == Xsand_stonein_4 ? j + 25 :
7963 i == Xsand_stoneout_1 && j == 0 ? 0 :
7964 i == Xsand_stoneout_1 && j == 1 ? 0 :
7965 i == Xsand_stoneout_1 && j == 2 ? 1 :
7966 i == Xsand_stoneout_1 && j == 3 ? 2 :
7967 i == Xsand_stoneout_1 && j == 4 ? 2 :
7968 i == Xsand_stoneout_1 && j == 5 ? 3 :
7969 i == Xsand_stoneout_1 && j == 6 ? 4 :
7970 i == Xsand_stoneout_1 && j == 7 ? 4 :
7971 i == Xsand_stoneout_2 && j == 0 ? 5 :
7972 i == Xsand_stoneout_2 && j == 1 ? 6 :
7973 i == Xsand_stoneout_2 && j == 2 ? 7 :
7974 i == Xsand_stoneout_2 && j == 3 ? 8 :
7975 i == Xsand_stoneout_2 && j == 4 ? 9 :
7976 i == Xsand_stoneout_2 && j == 5 ? 11 :
7977 i == Xsand_stoneout_2 && j == 6 ? 13 :
7978 i == Xsand_stoneout_2 && j == 7 ? 15 :
7979 i == Xboom_bug && j == 1 ? 2 :
7980 i == Xboom_bug && j == 2 ? 2 :
7981 i == Xboom_bug && j == 3 ? 4 :
7982 i == Xboom_bug && j == 4 ? 4 :
7983 i == Xboom_bug && j == 5 ? 2 :
7984 i == Xboom_bug && j == 6 ? 2 :
7985 i == Xboom_bug && j == 7 ? 0 :
7986 i == Xboom_bomb && j == 1 ? 2 :
7987 i == Xboom_bomb && j == 2 ? 2 :
7988 i == Xboom_bomb && j == 3 ? 4 :
7989 i == Xboom_bomb && j == 4 ? 4 :
7990 i == Xboom_bomb && j == 5 ? 2 :
7991 i == Xboom_bomb && j == 6 ? 2 :
7992 i == Xboom_bomb && j == 7 ? 0 :
7993 i == Xboom_android && j == 7 ? 6 :
7994 i == Xboom_1 && j == 1 ? 2 :
7995 i == Xboom_1 && j == 2 ? 2 :
7996 i == Xboom_1 && j == 3 ? 4 :
7997 i == Xboom_1 && j == 4 ? 4 :
7998 i == Xboom_1 && j == 5 ? 6 :
7999 i == Xboom_1 && j == 6 ? 6 :
8000 i == Xboom_1 && j == 7 ? 8 :
8001 i == Xboom_2 && j == 0 ? 8 :
8002 i == Xboom_2 && j == 1 ? 8 :
8003 i == Xboom_2 && j == 2 ? 10 :
8004 i == Xboom_2 && j == 3 ? 10 :
8005 i == Xboom_2 && j == 4 ? 10 :
8006 i == Xboom_2 && j == 5 ? 12 :
8007 i == Xboom_2 && j == 6 ? 12 :
8008 i == Xboom_2 && j == 7 ? 12 :
8009 special_animation && j == 4 ? 3 :
8010 effective_action != action ? 0 :
8014 Bitmap *debug_bitmap = g_em->bitmap;
8015 int debug_src_x = g_em->src_x;
8016 int debug_src_y = g_em->src_y;
8019 int frame = getAnimationFrame(g->anim_frames,
8022 g->anim_start_frame,
8025 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
8026 g->double_movement && is_backside);
8028 g_em->bitmap = src_bitmap;
8029 g_em->src_x = src_x;
8030 g_em->src_y = src_y;
8031 g_em->src_offset_x = 0;
8032 g_em->src_offset_y = 0;
8033 g_em->dst_offset_x = 0;
8034 g_em->dst_offset_y = 0;
8035 g_em->width = TILEX;
8036 g_em->height = TILEY;
8038 g_em->preserve_background = FALSE;
8040 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
8043 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
8044 effective_action == ACTION_MOVING ||
8045 effective_action == ACTION_PUSHING ||
8046 effective_action == ACTION_EATING)) ||
8047 (!has_action_graphics && (effective_action == ACTION_FILLING ||
8048 effective_action == ACTION_EMPTYING)))
8051 (effective_action == ACTION_FALLING ||
8052 effective_action == ACTION_FILLING ||
8053 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
8054 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
8055 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
8056 int num_steps = (i == Ydrip_s1 ? 16 :
8057 i == Ydrip_s1B ? 16 :
8058 i == Ydrip_s2 ? 16 :
8059 i == Ydrip_s2B ? 16 :
8060 i == Xsand_stonein_1 ? 32 :
8061 i == Xsand_stonein_2 ? 32 :
8062 i == Xsand_stonein_3 ? 32 :
8063 i == Xsand_stonein_4 ? 32 :
8064 i == Xsand_stoneout_1 ? 16 :
8065 i == Xsand_stoneout_2 ? 16 : 8);
8066 int cx = ABS(dx) * (TILEX / num_steps);
8067 int cy = ABS(dy) * (TILEY / num_steps);
8068 int step_frame = (i == Ydrip_s2 ? j + 8 :
8069 i == Ydrip_s2B ? j + 8 :
8070 i == Xsand_stonein_2 ? j + 8 :
8071 i == Xsand_stonein_3 ? j + 16 :
8072 i == Xsand_stonein_4 ? j + 24 :
8073 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
8074 int step = (is_backside ? step_frame : num_steps - step_frame);
8076 if (is_backside) /* tile where movement starts */
8078 if (dx < 0 || dy < 0)
8080 g_em->src_offset_x = cx * step;
8081 g_em->src_offset_y = cy * step;
8085 g_em->dst_offset_x = cx * step;
8086 g_em->dst_offset_y = cy * step;
8089 else /* tile where movement ends */
8091 if (dx < 0 || dy < 0)
8093 g_em->dst_offset_x = cx * step;
8094 g_em->dst_offset_y = cy * step;
8098 g_em->src_offset_x = cx * step;
8099 g_em->src_offset_y = cy * step;
8103 g_em->width = TILEX - cx * step;
8104 g_em->height = TILEY - cy * step;
8107 /* create unique graphic identifier to decide if tile must be redrawn */
8108 /* bit 31 - 16 (16 bit): EM style graphic
8109 bit 15 - 12 ( 4 bit): EM style frame
8110 bit 11 - 6 ( 6 bit): graphic width
8111 bit 5 - 0 ( 6 bit): graphic height */
8112 g_em->unique_identifier =
8113 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8117 /* skip check for EMC elements not contained in original EMC artwork */
8118 if (element == EL_EMC_FAKE_ACID)
8121 if (g_em->bitmap != debug_bitmap ||
8122 g_em->src_x != debug_src_x ||
8123 g_em->src_y != debug_src_y ||
8124 g_em->src_offset_x != 0 ||
8125 g_em->src_offset_y != 0 ||
8126 g_em->dst_offset_x != 0 ||
8127 g_em->dst_offset_y != 0 ||
8128 g_em->width != TILEX ||
8129 g_em->height != TILEY)
8131 static int last_i = -1;
8139 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8140 i, element, element_info[element].token_name,
8141 element_action_info[effective_action].suffix, direction);
8143 if (element != effective_element)
8144 printf(" [%d ('%s')]",
8146 element_info[effective_element].token_name);
8150 if (g_em->bitmap != debug_bitmap)
8151 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8152 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8154 if (g_em->src_x != debug_src_x ||
8155 g_em->src_y != debug_src_y)
8156 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8157 j, (is_backside ? 'B' : 'F'),
8158 g_em->src_x, g_em->src_y,
8159 g_em->src_x / 32, g_em->src_y / 32,
8160 debug_src_x, debug_src_y,
8161 debug_src_x / 32, debug_src_y / 32);
8163 if (g_em->src_offset_x != 0 ||
8164 g_em->src_offset_y != 0 ||
8165 g_em->dst_offset_x != 0 ||
8166 g_em->dst_offset_y != 0)
8167 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8169 g_em->src_offset_x, g_em->src_offset_y,
8170 g_em->dst_offset_x, g_em->dst_offset_y);
8172 if (g_em->width != TILEX ||
8173 g_em->height != TILEY)
8174 printf(" %d (%d): size %d,%d should be %d,%d\n",
8176 g_em->width, g_em->height, TILEX, TILEY);
8178 num_em_gfx_errors++;
8185 for (i = 0; i < TILE_MAX; i++)
8187 for (j = 0; j < 8; j++)
8189 int element = object_mapping[i].element_rnd;
8190 int action = object_mapping[i].action;
8191 int direction = object_mapping[i].direction;
8192 boolean is_backside = object_mapping[i].is_backside;
8193 int graphic_action = el_act_dir2img(element, action, direction);
8194 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8196 if ((action == ACTION_SMASHED_BY_ROCK ||
8197 action == ACTION_SMASHED_BY_SPRING ||
8198 action == ACTION_EATING) &&
8199 graphic_action == graphic_default)
8201 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8202 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8203 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8204 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8207 /* no separate animation for "smashed by rock" -- use rock instead */
8208 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8209 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8211 g_em->bitmap = g_xx->bitmap;
8212 g_em->src_x = g_xx->src_x;
8213 g_em->src_y = g_xx->src_y;
8214 g_em->src_offset_x = g_xx->src_offset_x;
8215 g_em->src_offset_y = g_xx->src_offset_y;
8216 g_em->dst_offset_x = g_xx->dst_offset_x;
8217 g_em->dst_offset_y = g_xx->dst_offset_y;
8218 g_em->width = g_xx->width;
8219 g_em->height = g_xx->height;
8220 g_em->unique_identifier = g_xx->unique_identifier;
8223 g_em->preserve_background = TRUE;
8228 for (p = 0; p < MAX_PLAYERS; p++)
8230 for (i = 0; i < SPR_MAX; i++)
8232 int element = player_mapping[p][i].element_rnd;
8233 int action = player_mapping[p][i].action;
8234 int direction = player_mapping[p][i].direction;
8236 for (j = 0; j < 8; j++)
8238 int effective_element = element;
8239 int effective_action = action;
8240 int graphic = (direction == MV_NONE ?
8241 el_act2img(effective_element, effective_action) :
8242 el_act_dir2img(effective_element, effective_action,
8244 struct GraphicInfo *g = &graphic_info[graphic];
8245 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8251 Bitmap *debug_bitmap = g_em->bitmap;
8252 int debug_src_x = g_em->src_x;
8253 int debug_src_y = g_em->src_y;
8256 int frame = getAnimationFrame(g->anim_frames,
8259 g->anim_start_frame,
8262 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8264 g_em->bitmap = src_bitmap;
8265 g_em->src_x = src_x;
8266 g_em->src_y = src_y;
8267 g_em->src_offset_x = 0;
8268 g_em->src_offset_y = 0;
8269 g_em->dst_offset_x = 0;
8270 g_em->dst_offset_y = 0;
8271 g_em->width = TILEX;
8272 g_em->height = TILEY;
8276 /* skip check for EMC elements not contained in original EMC artwork */
8277 if (element == EL_PLAYER_3 ||
8278 element == EL_PLAYER_4)
8281 if (g_em->bitmap != debug_bitmap ||
8282 g_em->src_x != debug_src_x ||
8283 g_em->src_y != debug_src_y)
8285 static int last_i = -1;
8293 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
8294 p, i, element, element_info[element].token_name,
8295 element_action_info[effective_action].suffix, direction);
8297 if (element != effective_element)
8298 printf(" [%d ('%s')]",
8300 element_info[effective_element].token_name);
8304 if (g_em->bitmap != debug_bitmap)
8305 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
8306 j, (int)(g_em->bitmap), (int)(debug_bitmap));
8308 if (g_em->src_x != debug_src_x ||
8309 g_em->src_y != debug_src_y)
8310 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8312 g_em->src_x, g_em->src_y,
8313 g_em->src_x / 32, g_em->src_y / 32,
8314 debug_src_x, debug_src_y,
8315 debug_src_x / 32, debug_src_y / 32);
8317 num_em_gfx_errors++;
8327 printf("::: [%d errors found]\n", num_em_gfx_errors);
8333 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
8334 boolean any_player_moving,
8335 boolean any_player_snapping,
8336 boolean any_player_dropping)
8338 if (frame == 0 && !any_player_dropping)
8340 if (!local_player->was_waiting)
8342 if (!CheckSaveEngineSnapshotToList())
8345 local_player->was_waiting = TRUE;
8348 else if (any_player_moving || any_player_snapping || any_player_dropping)
8350 local_player->was_waiting = FALSE;
8354 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8355 boolean murphy_is_dropping)
8357 if (murphy_is_waiting)
8359 if (!local_player->was_waiting)
8361 if (!CheckSaveEngineSnapshotToList())
8364 local_player->was_waiting = TRUE;
8369 local_player->was_waiting = FALSE;
8373 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8374 boolean any_player_moving,
8375 boolean any_player_snapping,
8376 boolean any_player_dropping)
8378 if (tape.single_step && tape.recording && !tape.pausing)
8379 if (frame == 0 && !any_player_dropping)
8380 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8382 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8383 any_player_snapping, any_player_dropping);
8386 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8387 boolean murphy_is_dropping)
8389 boolean murphy_starts_dropping = FALSE;
8392 for (i = 0; i < MAX_PLAYERS; i++)
8393 if (stored_player[i].force_dropping)
8394 murphy_starts_dropping = TRUE;
8396 if (tape.single_step && tape.recording && !tape.pausing)
8397 if (murphy_is_waiting && !murphy_starts_dropping)
8398 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8400 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8403 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8404 int graphic, int sync_frame, int x, int y)
8406 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8408 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8411 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8413 return (IS_NEXT_FRAME(sync_frame, graphic));
8416 int getGraphicInfo_Delay(int graphic)
8418 return graphic_info[graphic].anim_delay;
8421 void PlayMenuSoundExt(int sound)
8423 if (sound == SND_UNDEFINED)
8426 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8427 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8430 if (IS_LOOP_SOUND(sound))
8431 PlaySoundLoop(sound);
8436 void PlayMenuSound()
8438 PlayMenuSoundExt(menu.sound[game_status]);
8441 void PlayMenuSoundStereo(int sound, int stereo_position)
8443 if (sound == SND_UNDEFINED)
8446 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8447 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8450 if (IS_LOOP_SOUND(sound))
8451 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8453 PlaySoundStereo(sound, stereo_position);
8456 void PlayMenuSoundIfLoopExt(int sound)
8458 if (sound == SND_UNDEFINED)
8461 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8462 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8465 if (IS_LOOP_SOUND(sound))
8466 PlaySoundLoop(sound);
8469 void PlayMenuSoundIfLoop()
8471 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8474 void PlayMenuMusicExt(int music)
8476 if (music == MUS_UNDEFINED)
8479 if (!setup.sound_music)
8485 void PlayMenuMusic()
8487 char *curr_music = getCurrentlyPlayingMusicFilename();
8488 char *next_music = getMusicListEntry(menu.music[game_status])->filename;
8490 if (!strEqual(curr_music, next_music))
8491 PlayMenuMusicExt(menu.music[game_status]);
8494 void PlayMenuSoundsAndMusic()
8500 static void FadeMenuSounds()
8505 static void FadeMenuMusic()
8507 char *curr_music = getCurrentlyPlayingMusicFilename();
8508 char *next_music = getMusicListEntry(menu.music[game_status])->filename;
8510 if (!strEqual(curr_music, next_music))
8514 void FadeMenuSoundsAndMusic()
8520 void PlaySoundActivating()
8523 PlaySound(SND_MENU_ITEM_ACTIVATING);
8527 void PlaySoundSelecting()
8530 PlaySound(SND_MENU_ITEM_SELECTING);
8534 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8536 boolean change_fullscreen = (setup.fullscreen !=
8537 video.fullscreen_enabled);
8538 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8539 setup.window_scaling_percent !=
8540 video.window_scaling_percent);
8542 if (change_window_scaling_percent && video.fullscreen_enabled)
8545 if (!change_window_scaling_percent && !video.fullscreen_available)
8548 #if defined(TARGET_SDL2)
8549 if (change_window_scaling_percent)
8551 SDLSetWindowScaling(setup.window_scaling_percent);
8555 else if (change_fullscreen)
8557 SDLSetWindowFullscreen(setup.fullscreen);
8559 /* set setup value according to successfully changed fullscreen mode */
8560 setup.fullscreen = video.fullscreen_enabled;
8566 if (change_fullscreen ||
8567 change_window_scaling_percent)
8569 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8571 /* save backbuffer content which gets lost when toggling fullscreen mode */
8572 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8574 if (change_window_scaling_percent)
8576 /* keep window mode, but change window scaling */
8577 video.fullscreen_enabled = TRUE; /* force new window scaling */
8580 /* toggle fullscreen */
8581 ChangeVideoModeIfNeeded(setup.fullscreen);
8583 /* set setup value according to successfully changed fullscreen mode */
8584 setup.fullscreen = video.fullscreen_enabled;
8586 /* restore backbuffer content from temporary backbuffer backup bitmap */
8587 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8589 FreeBitmap(tmp_backbuffer);
8591 /* update visible window/screen */
8592 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8596 void JoinRectangles(int *x, int *y, int *width, int *height,
8597 int x2, int y2, int width2, int height2)
8599 // do not join with "off-screen" rectangle
8600 if (x2 == -1 || y2 == -1)
8605 *width = MAX(*width, width2);
8606 *height = MAX(*height, height2);
8609 void SetAnimStatus(int anim_status_new)
8611 if (anim_status_new == GAME_MODE_MAIN)
8612 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
8613 else if (anim_status_new == GAME_MODE_SCORES)
8614 anim_status_new = GAME_MODE_PSEUDO_SCORESOLD;
8616 global.anim_status_next = anim_status_new;
8618 // directly set screen modes that are entered without fading
8619 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
8620 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
8621 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
8622 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
8623 global.anim_status = global.anim_status_next;
8626 void SetGameStatus(int game_status_new)
8628 if (game_status_new != game_status)
8629 game_status_last_screen = game_status;
8631 game_status = game_status_new;
8633 SetAnimStatus(game_status_new);
8636 void SetFontStatus(int game_status_new)
8638 static int last_game_status = -1;
8640 if (game_status_new != -1)
8642 // set game status for font use after storing last game status
8643 last_game_status = game_status;
8644 game_status = game_status_new;
8648 // reset game status after font use from last stored game status
8649 game_status = last_game_status;
8653 void ResetFontStatus()
8658 void ChangeViewportPropertiesIfNeeded()
8660 int gfx_game_mode = game_status;
8661 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8663 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
8664 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8665 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8666 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8667 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8668 int new_win_xsize = vp_window->width;
8669 int new_win_ysize = vp_window->height;
8670 int border_size = vp_playfield->border_size;
8671 int new_sx = vp_playfield->x + border_size;
8672 int new_sy = vp_playfield->y + border_size;
8673 int new_sxsize = vp_playfield->width - 2 * border_size;
8674 int new_sysize = vp_playfield->height - 2 * border_size;
8675 int new_real_sx = vp_playfield->x;
8676 int new_real_sy = vp_playfield->y;
8677 int new_full_sxsize = vp_playfield->width;
8678 int new_full_sysize = vp_playfield->height;
8679 int new_dx = vp_door_1->x;
8680 int new_dy = vp_door_1->y;
8681 int new_dxsize = vp_door_1->width;
8682 int new_dysize = vp_door_1->height;
8683 int new_vx = vp_door_2->x;
8684 int new_vy = vp_door_2->y;
8685 int new_vxsize = vp_door_2->width;
8686 int new_vysize = vp_door_2->height;
8687 int new_ex = vp_door_3->x;
8688 int new_ey = vp_door_3->y;
8689 int new_exsize = vp_door_3->width;
8690 int new_eysize = vp_door_3->height;
8691 int new_tilesize_var =
8692 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8694 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8695 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8696 int new_scr_fieldx = new_sxsize / tilesize;
8697 int new_scr_fieldy = new_sysize / tilesize;
8698 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8699 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8700 boolean init_gfx_buffers = FALSE;
8701 boolean init_video_buffer = FALSE;
8702 boolean init_gadgets_and_anims = FALSE;
8703 boolean init_em_graphics = FALSE;
8705 if (new_win_xsize != WIN_XSIZE ||
8706 new_win_ysize != WIN_YSIZE)
8708 WIN_XSIZE = new_win_xsize;
8709 WIN_YSIZE = new_win_ysize;
8711 init_video_buffer = TRUE;
8712 init_gfx_buffers = TRUE;
8713 init_gadgets_and_anims = TRUE;
8715 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8718 if (new_scr_fieldx != SCR_FIELDX ||
8719 new_scr_fieldy != SCR_FIELDY)
8721 /* this always toggles between MAIN and GAME when using small tile size */
8723 SCR_FIELDX = new_scr_fieldx;
8724 SCR_FIELDY = new_scr_fieldy;
8726 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8737 new_sxsize != SXSIZE ||
8738 new_sysize != SYSIZE ||
8739 new_dxsize != DXSIZE ||
8740 new_dysize != DYSIZE ||
8741 new_vxsize != VXSIZE ||
8742 new_vysize != VYSIZE ||
8743 new_exsize != EXSIZE ||
8744 new_eysize != EYSIZE ||
8745 new_real_sx != REAL_SX ||
8746 new_real_sy != REAL_SY ||
8747 new_full_sxsize != FULL_SXSIZE ||
8748 new_full_sysize != FULL_SYSIZE ||
8749 new_tilesize_var != TILESIZE_VAR
8752 // ------------------------------------------------------------------------
8753 // determine next fading area for changed viewport definitions
8754 // ------------------------------------------------------------------------
8756 // start with current playfield area (default fading area)
8759 FADE_SXSIZE = FULL_SXSIZE;
8760 FADE_SYSIZE = FULL_SYSIZE;
8762 // add new playfield area if position or size has changed
8763 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8764 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8766 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8767 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8770 // add current and new door 1 area if position or size has changed
8771 if (new_dx != DX || new_dy != DY ||
8772 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8774 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8775 DX, DY, DXSIZE, DYSIZE);
8776 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8777 new_dx, new_dy, new_dxsize, new_dysize);
8780 // add current and new door 2 area if position or size has changed
8781 if (new_dx != VX || new_dy != VY ||
8782 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8784 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8785 VX, VY, VXSIZE, VYSIZE);
8786 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8787 new_vx, new_vy, new_vxsize, new_vysize);
8790 // ------------------------------------------------------------------------
8791 // handle changed tile size
8792 // ------------------------------------------------------------------------
8794 if (new_tilesize_var != TILESIZE_VAR)
8796 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8798 // changing tile size invalidates scroll values of engine snapshots
8799 FreeEngineSnapshotSingle();
8801 // changing tile size requires update of graphic mapping for EM engine
8802 init_em_graphics = TRUE;
8813 SXSIZE = new_sxsize;
8814 SYSIZE = new_sysize;
8815 DXSIZE = new_dxsize;
8816 DYSIZE = new_dysize;
8817 VXSIZE = new_vxsize;
8818 VYSIZE = new_vysize;
8819 EXSIZE = new_exsize;
8820 EYSIZE = new_eysize;
8821 REAL_SX = new_real_sx;
8822 REAL_SY = new_real_sy;
8823 FULL_SXSIZE = new_full_sxsize;
8824 FULL_SYSIZE = new_full_sysize;
8825 TILESIZE_VAR = new_tilesize_var;
8827 init_gfx_buffers = TRUE;
8828 init_gadgets_and_anims = TRUE;
8830 // printf("::: viewports: init_gfx_buffers\n");
8831 // printf("::: viewports: init_gadgets_and_anims\n");
8834 if (init_gfx_buffers)
8836 // printf("::: init_gfx_buffers\n");
8838 SCR_FIELDX = new_scr_fieldx_buffers;
8839 SCR_FIELDY = new_scr_fieldy_buffers;
8843 SCR_FIELDX = new_scr_fieldx;
8844 SCR_FIELDY = new_scr_fieldy;
8846 SetDrawDeactivationMask(REDRAW_NONE);
8847 SetDrawBackgroundMask(REDRAW_FIELD);
8850 if (init_video_buffer)
8852 // printf("::: init_video_buffer\n");
8854 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8855 InitImageTextures();
8858 if (init_gadgets_and_anims)
8860 // printf("::: init_gadgets_and_anims\n");
8863 InitGlobalAnimations();
8866 if (init_em_graphics)
8868 InitGraphicInfo_EM();