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 static int getFieldbufferOffsetX_RND()
197 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
198 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
199 int dx_var = dx * TILESIZE_VAR / TILESIZE;
202 if (EVEN(SCR_FIELDX))
204 int ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
206 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
207 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
209 fx += (dx_var > 0 ? TILEX_VAR : 0);
216 if (full_lev_fieldx <= SCR_FIELDX)
218 if (EVEN(SCR_FIELDX))
219 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
221 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
227 static int getFieldbufferOffsetY_RND()
229 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
230 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
231 int dy_var = dy * TILESIZE_VAR / TILESIZE;
234 if (EVEN(SCR_FIELDY))
236 int ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
238 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
239 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
241 fy += (dy_var > 0 ? TILEY_VAR : 0);
248 if (full_lev_fieldy <= SCR_FIELDY)
250 if (EVEN(SCR_FIELDY))
251 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
253 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
259 int getFieldbufferOffsetX()
261 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
262 return getFieldbufferOffsetX_EM();
263 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
264 return getFieldbufferOffsetX_SP();
266 return getFieldbufferOffsetX_RND();
269 int getFieldbufferOffsetY()
271 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
272 return getFieldbufferOffsetY_EM();
273 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
274 return getFieldbufferOffsetY_SP();
276 return getFieldbufferOffsetY_RND();
279 static int getLevelFromScreenX_RND(int sx)
281 int fx = getFieldbufferOffsetX_RND();
284 int lx = LEVELX((px + dx) / TILESIZE_VAR);
289 static int getLevelFromScreenY_RND(int sy)
291 int fy = getFieldbufferOffsetY_RND();
294 int ly = LEVELY((py + dy) / TILESIZE_VAR);
299 int getLevelFromScreenX_EM(int sx)
301 int level_xsize = level.native_em_level->lev->width;
302 int full_xsize = level_xsize * TILESIZE_VAR;
304 sx -= (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
306 int fx = getFieldbufferOffsetX_EM();
309 int lx = LEVELX((px + dx) / TILESIZE_VAR) - 1;
311 lx -= (BorderElement != EL_EMPTY ? 1 : 0);
316 int getLevelFromScreenY_EM(int sy)
318 int level_ysize = level.native_em_level->lev->height;
319 int full_ysize = level_ysize * TILESIZE_VAR;
321 sy -= (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
323 int fy = getFieldbufferOffsetY_EM();
326 int ly = LEVELY((py + dy) / TILESIZE_VAR) - 1;
328 ly -= (BorderElement != EL_EMPTY ? 1 : 0);
333 int getLevelFromScreenX_SP(int sx)
335 int menBorder = setup.sp_show_border_elements;
336 int level_xsize = level.native_sp_level->width;
337 int full_xsize = (level_xsize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
339 sx += (full_xsize < SXSIZE ? (SXSIZE - full_xsize) / 2 : 0);
341 int fx = getFieldbufferOffsetX_SP();
344 int lx = LEVELX((px + dx) / TILESIZE_VAR);
349 int getLevelFromScreenY_SP(int sy)
351 int menBorder = setup.sp_show_border_elements;
352 int level_ysize = level.native_sp_level->height;
353 int full_ysize = (level_ysize - (menBorder ? 0 : 1)) * TILESIZE_VAR;
355 sy += (full_ysize < SYSIZE ? (SYSIZE - full_ysize) / 2 : 0);
357 int fy = getFieldbufferOffsetY_SP();
360 int ly = LEVELY((py + dy) / TILESIZE_VAR);
365 int getLevelFromScreenX(int x)
367 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
368 return getLevelFromScreenX_EM(x);
369 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
370 return getLevelFromScreenX_SP(x);
372 return getLevelFromScreenX_RND(x);
375 int getLevelFromScreenY(int y)
377 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
378 return getLevelFromScreenY_EM(y);
379 if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
380 return getLevelFromScreenY_SP(y);
382 return getLevelFromScreenY_RND(y);
385 void DumpTile(int x, int y)
391 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
398 printf_line("-", 79);
399 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
400 printf_line("-", 79);
402 if (!IN_LEV_FIELD(x, y))
404 printf("(not in level field)\n");
410 printf(" Feld: %d\t['%s']\n", Feld[x][y],
411 element_info[Feld[x][y]].token_name);
412 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
413 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
414 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
415 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
416 printf(" MovPos: %d\n", MovPos[x][y]);
417 printf(" MovDir: %d\n", MovDir[x][y]);
418 printf(" MovDelay: %d\n", MovDelay[x][y]);
419 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
420 printf(" CustomValue: %d\n", CustomValue[x][y]);
421 printf(" GfxElement: %d\n", GfxElement[x][y]);
422 printf(" GfxAction: %d\n", GfxAction[x][y]);
423 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
427 void DumpTileFromScreen(int sx, int sy)
429 int lx = getLevelFromScreenX(sx);
430 int ly = getLevelFromScreenY(sy);
435 void SetDrawtoField(int mode)
437 if (mode == DRAW_TO_FIELDBUFFER)
443 BX2 = SCR_FIELDX + 1;
444 BY2 = SCR_FIELDY + 1;
446 drawto_field = fieldbuffer;
448 else /* DRAW_TO_BACKBUFFER */
454 BX2 = SCR_FIELDX - 1;
455 BY2 = SCR_FIELDY - 1;
457 drawto_field = backbuffer;
461 static void RedrawPlayfield_RND()
463 if (game.envelope_active)
466 DrawLevel(REDRAW_ALL);
470 void RedrawPlayfield()
472 if (game_status != GAME_MODE_PLAYING)
475 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
476 RedrawPlayfield_EM(TRUE);
477 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
478 RedrawPlayfield_SP(TRUE);
479 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
480 RedrawPlayfield_RND();
482 BlitScreenToBitmap(backbuffer);
484 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
488 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
491 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
492 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
494 if (x == -1 && y == -1)
497 if (draw_target == DRAW_TO_SCREEN)
498 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
500 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
503 static void DrawMaskedBorderExt_FIELD(int draw_target)
505 if (global.border_status >= GAME_MODE_MAIN &&
506 global.border_status <= GAME_MODE_PLAYING &&
507 border.draw_masked[global.border_status])
508 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
512 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
514 // when drawing to backbuffer, never draw border over open doors
515 if (draw_target == DRAW_TO_BACKBUFFER &&
516 (GetDoorState() & DOOR_OPEN_1))
519 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
520 (global.border_status != GAME_MODE_EDITOR ||
521 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
522 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
525 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
527 // when drawing to backbuffer, never draw border over open doors
528 if (draw_target == DRAW_TO_BACKBUFFER &&
529 (GetDoorState() & DOOR_OPEN_2))
532 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
533 global.border_status != GAME_MODE_EDITOR)
534 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
537 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
539 /* currently not available */
542 static void DrawMaskedBorderExt_ALL(int draw_target)
544 DrawMaskedBorderExt_FIELD(draw_target);
545 DrawMaskedBorderExt_DOOR_1(draw_target);
546 DrawMaskedBorderExt_DOOR_2(draw_target);
547 DrawMaskedBorderExt_DOOR_3(draw_target);
550 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
552 /* never draw masked screen borders on borderless screens */
553 if (global.border_status == GAME_MODE_LOADING ||
554 global.border_status == GAME_MODE_TITLE)
557 if (redraw_mask & REDRAW_ALL)
558 DrawMaskedBorderExt_ALL(draw_target);
561 if (redraw_mask & REDRAW_FIELD)
562 DrawMaskedBorderExt_FIELD(draw_target);
563 if (redraw_mask & REDRAW_DOOR_1)
564 DrawMaskedBorderExt_DOOR_1(draw_target);
565 if (redraw_mask & REDRAW_DOOR_2)
566 DrawMaskedBorderExt_DOOR_2(draw_target);
567 if (redraw_mask & REDRAW_DOOR_3)
568 DrawMaskedBorderExt_DOOR_3(draw_target);
572 void DrawMaskedBorder_FIELD()
574 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
577 void DrawMaskedBorder(int redraw_mask)
579 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
582 void DrawMaskedBorderToTarget(int draw_target)
584 if (draw_target == DRAW_TO_BACKBUFFER ||
585 draw_target == DRAW_TO_SCREEN)
587 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
591 int last_border_status = global.border_status;
593 if (draw_target == DRAW_TO_FADE_SOURCE)
595 global.border_status = gfx.fade_border_source_status;
596 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
598 else if (draw_target == DRAW_TO_FADE_TARGET)
600 global.border_status = gfx.fade_border_target_status;
601 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
604 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
606 global.border_status = last_border_status;
607 gfx.masked_border_bitmap_ptr = backbuffer;
611 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
613 int fx = getFieldbufferOffsetX_RND();
614 int fy = getFieldbufferOffsetY_RND();
616 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
619 void BlitScreenToBitmap(Bitmap *target_bitmap)
621 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
622 BlitScreenToBitmap_EM(target_bitmap);
623 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
624 BlitScreenToBitmap_SP(target_bitmap);
625 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
626 BlitScreenToBitmap_RND(target_bitmap);
628 redraw_mask |= REDRAW_FIELD;
631 void DrawFramesPerSecond()
634 int font_nr = FONT_TEXT_2;
635 int font_width = getFontWidth(font_nr);
637 sprintf(text, "%04.1f fps", global.frames_per_second);
639 DrawTextExt(backbuffer, WIN_XSIZE - font_width * strlen(text), 0, text,
640 font_nr, BLIT_OPAQUE);
644 static void PrintFrameTimeDebugging()
646 static unsigned int last_counter = 0;
647 unsigned int counter = Counter();
648 int diff_1 = counter - last_counter;
649 int diff_2 = diff_1 - GAME_FRAME_DELAY;
651 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
652 char diff_bar[2 * diff_2_max + 5];
656 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
658 for (i = 0; i < diff_2_max; i++)
659 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
660 i >= diff_2_max - diff_2_cut ? '-' : ' ');
662 diff_bar[pos++] = '|';
664 for (i = 0; i < diff_2_max; i++)
665 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
667 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
669 diff_bar[pos++] = '\0';
671 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
674 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
677 last_counter = counter;
681 static int unifiedRedrawMask(int mask)
683 if (mask & REDRAW_ALL)
686 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
692 static boolean equalRedrawMasks(int mask_1, int mask_2)
694 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
699 static int last_redraw_mask = REDRAW_NONE;
701 // force screen redraw in every frame to continue drawing global animations
702 // (but always use the last redraw mask to prevent unwanted side effects)
703 if (redraw_mask == REDRAW_NONE)
704 redraw_mask = last_redraw_mask;
706 last_redraw_mask = redraw_mask;
709 // masked border now drawn immediately when blitting backbuffer to window
711 // draw masked border to all viewports, if defined
712 DrawMaskedBorder(redraw_mask);
715 // draw frames per second (only if debug mode is enabled)
716 if (redraw_mask & REDRAW_FPS)
717 DrawFramesPerSecond();
719 // remove playfield redraw before potentially merging with doors redraw
720 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
721 redraw_mask &= ~REDRAW_FIELD;
723 // redraw complete window if both playfield and (some) doors need redraw
724 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
725 redraw_mask = REDRAW_ALL;
727 /* although redrawing the whole window would be fine for normal gameplay,
728 being able to only redraw the playfield is required for deactivating
729 certain drawing areas (mainly playfield) to work, which is needed for
730 warp-forward to be fast enough (by skipping redraw of most frames) */
732 if (redraw_mask & REDRAW_ALL)
734 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
736 else if (redraw_mask & REDRAW_FIELD)
738 BlitBitmap(backbuffer, window,
739 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
741 else if (redraw_mask & REDRAW_DOORS)
743 // merge door areas to prevent calling screen redraw more than once
749 if (redraw_mask & REDRAW_DOOR_1)
753 x2 = MAX(x2, DX + DXSIZE);
754 y2 = MAX(y2, DY + DYSIZE);
757 if (redraw_mask & REDRAW_DOOR_2)
761 x2 = MAX(x2, VX + VXSIZE);
762 y2 = MAX(y2, VY + VYSIZE);
765 if (redraw_mask & REDRAW_DOOR_3)
769 x2 = MAX(x2, EX + EXSIZE);
770 y2 = MAX(y2, EY + EYSIZE);
773 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
776 redraw_mask = REDRAW_NONE;
779 PrintFrameTimeDebugging();
783 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
785 unsigned int frame_delay_value_old = GetVideoFrameDelay();
787 SetVideoFrameDelay(frame_delay_value);
791 SetVideoFrameDelay(frame_delay_value_old);
794 static int fade_type_skip = FADE_TYPE_NONE;
796 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
798 void (*draw_border_function)(void) = NULL;
799 int x, y, width, height;
800 int fade_delay, post_delay;
802 if (fade_type == FADE_TYPE_FADE_OUT)
804 if (fade_type_skip != FADE_TYPE_NONE)
806 /* skip all fade operations until specified fade operation */
807 if (fade_type & fade_type_skip)
808 fade_type_skip = FADE_TYPE_NONE;
813 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
817 redraw_mask |= fade_mask;
819 if (fade_type == FADE_TYPE_SKIP)
821 fade_type_skip = fade_mode;
826 fade_delay = fading.fade_delay;
827 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
829 if (fade_type_skip != FADE_TYPE_NONE)
831 /* skip all fade operations until specified fade operation */
832 if (fade_type & fade_type_skip)
833 fade_type_skip = FADE_TYPE_NONE;
838 if (global.autoplay_leveldir)
843 if (fade_mask == REDRAW_FIELD)
848 height = FADE_SYSIZE;
850 if (border.draw_masked_when_fading)
851 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
853 DrawMaskedBorder_FIELD(); /* draw once */
855 else /* REDRAW_ALL */
863 if (!setup.fade_screens ||
865 fading.fade_mode == FADE_MODE_NONE)
867 if (fade_mode == FADE_MODE_FADE_OUT)
870 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
872 redraw_mask &= ~fade_mask;
877 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
878 draw_border_function);
880 redraw_mask &= ~fade_mask;
883 static void SetScreenStates_BeforeFadingIn()
885 // temporarily set screen mode for animations to screen after fading in
886 global.anim_status = global.anim_status_next;
888 // store backbuffer with all animations that will be started after fading in
889 if (fade_type_skip != FADE_MODE_SKIP_FADE_IN)
890 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
892 // set screen mode for animations back to fading
893 global.anim_status = GAME_MODE_PSEUDO_FADING;
896 static void SetScreenStates_AfterFadingIn()
898 // store new source screen (to use correct masked border for fading)
899 gfx.fade_border_source_status = global.border_status;
901 global.anim_status = global.anim_status_next;
904 static void SetScreenStates_BeforeFadingOut()
906 // store new target screen (to use correct masked border for fading)
907 gfx.fade_border_target_status = game_status;
909 // set screen mode for animations to fading
910 global.anim_status = GAME_MODE_PSEUDO_FADING;
912 // store backbuffer with all animations that will be stopped for fading out
913 if (fade_type_skip != FADE_MODE_SKIP_FADE_OUT)
914 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
917 static void SetScreenStates_AfterFadingOut()
919 global.border_status = game_status;
922 void FadeIn(int fade_mask)
924 SetScreenStates_BeforeFadingIn();
927 DrawMaskedBorder(REDRAW_ALL);
930 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
931 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
933 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
937 FADE_SXSIZE = FULL_SXSIZE;
938 FADE_SYSIZE = FULL_SYSIZE;
940 SetScreenStates_AfterFadingIn();
942 // force update of global animation status in case of rapid screen changes
943 redraw_mask = REDRAW_ALL;
947 void FadeOut(int fade_mask)
949 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
950 if (!equalRedrawMasks(fade_mask, redraw_mask))
953 SetScreenStates_BeforeFadingOut();
956 DrawMaskedBorder(REDRAW_ALL);
959 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
960 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
962 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
964 SetScreenStates_AfterFadingOut();
967 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
969 static struct TitleFadingInfo fading_leave_stored;
972 fading_leave_stored = fading_leave;
974 fading = fading_leave_stored;
977 void FadeSetEnterMenu()
979 fading = menu.enter_menu;
981 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
984 void FadeSetLeaveMenu()
986 fading = menu.leave_menu;
988 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
991 void FadeSetEnterScreen()
993 fading = menu.enter_screen[game_status];
995 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
998 void FadeSetNextScreen()
1000 fading = menu.next_screen[game_status];
1002 // (do not overwrite fade mode set by FadeSetEnterScreen)
1003 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
1006 void FadeSetLeaveScreen()
1008 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
1011 void FadeSetFromType(int type)
1013 if (type & TYPE_ENTER_SCREEN)
1014 FadeSetEnterScreen();
1015 else if (type & TYPE_ENTER)
1017 else if (type & TYPE_LEAVE)
1021 void FadeSetDisabled()
1023 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
1025 fading = fading_none;
1028 void FadeSkipNextFadeIn()
1030 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
1033 void FadeSkipNextFadeOut()
1035 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
1038 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
1040 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
1042 return (graphic == IMG_UNDEFINED ? NULL :
1043 graphic_info[graphic].bitmap != NULL || redefined ?
1044 graphic_info[graphic].bitmap :
1045 graphic_info[default_graphic].bitmap);
1048 Bitmap *getBackgroundBitmap(int graphic)
1050 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
1053 Bitmap *getGlobalBorderBitmap(int graphic)
1055 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
1058 Bitmap *getGlobalBorderBitmapFromStatus(int status)
1061 (status == GAME_MODE_MAIN ||
1062 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
1063 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
1064 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
1065 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
1068 return getGlobalBorderBitmap(graphic);
1071 void SetWindowBackgroundImageIfDefined(int graphic)
1073 if (graphic_info[graphic].bitmap)
1074 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
1077 void SetMainBackgroundImageIfDefined(int graphic)
1079 if (graphic_info[graphic].bitmap)
1080 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
1083 void SetDoorBackgroundImageIfDefined(int graphic)
1085 if (graphic_info[graphic].bitmap)
1086 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
1089 void SetWindowBackgroundImage(int graphic)
1091 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
1094 void SetMainBackgroundImage(int graphic)
1096 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
1099 void SetDoorBackgroundImage(int graphic)
1101 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
1104 void SetPanelBackground()
1106 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
1108 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
1109 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
1111 SetDoorBackgroundBitmap(bitmap_db_panel);
1114 void DrawBackground(int x, int y, int width, int height)
1116 /* "drawto" might still point to playfield buffer here (hall of fame) */
1117 ClearRectangleOnBackground(backbuffer, x, y, width, height);
1119 if (IN_GFX_FIELD_FULL(x, y))
1120 redraw_mask |= REDRAW_FIELD;
1121 else if (IN_GFX_DOOR_1(x, y))
1122 redraw_mask |= REDRAW_DOOR_1;
1123 else if (IN_GFX_DOOR_2(x, y))
1124 redraw_mask |= REDRAW_DOOR_2;
1125 else if (IN_GFX_DOOR_3(x, y))
1126 redraw_mask |= REDRAW_DOOR_3;
1129 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
1131 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
1133 if (font->bitmap == NULL)
1136 DrawBackground(x, y, width, height);
1139 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
1141 struct GraphicInfo *g = &graphic_info[graphic];
1143 if (g->bitmap == NULL)
1146 DrawBackground(x, y, width, height);
1149 static int game_status_last = -1;
1150 static Bitmap *global_border_bitmap_last = NULL;
1151 static Bitmap *global_border_bitmap = NULL;
1152 static int real_sx_last = -1, real_sy_last = -1;
1153 static int full_sxsize_last = -1, full_sysize_last = -1;
1154 static int dx_last = -1, dy_last = -1;
1155 static int dxsize_last = -1, dysize_last = -1;
1156 static int vx_last = -1, vy_last = -1;
1157 static int vxsize_last = -1, vysize_last = -1;
1159 boolean CheckIfGlobalBorderHasChanged()
1161 // if game status has not changed, global border has not changed either
1162 if (game_status == game_status_last)
1165 // determine and store new global border bitmap for current game status
1166 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1168 return (global_border_bitmap_last != global_border_bitmap);
1171 boolean CheckIfGlobalBorderRedrawIsNeeded()
1173 // if game status has not changed, nothing has to be redrawn
1174 if (game_status == game_status_last)
1177 // redraw if last screen was title screen
1178 if (game_status_last == GAME_MODE_TITLE)
1181 // redraw if global screen border has changed
1182 if (CheckIfGlobalBorderHasChanged())
1185 // redraw if position or size of playfield area has changed
1186 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1187 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1190 // redraw if position or size of door area has changed
1191 if (dx_last != DX || dy_last != DY ||
1192 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1195 // redraw if position or size of tape area has changed
1196 if (vx_last != VX || vy_last != VY ||
1197 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1203 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1206 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1208 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1211 void RedrawGlobalBorder()
1213 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1215 RedrawGlobalBorderFromBitmap(bitmap);
1217 redraw_mask = REDRAW_ALL;
1220 static void RedrawGlobalBorderIfNeeded()
1222 if (game_status == game_status_last)
1225 // copy current draw buffer to later copy back areas that have not changed
1226 if (game_status_last != GAME_MODE_TITLE)
1227 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1229 if (CheckIfGlobalBorderRedrawIsNeeded())
1231 // redraw global screen border (or clear, if defined to be empty)
1232 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1234 // copy previous playfield and door areas, if they are defined on both
1235 // previous and current screen and if they still have the same size
1237 if (real_sx_last != -1 && real_sy_last != -1 &&
1238 REAL_SX != -1 && REAL_SY != -1 &&
1239 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1240 BlitBitmap(bitmap_db_store_1, backbuffer,
1241 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1244 if (dx_last != -1 && dy_last != -1 &&
1245 DX != -1 && DY != -1 &&
1246 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1247 BlitBitmap(bitmap_db_store_1, backbuffer,
1248 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1250 if (vx_last != -1 && vy_last != -1 &&
1251 VX != -1 && VY != -1 &&
1252 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1253 BlitBitmap(bitmap_db_store_1, backbuffer,
1254 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1256 redraw_mask = REDRAW_ALL;
1259 game_status_last = game_status;
1261 global_border_bitmap_last = global_border_bitmap;
1263 real_sx_last = REAL_SX;
1264 real_sy_last = REAL_SY;
1265 full_sxsize_last = FULL_SXSIZE;
1266 full_sysize_last = FULL_SYSIZE;
1269 dxsize_last = DXSIZE;
1270 dysize_last = DYSIZE;
1273 vxsize_last = VXSIZE;
1274 vysize_last = VYSIZE;
1279 RedrawGlobalBorderIfNeeded();
1281 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1282 /* (when entering hall of fame after playing) */
1283 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1285 /* !!! maybe this should be done before clearing the background !!! */
1286 if (game_status == GAME_MODE_PLAYING)
1288 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1289 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1293 SetDrawtoField(DRAW_TO_BACKBUFFER);
1297 void MarkTileDirty(int x, int y)
1299 redraw_mask |= REDRAW_FIELD;
1302 void SetBorderElement()
1306 BorderElement = EL_EMPTY;
1308 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1310 for (x = 0; x < lev_fieldx; x++)
1312 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1313 BorderElement = EL_STEELWALL;
1315 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1321 void FloodFillLevel(int from_x, int from_y, int fill_element,
1322 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1323 int max_fieldx, int max_fieldy)
1327 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1328 static int safety = 0;
1330 /* check if starting field still has the desired content */
1331 if (field[from_x][from_y] == fill_element)
1336 if (safety > max_fieldx * max_fieldy)
1337 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1339 old_element = field[from_x][from_y];
1340 field[from_x][from_y] = fill_element;
1342 for (i = 0; i < 4; i++)
1344 x = from_x + check[i][0];
1345 y = from_y + check[i][1];
1347 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1348 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1354 void SetRandomAnimationValue(int x, int y)
1356 gfx.anim_random_frame = GfxRandom[x][y];
1359 int getGraphicAnimationFrame(int graphic, int sync_frame)
1361 /* animation synchronized with global frame counter, not move position */
1362 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1363 sync_frame = FrameCounter;
1365 return getAnimationFrame(graphic_info[graphic].anim_frames,
1366 graphic_info[graphic].anim_delay,
1367 graphic_info[graphic].anim_mode,
1368 graphic_info[graphic].anim_start_frame,
1372 void getGraphicSourceBitmap(int graphic, int tilesize, Bitmap **bitmap)
1374 struct GraphicInfo *g = &graphic_info[graphic];
1375 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1377 if (tilesize == gfx.standard_tile_size)
1378 *bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1379 else if (tilesize == game.tile_size)
1380 *bitmap = g->bitmaps[IMG_BITMAP_GAME];
1382 *bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1385 void getGraphicSourceXY(int graphic, int frame, int *x, int *y,
1386 boolean get_backside)
1388 struct GraphicInfo *g = &graphic_info[graphic];
1389 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1390 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1392 if (g->offset_y == 0) /* frames are ordered horizontally */
1394 int max_width = g->anim_frames_per_line * g->width;
1395 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1397 *x = pos % max_width;
1398 *y = src_y % g->height + pos / max_width * g->height;
1400 else if (g->offset_x == 0) /* frames are ordered vertically */
1402 int max_height = g->anim_frames_per_line * g->height;
1403 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1405 *x = src_x % g->width + pos / max_height * g->width;
1406 *y = pos % max_height;
1408 else /* frames are ordered diagonally */
1410 *x = src_x + frame * g->offset_x;
1411 *y = src_y + frame * g->offset_y;
1415 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1416 Bitmap **bitmap, int *x, int *y,
1417 boolean get_backside)
1419 struct GraphicInfo *g = &graphic_info[graphic];
1421 // if no in-game graphics defined, always use standard graphic size
1422 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1423 tilesize = TILESIZE;
1425 getGraphicSourceBitmap(graphic, tilesize, bitmap);
1426 getGraphicSourceXY(graphic, frame, x, y, get_backside);
1428 *x = *x * tilesize / g->tile_size;
1429 *y = *y * tilesize / g->tile_size;
1432 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1433 int *x, int *y, boolean get_backside)
1435 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1439 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1440 Bitmap **bitmap, int *x, int *y)
1442 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1445 void getFixedGraphicSource(int graphic, int frame,
1446 Bitmap **bitmap, int *x, int *y)
1448 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1451 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1453 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1456 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1457 int *x, int *y, boolean get_backside)
1459 getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1463 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1465 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1468 void DrawGraphic(int x, int y, int graphic, int frame)
1471 if (!IN_SCR_FIELD(x, y))
1473 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1474 printf("DrawGraphic(): This should never happen!\n");
1479 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1482 MarkTileDirty(x, y);
1485 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1488 if (!IN_SCR_FIELD(x, y))
1490 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1491 printf("DrawGraphic(): This should never happen!\n");
1496 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1498 MarkTileDirty(x, y);
1501 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1507 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1509 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1512 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1518 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1519 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1522 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1525 if (!IN_SCR_FIELD(x, y))
1527 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1528 printf("DrawGraphicThruMask(): This should never happen!\n");
1533 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1536 MarkTileDirty(x, y);
1539 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1542 if (!IN_SCR_FIELD(x, y))
1544 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1545 printf("DrawGraphicThruMask(): This should never happen!\n");
1550 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1552 MarkTileDirty(x, y);
1555 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1561 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1563 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1567 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1568 int graphic, int frame)
1573 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1575 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1579 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1581 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1583 MarkTileDirty(x / tilesize, y / tilesize);
1586 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1592 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1593 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1596 void DrawMiniGraphic(int x, int y, int graphic)
1598 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1599 MarkTileDirty(x / 2, y / 2);
1602 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1607 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1608 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1611 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1612 int graphic, int frame,
1613 int cut_mode, int mask_mode)
1618 int width = TILEX, height = TILEY;
1621 if (dx || dy) /* shifted graphic */
1623 if (x < BX1) /* object enters playfield from the left */
1630 else if (x > BX2) /* object enters playfield from the right */
1636 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1642 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1644 else if (dx) /* general horizontal movement */
1645 MarkTileDirty(x + SIGN(dx), y);
1647 if (y < BY1) /* object enters playfield from the top */
1649 if (cut_mode == CUT_BELOW) /* object completely above top border */
1657 else if (y > BY2) /* object enters playfield from the bottom */
1663 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1669 else if (dy > 0 && cut_mode == CUT_ABOVE)
1671 if (y == BY2) /* object completely above bottom border */
1677 MarkTileDirty(x, y + 1);
1678 } /* object leaves playfield to the bottom */
1679 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1681 else if (dy) /* general vertical movement */
1682 MarkTileDirty(x, y + SIGN(dy));
1686 if (!IN_SCR_FIELD(x, y))
1688 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1689 printf("DrawGraphicShifted(): This should never happen!\n");
1694 width = width * TILESIZE_VAR / TILESIZE;
1695 height = height * TILESIZE_VAR / TILESIZE;
1696 cx = cx * TILESIZE_VAR / TILESIZE;
1697 cy = cy * TILESIZE_VAR / TILESIZE;
1698 dx = dx * TILESIZE_VAR / TILESIZE;
1699 dy = dy * TILESIZE_VAR / TILESIZE;
1701 if (width > 0 && height > 0)
1703 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1708 dst_x = FX + x * TILEX_VAR + dx;
1709 dst_y = FY + y * TILEY_VAR + dy;
1711 if (mask_mode == USE_MASKING)
1712 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1715 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1718 MarkTileDirty(x, y);
1722 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1723 int graphic, int frame,
1724 int cut_mode, int mask_mode)
1729 int width = TILEX_VAR, height = TILEY_VAR;
1732 int x2 = x + SIGN(dx);
1733 int y2 = y + SIGN(dy);
1735 /* movement with two-tile animations must be sync'ed with movement position,
1736 not with current GfxFrame (which can be higher when using slow movement) */
1737 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1738 int anim_frames = graphic_info[graphic].anim_frames;
1740 /* (we also need anim_delay here for movement animations with less frames) */
1741 int anim_delay = graphic_info[graphic].anim_delay;
1742 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1744 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1745 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1747 /* re-calculate animation frame for two-tile movement animation */
1748 frame = getGraphicAnimationFrame(graphic, sync_frame);
1750 /* check if movement start graphic inside screen area and should be drawn */
1751 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1753 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1755 dst_x = FX + x1 * TILEX_VAR;
1756 dst_y = FY + y1 * TILEY_VAR;
1758 if (mask_mode == USE_MASKING)
1759 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1762 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1765 MarkTileDirty(x1, y1);
1768 /* check if movement end graphic inside screen area and should be drawn */
1769 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1771 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1773 dst_x = FX + x2 * TILEX_VAR;
1774 dst_y = FY + y2 * TILEY_VAR;
1776 if (mask_mode == USE_MASKING)
1777 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1780 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1783 MarkTileDirty(x2, y2);
1787 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1788 int graphic, int frame,
1789 int cut_mode, int mask_mode)
1793 DrawGraphic(x, y, graphic, frame);
1798 if (graphic_info[graphic].double_movement) /* EM style movement images */
1799 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1801 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1804 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1805 int frame, int cut_mode)
1807 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1810 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1811 int cut_mode, int mask_mode)
1813 int lx = LEVELX(x), ly = LEVELY(y);
1817 if (IN_LEV_FIELD(lx, ly))
1819 SetRandomAnimationValue(lx, ly);
1821 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1822 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1824 /* do not use double (EM style) movement graphic when not moving */
1825 if (graphic_info[graphic].double_movement && !dx && !dy)
1827 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1828 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1831 else /* border element */
1833 graphic = el2img(element);
1834 frame = getGraphicAnimationFrame(graphic, -1);
1837 if (element == EL_EXPANDABLE_WALL)
1839 boolean left_stopped = FALSE, right_stopped = FALSE;
1841 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1842 left_stopped = TRUE;
1843 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1844 right_stopped = TRUE;
1846 if (left_stopped && right_stopped)
1848 else if (left_stopped)
1850 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1851 frame = graphic_info[graphic].anim_frames - 1;
1853 else if (right_stopped)
1855 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1856 frame = graphic_info[graphic].anim_frames - 1;
1861 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1862 else if (mask_mode == USE_MASKING)
1863 DrawGraphicThruMask(x, y, graphic, frame);
1865 DrawGraphic(x, y, graphic, frame);
1868 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1869 int cut_mode, int mask_mode)
1871 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1872 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1873 cut_mode, mask_mode);
1876 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1879 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1882 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1885 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1888 void DrawLevelElementThruMask(int x, int y, int element)
1890 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1893 void DrawLevelFieldThruMask(int x, int y)
1895 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1898 /* !!! implementation of quicksand is totally broken !!! */
1899 #define IS_CRUMBLED_TILE(x, y, e) \
1900 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1901 !IS_MOVING(x, y) || \
1902 (e) == EL_QUICKSAND_EMPTYING || \
1903 (e) == EL_QUICKSAND_FAST_EMPTYING))
1905 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1910 int width, height, cx, cy;
1911 int sx = SCREENX(x), sy = SCREENY(y);
1912 int crumbled_border_size = graphic_info[graphic].border_size;
1913 int crumbled_tile_size = graphic_info[graphic].tile_size;
1914 int crumbled_border_size_var =
1915 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
1918 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1920 for (i = 1; i < 4; i++)
1922 int dxx = (i & 1 ? dx : 0);
1923 int dyy = (i & 2 ? dy : 0);
1926 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1929 /* check if neighbour field is of same crumble type */
1930 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1931 graphic_info[graphic].class ==
1932 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1934 /* return if check prevents inner corner */
1935 if (same == (dxx == dx && dyy == dy))
1939 /* if we reach this point, we have an inner corner */
1941 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1943 width = crumbled_border_size_var;
1944 height = crumbled_border_size_var;
1945 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1946 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1948 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1949 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1952 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1957 int width, height, bx, by, cx, cy;
1958 int sx = SCREENX(x), sy = SCREENY(y);
1959 int crumbled_border_size = graphic_info[graphic].border_size;
1960 int crumbled_tile_size = graphic_info[graphic].tile_size;
1961 int crumbled_border_size_var =
1962 crumbled_border_size * TILESIZE_VAR / crumbled_tile_size;
1963 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1966 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1968 /* draw simple, sloppy, non-corner-accurate crumbled border */
1970 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1971 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1972 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1973 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1975 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1976 FX + sx * TILEX_VAR + cx,
1977 FY + sy * TILEY_VAR + cy);
1979 /* (remaining middle border part must be at least as big as corner part) */
1980 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1981 crumbled_border_size_var >= TILESIZE_VAR / 3)
1984 /* correct corners of crumbled border, if needed */
1986 for (i = -1; i <= 1; i += 2)
1988 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1989 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1990 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1993 /* check if neighbour field is of same crumble type */
1994 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1995 graphic_info[graphic].class ==
1996 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1998 /* no crumbled corner, but continued crumbled border */
2000 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
2001 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
2002 int b1 = (i == 1 ? crumbled_border_size_var :
2003 TILESIZE_VAR - 2 * crumbled_border_size_var);
2005 width = crumbled_border_size_var;
2006 height = crumbled_border_size_var;
2008 if (dir == 1 || dir == 2)
2023 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
2025 FX + sx * TILEX_VAR + cx,
2026 FY + sy * TILEY_VAR + cy);
2031 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
2033 int sx = SCREENX(x), sy = SCREENY(y);
2036 static int xy[4][2] =
2044 if (!IN_LEV_FIELD(x, y))
2047 element = TILE_GFX_ELEMENT(x, y);
2049 if (IS_CRUMBLED_TILE(x, y, element)) /* crumble field itself */
2051 if (!IN_SCR_FIELD(sx, sy))
2054 /* crumble field borders towards direct neighbour fields */
2055 for (i = 0; i < 4; i++)
2057 int xx = x + xy[i][0];
2058 int yy = y + xy[i][1];
2060 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
2063 /* check if neighbour field is of same crumble type */
2064 if (IS_CRUMBLED_TILE(xx, yy, element) &&
2065 graphic_info[graphic].class ==
2066 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
2069 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
2072 /* crumble inner field corners towards corner neighbour fields */
2073 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2074 graphic_info[graphic].anim_frames == 2)
2076 for (i = 0; i < 4; i++)
2078 int dx = (i & 1 ? +1 : -1);
2079 int dy = (i & 2 ? +1 : -1);
2081 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
2085 MarkTileDirty(sx, sy);
2087 else /* center field is not crumbled -- crumble neighbour fields */
2089 /* crumble field borders of direct neighbour fields */
2090 for (i = 0; i < 4; i++)
2092 int xx = x + xy[i][0];
2093 int yy = y + xy[i][1];
2094 int sxx = sx + xy[i][0];
2095 int syy = sy + xy[i][1];
2097 if (!IN_LEV_FIELD(xx, yy) ||
2098 !IN_SCR_FIELD(sxx, syy))
2101 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2104 element = TILE_GFX_ELEMENT(xx, yy);
2106 if (!IS_CRUMBLED_TILE(xx, yy, element))
2109 graphic = el_act2crm(element, ACTION_DEFAULT);
2111 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
2113 MarkTileDirty(sxx, syy);
2116 /* crumble inner field corners of corner neighbour fields */
2117 for (i = 0; i < 4; i++)
2119 int dx = (i & 1 ? +1 : -1);
2120 int dy = (i & 2 ? +1 : -1);
2126 if (!IN_LEV_FIELD(xx, yy) ||
2127 !IN_SCR_FIELD(sxx, syy))
2130 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
2133 element = TILE_GFX_ELEMENT(xx, yy);
2135 if (!IS_CRUMBLED_TILE(xx, yy, element))
2138 graphic = el_act2crm(element, ACTION_DEFAULT);
2140 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2141 graphic_info[graphic].anim_frames == 2)
2142 DrawLevelFieldCrumbledInnerCorners(xx, yy, -dx, -dy, graphic);
2144 MarkTileDirty(sxx, syy);
2149 void DrawLevelFieldCrumbled(int x, int y)
2153 if (!IN_LEV_FIELD(x, y))
2156 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
2157 GfxElement[x][y] != EL_UNDEFINED &&
2158 GFX_CRUMBLED(GfxElement[x][y]))
2160 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
2165 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
2167 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
2170 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2173 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2174 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2175 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2176 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2177 int sx = SCREENX(x), sy = SCREENY(y);
2179 DrawGraphic(sx, sy, graphic1, frame1);
2180 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2183 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2185 int sx = SCREENX(x), sy = SCREENY(y);
2186 static int xy[4][2] =
2195 /* crumble direct neighbour fields (required for field borders) */
2196 for (i = 0; i < 4; i++)
2198 int xx = x + xy[i][0];
2199 int yy = y + xy[i][1];
2200 int sxx = sx + xy[i][0];
2201 int syy = sy + xy[i][1];
2203 if (!IN_LEV_FIELD(xx, yy) ||
2204 !IN_SCR_FIELD(sxx, syy) ||
2205 !GFX_CRUMBLED(Feld[xx][yy]) ||
2209 DrawLevelField(xx, yy);
2212 /* crumble corner neighbour fields (required for inner field corners) */
2213 for (i = 0; i < 4; i++)
2215 int dx = (i & 1 ? +1 : -1);
2216 int dy = (i & 2 ? +1 : -1);
2222 if (!IN_LEV_FIELD(xx, yy) ||
2223 !IN_SCR_FIELD(sxx, syy) ||
2224 !GFX_CRUMBLED(Feld[xx][yy]) ||
2228 int element = TILE_GFX_ELEMENT(xx, yy);
2229 int graphic = el_act2crm(element, ACTION_DEFAULT);
2231 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
2232 graphic_info[graphic].anim_frames == 2)
2233 DrawLevelField(xx, yy);
2237 static int getBorderElement(int x, int y)
2241 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2242 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2243 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2244 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2245 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2246 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2247 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2249 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2250 int steel_position = (x == -1 && y == -1 ? 0 :
2251 x == lev_fieldx && y == -1 ? 1 :
2252 x == -1 && y == lev_fieldy ? 2 :
2253 x == lev_fieldx && y == lev_fieldy ? 3 :
2254 x == -1 || x == lev_fieldx ? 4 :
2255 y == -1 || y == lev_fieldy ? 5 : 6);
2257 return border[steel_position][steel_type];
2260 void DrawScreenElement(int x, int y, int element)
2262 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2263 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2266 void DrawLevelElement(int x, int y, int element)
2268 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2269 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2272 void DrawScreenField(int x, int y)
2274 int lx = LEVELX(x), ly = LEVELY(y);
2275 int element, content;
2277 if (!IN_LEV_FIELD(lx, ly))
2279 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2282 element = getBorderElement(lx, ly);
2284 DrawScreenElement(x, y, element);
2289 element = Feld[lx][ly];
2290 content = Store[lx][ly];
2292 if (IS_MOVING(lx, ly))
2294 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2295 boolean cut_mode = NO_CUTTING;
2297 if (element == EL_QUICKSAND_EMPTYING ||
2298 element == EL_QUICKSAND_FAST_EMPTYING ||
2299 element == EL_MAGIC_WALL_EMPTYING ||
2300 element == EL_BD_MAGIC_WALL_EMPTYING ||
2301 element == EL_DC_MAGIC_WALL_EMPTYING ||
2302 element == EL_AMOEBA_DROPPING)
2303 cut_mode = CUT_ABOVE;
2304 else if (element == EL_QUICKSAND_FILLING ||
2305 element == EL_QUICKSAND_FAST_FILLING ||
2306 element == EL_MAGIC_WALL_FILLING ||
2307 element == EL_BD_MAGIC_WALL_FILLING ||
2308 element == EL_DC_MAGIC_WALL_FILLING)
2309 cut_mode = CUT_BELOW;
2311 if (cut_mode == CUT_ABOVE)
2312 DrawScreenElement(x, y, element);
2314 DrawScreenElement(x, y, EL_EMPTY);
2317 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2318 else if (cut_mode == NO_CUTTING)
2319 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2322 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2324 if (cut_mode == CUT_BELOW &&
2325 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2326 DrawLevelElement(lx, ly + 1, element);
2329 if (content == EL_ACID)
2331 int dir = MovDir[lx][ly];
2332 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2333 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2335 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2338 else if (IS_BLOCKED(lx, ly))
2343 boolean cut_mode = NO_CUTTING;
2344 int element_old, content_old;
2346 Blocked2Moving(lx, ly, &oldx, &oldy);
2349 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2350 MovDir[oldx][oldy] == MV_RIGHT);
2352 element_old = Feld[oldx][oldy];
2353 content_old = Store[oldx][oldy];
2355 if (element_old == EL_QUICKSAND_EMPTYING ||
2356 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2357 element_old == EL_MAGIC_WALL_EMPTYING ||
2358 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2359 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2360 element_old == EL_AMOEBA_DROPPING)
2361 cut_mode = CUT_ABOVE;
2363 DrawScreenElement(x, y, EL_EMPTY);
2366 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2368 else if (cut_mode == NO_CUTTING)
2369 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2372 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2375 else if (IS_DRAWABLE(element))
2376 DrawScreenElement(x, y, element);
2378 DrawScreenElement(x, y, EL_EMPTY);
2381 void DrawLevelField(int x, int y)
2383 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2384 DrawScreenField(SCREENX(x), SCREENY(y));
2385 else if (IS_MOVING(x, y))
2389 Moving2Blocked(x, y, &newx, &newy);
2390 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2391 DrawScreenField(SCREENX(newx), SCREENY(newy));
2393 else if (IS_BLOCKED(x, y))
2397 Blocked2Moving(x, y, &oldx, &oldy);
2398 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2399 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2403 void DrawSizedElement(int x, int y, int element, int tilesize)
2407 graphic = el2edimg(element);
2408 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2411 void DrawMiniElement(int x, int y, int element)
2415 graphic = el2edimg(element);
2416 DrawMiniGraphic(x, y, graphic);
2419 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2422 int x = sx + scroll_x, y = sy + scroll_y;
2424 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2425 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2426 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2427 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2429 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2432 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2434 int x = sx + scroll_x, y = sy + scroll_y;
2436 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2437 DrawMiniElement(sx, sy, EL_EMPTY);
2438 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2439 DrawMiniElement(sx, sy, Feld[x][y]);
2441 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2444 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2445 int x, int y, int xsize, int ysize,
2446 int tile_width, int tile_height)
2450 int dst_x = startx + x * tile_width;
2451 int dst_y = starty + y * tile_height;
2452 int width = graphic_info[graphic].width;
2453 int height = graphic_info[graphic].height;
2454 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2455 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2456 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2457 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2458 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2459 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2460 boolean draw_masked = graphic_info[graphic].draw_masked;
2462 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2464 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2466 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2470 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2471 inner_sx + (x - 1) * tile_width % inner_width);
2472 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2473 inner_sy + (y - 1) * tile_height % inner_height);
2476 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2479 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2483 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2484 int x, int y, int xsize, int ysize, int font_nr)
2486 int font_width = getFontWidth(font_nr);
2487 int font_height = getFontHeight(font_nr);
2489 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2490 font_width, font_height);
2493 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2495 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2496 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2497 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2498 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2499 boolean no_delay = (tape.warp_forward);
2500 unsigned int anim_delay = 0;
2501 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2502 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2503 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2504 int font_width = getFontWidth(font_nr);
2505 int font_height = getFontHeight(font_nr);
2506 int max_xsize = level.envelope[envelope_nr].xsize;
2507 int max_ysize = level.envelope[envelope_nr].ysize;
2508 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2509 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2510 int xend = max_xsize;
2511 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2512 int xstep = (xstart < xend ? 1 : 0);
2513 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2515 int end = MAX(xend - xstart, yend - ystart);
2518 for (i = start; i <= end; i++)
2520 int last_frame = end; // last frame of this "for" loop
2521 int x = xstart + i * xstep;
2522 int y = ystart + i * ystep;
2523 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2524 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2525 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2526 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2529 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2531 BlitScreenToBitmap(backbuffer);
2533 SetDrawtoField(DRAW_TO_BACKBUFFER);
2535 for (yy = 0; yy < ysize; yy++)
2536 for (xx = 0; xx < xsize; xx++)
2537 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2539 DrawTextBuffer(sx + font_width, sy + font_height,
2540 level.envelope[envelope_nr].text, font_nr, max_xsize,
2541 xsize - 2, ysize - 2, 0, mask_mode,
2542 level.envelope[envelope_nr].autowrap,
2543 level.envelope[envelope_nr].centered, FALSE);
2545 redraw_mask |= REDRAW_FIELD;
2548 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2552 void ShowEnvelope(int envelope_nr)
2554 int element = EL_ENVELOPE_1 + envelope_nr;
2555 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2556 int sound_opening = element_info[element].sound[ACTION_OPENING];
2557 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2558 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2559 boolean no_delay = (tape.warp_forward);
2560 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2561 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2562 int anim_mode = graphic_info[graphic].anim_mode;
2563 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2564 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2566 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2568 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2570 if (anim_mode == ANIM_DEFAULT)
2571 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2573 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2576 Delay(wait_delay_value);
2578 WaitForEventToContinue();
2580 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2582 if (anim_mode != ANIM_NONE)
2583 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2585 if (anim_mode == ANIM_DEFAULT)
2586 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2588 game.envelope_active = FALSE;
2590 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2592 redraw_mask |= REDRAW_FIELD;
2596 static void setRequestBasePosition(int *x, int *y)
2598 int sx_base, sy_base;
2600 if (request.x != -1)
2601 sx_base = request.x;
2602 else if (request.align == ALIGN_LEFT)
2604 else if (request.align == ALIGN_RIGHT)
2605 sx_base = SX + SXSIZE;
2607 sx_base = SX + SXSIZE / 2;
2609 if (request.y != -1)
2610 sy_base = request.y;
2611 else if (request.valign == VALIGN_TOP)
2613 else if (request.valign == VALIGN_BOTTOM)
2614 sy_base = SY + SYSIZE;
2616 sy_base = SY + SYSIZE / 2;
2622 static void setRequestPositionExt(int *x, int *y, int width, int height,
2623 boolean add_border_size)
2625 int border_size = request.border_size;
2626 int sx_base, sy_base;
2629 setRequestBasePosition(&sx_base, &sy_base);
2631 if (request.align == ALIGN_LEFT)
2633 else if (request.align == ALIGN_RIGHT)
2634 sx = sx_base - width;
2636 sx = sx_base - width / 2;
2638 if (request.valign == VALIGN_TOP)
2640 else if (request.valign == VALIGN_BOTTOM)
2641 sy = sy_base - height;
2643 sy = sy_base - height / 2;
2645 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2646 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2648 if (add_border_size)
2658 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2660 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2663 void DrawEnvelopeRequest(char *text)
2665 char *text_final = text;
2666 char *text_door_style = NULL;
2667 int graphic = IMG_BACKGROUND_REQUEST;
2668 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2669 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2670 int font_nr = FONT_REQUEST;
2671 int font_width = getFontWidth(font_nr);
2672 int font_height = getFontHeight(font_nr);
2673 int border_size = request.border_size;
2674 int line_spacing = request.line_spacing;
2675 int line_height = font_height + line_spacing;
2676 int max_text_width = request.width - 2 * border_size;
2677 int max_text_height = request.height - 2 * border_size;
2678 int line_length = max_text_width / font_width;
2679 int max_lines = max_text_height / line_height;
2680 int text_width = line_length * font_width;
2681 int width = request.width;
2682 int height = request.height;
2683 int tile_size = MAX(request.step_offset, 1);
2684 int x_steps = width / tile_size;
2685 int y_steps = height / tile_size;
2686 int sx_offset = border_size;
2687 int sy_offset = border_size;
2691 if (request.centered)
2692 sx_offset = (request.width - text_width) / 2;
2694 if (request.wrap_single_words && !request.autowrap)
2696 char *src_text_ptr, *dst_text_ptr;
2698 text_door_style = checked_malloc(2 * strlen(text) + 1);
2700 src_text_ptr = text;
2701 dst_text_ptr = text_door_style;
2703 while (*src_text_ptr)
2705 if (*src_text_ptr == ' ' ||
2706 *src_text_ptr == '?' ||
2707 *src_text_ptr == '!')
2708 *dst_text_ptr++ = '\n';
2710 if (*src_text_ptr != ' ')
2711 *dst_text_ptr++ = *src_text_ptr;
2716 *dst_text_ptr = '\0';
2718 text_final = text_door_style;
2721 setRequestPosition(&sx, &sy, FALSE);
2723 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2725 for (y = 0; y < y_steps; y++)
2726 for (x = 0; x < x_steps; x++)
2727 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2728 x, y, x_steps, y_steps,
2729 tile_size, tile_size);
2731 /* force DOOR font inside door area */
2732 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2734 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2735 line_length, -1, max_lines, line_spacing, mask_mode,
2736 request.autowrap, request.centered, FALSE);
2740 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2741 RedrawGadget(tool_gadget[i]);
2743 // store readily prepared envelope request for later use when animating
2744 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2746 if (text_door_style)
2747 free(text_door_style);
2750 void AnimateEnvelopeRequest(int anim_mode, int action)
2752 int graphic = IMG_BACKGROUND_REQUEST;
2753 boolean draw_masked = graphic_info[graphic].draw_masked;
2754 int delay_value_normal = request.step_delay;
2755 int delay_value_fast = delay_value_normal / 2;
2756 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2757 boolean no_delay = (tape.warp_forward);
2758 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2759 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2760 unsigned int anim_delay = 0;
2762 int tile_size = MAX(request.step_offset, 1);
2763 int max_xsize = request.width / tile_size;
2764 int max_ysize = request.height / tile_size;
2765 int max_xsize_inner = max_xsize - 2;
2766 int max_ysize_inner = max_ysize - 2;
2768 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2769 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2770 int xend = max_xsize_inner;
2771 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2772 int xstep = (xstart < xend ? 1 : 0);
2773 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2775 int end = MAX(xend - xstart, yend - ystart);
2778 if (setup.quick_doors)
2785 for (i = start; i <= end; i++)
2787 int last_frame = end; // last frame of this "for" loop
2788 int x = xstart + i * xstep;
2789 int y = ystart + i * ystep;
2790 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2791 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2792 int xsize_size_left = (xsize - 1) * tile_size;
2793 int ysize_size_top = (ysize - 1) * tile_size;
2794 int max_xsize_pos = (max_xsize - 1) * tile_size;
2795 int max_ysize_pos = (max_ysize - 1) * tile_size;
2796 int width = xsize * tile_size;
2797 int height = ysize * tile_size;
2802 setRequestPosition(&src_x, &src_y, FALSE);
2803 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2805 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2807 for (yy = 0; yy < 2; yy++)
2809 for (xx = 0; xx < 2; xx++)
2811 int src_xx = src_x + xx * max_xsize_pos;
2812 int src_yy = src_y + yy * max_ysize_pos;
2813 int dst_xx = dst_x + xx * xsize_size_left;
2814 int dst_yy = dst_y + yy * ysize_size_top;
2815 int xx_size = (xx ? tile_size : xsize_size_left);
2816 int yy_size = (yy ? tile_size : ysize_size_top);
2819 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
2820 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2822 BlitBitmap(bitmap_db_store_2, backbuffer,
2823 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2827 redraw_mask |= REDRAW_FIELD;
2831 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2835 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2837 int graphic = IMG_BACKGROUND_REQUEST;
2838 int sound_opening = SND_REQUEST_OPENING;
2839 int sound_closing = SND_REQUEST_CLOSING;
2840 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2841 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2842 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2843 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2844 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2846 if (game_status == GAME_MODE_PLAYING)
2847 BlitScreenToBitmap(backbuffer);
2849 SetDrawtoField(DRAW_TO_BACKBUFFER);
2851 // SetDrawBackgroundMask(REDRAW_NONE);
2853 if (action == ACTION_OPENING)
2855 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2857 if (req_state & REQ_ASK)
2859 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2860 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2862 else if (req_state & REQ_CONFIRM)
2864 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2866 else if (req_state & REQ_PLAYER)
2868 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2869 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2870 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2871 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2874 DrawEnvelopeRequest(text);
2877 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2879 if (action == ACTION_OPENING)
2881 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2883 if (anim_mode == ANIM_DEFAULT)
2884 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2886 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2890 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2892 if (anim_mode != ANIM_NONE)
2893 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2895 if (anim_mode == ANIM_DEFAULT)
2896 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2899 game.envelope_active = FALSE;
2901 if (action == ACTION_CLOSING)
2902 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2904 // SetDrawBackgroundMask(last_draw_background_mask);
2906 redraw_mask |= REDRAW_FIELD;
2910 if (action == ACTION_CLOSING &&
2911 game_status == GAME_MODE_PLAYING &&
2912 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2913 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2916 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2920 int graphic = el2preimg(element);
2922 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2923 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2926 void DrawLevel(int draw_background_mask)
2930 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2931 SetDrawBackgroundMask(draw_background_mask);
2935 for (x = BX1; x <= BX2; x++)
2936 for (y = BY1; y <= BY2; y++)
2937 DrawScreenField(x, y);
2939 redraw_mask |= REDRAW_FIELD;
2942 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2947 for (x = 0; x < size_x; x++)
2948 for (y = 0; y < size_y; y++)
2949 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2951 redraw_mask |= REDRAW_FIELD;
2954 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2958 for (x = 0; x < size_x; x++)
2959 for (y = 0; y < size_y; y++)
2960 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2962 redraw_mask |= REDRAW_FIELD;
2965 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2967 boolean show_level_border = (BorderElement != EL_EMPTY);
2968 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2969 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2970 int tile_size = preview.tile_size;
2971 int preview_width = preview.xsize * tile_size;
2972 int preview_height = preview.ysize * tile_size;
2973 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2974 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2975 int real_preview_width = real_preview_xsize * tile_size;
2976 int real_preview_height = real_preview_ysize * tile_size;
2977 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2978 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2981 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2984 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2986 dst_x += (preview_width - real_preview_width) / 2;
2987 dst_y += (preview_height - real_preview_height) / 2;
2989 for (x = 0; x < real_preview_xsize; x++)
2991 for (y = 0; y < real_preview_ysize; y++)
2993 int lx = from_x + x + (show_level_border ? -1 : 0);
2994 int ly = from_y + y + (show_level_border ? -1 : 0);
2995 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2996 getBorderElement(lx, ly));
2998 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2999 element, tile_size);
3003 redraw_mask |= REDRAW_FIELD;
3006 #define MICROLABEL_EMPTY 0
3007 #define MICROLABEL_LEVEL_NAME 1
3008 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
3009 #define MICROLABEL_LEVEL_AUTHOR 3
3010 #define MICROLABEL_IMPORTED_FROM_HEAD 4
3011 #define MICROLABEL_IMPORTED_FROM 5
3012 #define MICROLABEL_IMPORTED_BY_HEAD 6
3013 #define MICROLABEL_IMPORTED_BY 7
3015 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
3017 int max_text_width = SXSIZE;
3018 int font_width = getFontWidth(font_nr);
3020 if (pos->align == ALIGN_CENTER)
3021 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
3022 else if (pos->align == ALIGN_RIGHT)
3023 max_text_width = pos->x;
3025 max_text_width = SXSIZE - pos->x;
3027 return max_text_width / font_width;
3030 static void DrawPreviewLevelLabelExt(int mode)
3032 struct TextPosInfo *pos = &menu.main.text.level_info_2;
3033 char label_text[MAX_OUTPUT_LINESIZE + 1];
3034 int max_len_label_text;
3035 int font_nr = pos->font;
3038 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3041 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
3042 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
3043 mode == MICROLABEL_IMPORTED_BY_HEAD)
3044 font_nr = pos->font_alt;
3046 max_len_label_text = getMaxTextLength(pos, font_nr);
3048 if (pos->size != -1)
3049 max_len_label_text = pos->size;
3051 for (i = 0; i < max_len_label_text; i++)
3052 label_text[i] = ' ';
3053 label_text[max_len_label_text] = '\0';
3055 if (strlen(label_text) > 0)
3056 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3059 (mode == MICROLABEL_LEVEL_NAME ? level.name :
3060 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
3061 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
3062 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
3063 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
3064 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
3065 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
3066 max_len_label_text);
3067 label_text[max_len_label_text] = '\0';
3069 if (strlen(label_text) > 0)
3070 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3072 redraw_mask |= REDRAW_FIELD;
3075 static void DrawPreviewLevelExt(boolean restart)
3077 static unsigned int scroll_delay = 0;
3078 static unsigned int label_delay = 0;
3079 static int from_x, from_y, scroll_direction;
3080 static int label_state, label_counter;
3081 unsigned int scroll_delay_value = preview.step_delay;
3082 boolean show_level_border = (BorderElement != EL_EMPTY);
3083 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
3084 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
3091 if (preview.anim_mode == ANIM_CENTERED)
3093 if (level_xsize > preview.xsize)
3094 from_x = (level_xsize - preview.xsize) / 2;
3095 if (level_ysize > preview.ysize)
3096 from_y = (level_ysize - preview.ysize) / 2;
3099 from_x += preview.xoffset;
3100 from_y += preview.yoffset;
3102 scroll_direction = MV_RIGHT;
3106 DrawPreviewLevelPlayfieldExt(from_x, from_y);
3107 DrawPreviewLevelLabelExt(label_state);
3109 /* initialize delay counters */
3110 DelayReached(&scroll_delay, 0);
3111 DelayReached(&label_delay, 0);
3113 if (leveldir_current->name)
3115 struct TextPosInfo *pos = &menu.main.text.level_info_1;
3116 char label_text[MAX_OUTPUT_LINESIZE + 1];
3117 int font_nr = pos->font;
3118 int max_len_label_text = getMaxTextLength(pos, font_nr);
3120 if (pos->size != -1)
3121 max_len_label_text = pos->size;
3123 strncpy(label_text, leveldir_current->name, max_len_label_text);
3124 label_text[max_len_label_text] = '\0';
3126 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
3127 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
3133 /* scroll preview level, if needed */
3134 if (preview.anim_mode != ANIM_NONE &&
3135 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
3136 DelayReached(&scroll_delay, scroll_delay_value))
3138 switch (scroll_direction)
3143 from_x -= preview.step_offset;
3144 from_x = (from_x < 0 ? 0 : from_x);
3147 scroll_direction = MV_UP;
3151 if (from_x < level_xsize - preview.xsize)
3153 from_x += preview.step_offset;
3154 from_x = (from_x > level_xsize - preview.xsize ?
3155 level_xsize - preview.xsize : from_x);
3158 scroll_direction = MV_DOWN;
3164 from_y -= preview.step_offset;
3165 from_y = (from_y < 0 ? 0 : from_y);
3168 scroll_direction = MV_RIGHT;
3172 if (from_y < level_ysize - preview.ysize)
3174 from_y += preview.step_offset;
3175 from_y = (from_y > level_ysize - preview.ysize ?
3176 level_ysize - preview.ysize : from_y);
3179 scroll_direction = MV_LEFT;
3186 DrawPreviewLevelPlayfieldExt(from_x, from_y);
3189 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
3190 /* redraw micro level label, if needed */
3191 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
3192 !strEqual(level.author, ANONYMOUS_NAME) &&
3193 !strEqual(level.author, leveldir_current->name) &&
3194 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3196 int max_label_counter = 23;
3198 if (leveldir_current->imported_from != NULL &&
3199 strlen(leveldir_current->imported_from) > 0)
3200 max_label_counter += 14;
3201 if (leveldir_current->imported_by != NULL &&
3202 strlen(leveldir_current->imported_by) > 0)
3203 max_label_counter += 14;
3205 label_counter = (label_counter + 1) % max_label_counter;
3206 label_state = (label_counter >= 0 && label_counter <= 7 ?
3207 MICROLABEL_LEVEL_NAME :
3208 label_counter >= 9 && label_counter <= 12 ?
3209 MICROLABEL_LEVEL_AUTHOR_HEAD :
3210 label_counter >= 14 && label_counter <= 21 ?
3211 MICROLABEL_LEVEL_AUTHOR :
3212 label_counter >= 23 && label_counter <= 26 ?
3213 MICROLABEL_IMPORTED_FROM_HEAD :
3214 label_counter >= 28 && label_counter <= 35 ?
3215 MICROLABEL_IMPORTED_FROM :
3216 label_counter >= 37 && label_counter <= 40 ?
3217 MICROLABEL_IMPORTED_BY_HEAD :
3218 label_counter >= 42 && label_counter <= 49 ?
3219 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3221 if (leveldir_current->imported_from == NULL &&
3222 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3223 label_state == MICROLABEL_IMPORTED_FROM))
3224 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3225 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3227 DrawPreviewLevelLabelExt(label_state);
3231 void DrawPreviewLevelInitial()
3233 DrawPreviewLevelExt(TRUE);
3236 void DrawPreviewLevelAnimation()
3238 DrawPreviewLevelExt(FALSE);
3241 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3242 int graphic, int sync_frame,
3245 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3247 if (mask_mode == USE_MASKING)
3248 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3250 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3253 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3254 int graphic, int sync_frame, int mask_mode)
3256 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3258 if (mask_mode == USE_MASKING)
3259 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3261 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3264 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3266 int lx = LEVELX(x), ly = LEVELY(y);
3268 if (!IN_SCR_FIELD(x, y))
3271 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3272 graphic, GfxFrame[lx][ly], NO_MASKING);
3274 MarkTileDirty(x, y);
3277 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3279 int lx = LEVELX(x), ly = LEVELY(y);
3281 if (!IN_SCR_FIELD(x, y))
3284 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3285 graphic, GfxFrame[lx][ly], NO_MASKING);
3286 MarkTileDirty(x, y);
3289 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3291 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3294 void DrawLevelElementAnimation(int x, int y, int element)
3296 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3298 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3301 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3303 int sx = SCREENX(x), sy = SCREENY(y);
3305 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3308 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3311 DrawGraphicAnimation(sx, sy, graphic);
3314 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3315 DrawLevelFieldCrumbled(x, y);
3317 if (GFX_CRUMBLED(Feld[x][y]))
3318 DrawLevelFieldCrumbled(x, y);
3322 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3324 int sx = SCREENX(x), sy = SCREENY(y);
3327 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3330 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3332 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3335 DrawGraphicAnimation(sx, sy, graphic);
3337 if (GFX_CRUMBLED(element))
3338 DrawLevelFieldCrumbled(x, y);
3341 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3343 if (player->use_murphy)
3345 /* this works only because currently only one player can be "murphy" ... */
3346 static int last_horizontal_dir = MV_LEFT;
3347 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3349 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3350 last_horizontal_dir = move_dir;
3352 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3354 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3356 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3362 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3365 static boolean equalGraphics(int graphic1, int graphic2)
3367 struct GraphicInfo *g1 = &graphic_info[graphic1];
3368 struct GraphicInfo *g2 = &graphic_info[graphic2];
3370 return (g1->bitmap == g2->bitmap &&
3371 g1->src_x == g2->src_x &&
3372 g1->src_y == g2->src_y &&
3373 g1->anim_frames == g2->anim_frames &&
3374 g1->anim_delay == g2->anim_delay &&
3375 g1->anim_mode == g2->anim_mode);
3378 void DrawAllPlayers()
3382 for (i = 0; i < MAX_PLAYERS; i++)
3383 if (stored_player[i].active)
3384 DrawPlayer(&stored_player[i]);
3387 void DrawPlayerField(int x, int y)
3389 if (!IS_PLAYER(x, y))
3392 DrawPlayer(PLAYERINFO(x, y));
3395 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3397 void DrawPlayer(struct PlayerInfo *player)
3399 int jx = player->jx;
3400 int jy = player->jy;
3401 int move_dir = player->MovDir;
3402 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3403 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3404 int last_jx = (player->is_moving ? jx - dx : jx);
3405 int last_jy = (player->is_moving ? jy - dy : jy);
3406 int next_jx = jx + dx;
3407 int next_jy = jy + dy;
3408 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3409 boolean player_is_opaque = FALSE;
3410 int sx = SCREENX(jx), sy = SCREENY(jy);
3411 int sxx = 0, syy = 0;
3412 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3414 int action = ACTION_DEFAULT;
3415 int last_player_graphic = getPlayerGraphic(player, move_dir);
3416 int last_player_frame = player->Frame;
3419 /* GfxElement[][] is set to the element the player is digging or collecting;
3420 remove also for off-screen player if the player is not moving anymore */
3421 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3422 GfxElement[jx][jy] = EL_UNDEFINED;
3424 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3428 if (!IN_LEV_FIELD(jx, jy))
3430 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3431 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3432 printf("DrawPlayerField(): This should never happen!\n");
3437 if (element == EL_EXPLOSION)
3440 action = (player->is_pushing ? ACTION_PUSHING :
3441 player->is_digging ? ACTION_DIGGING :
3442 player->is_collecting ? ACTION_COLLECTING :
3443 player->is_moving ? ACTION_MOVING :
3444 player->is_snapping ? ACTION_SNAPPING :
3445 player->is_dropping ? ACTION_DROPPING :
3446 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3448 if (player->is_waiting)
3449 move_dir = player->dir_waiting;
3451 InitPlayerGfxAnimation(player, action, move_dir);
3453 /* ----------------------------------------------------------------------- */
3454 /* draw things in the field the player is leaving, if needed */
3455 /* ----------------------------------------------------------------------- */
3457 if (player->is_moving)
3459 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3461 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3463 if (last_element == EL_DYNAMITE_ACTIVE ||
3464 last_element == EL_EM_DYNAMITE_ACTIVE ||
3465 last_element == EL_SP_DISK_RED_ACTIVE)
3466 DrawDynamite(last_jx, last_jy);
3468 DrawLevelFieldThruMask(last_jx, last_jy);
3470 else if (last_element == EL_DYNAMITE_ACTIVE ||
3471 last_element == EL_EM_DYNAMITE_ACTIVE ||
3472 last_element == EL_SP_DISK_RED_ACTIVE)
3473 DrawDynamite(last_jx, last_jy);
3475 /* !!! this is not enough to prevent flickering of players which are
3476 moving next to each others without a free tile between them -- this
3477 can only be solved by drawing all players layer by layer (first the
3478 background, then the foreground etc.) !!! => TODO */
3479 else if (!IS_PLAYER(last_jx, last_jy))
3480 DrawLevelField(last_jx, last_jy);
3483 DrawLevelField(last_jx, last_jy);
3486 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3487 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3490 if (!IN_SCR_FIELD(sx, sy))
3493 /* ----------------------------------------------------------------------- */
3494 /* draw things behind the player, if needed */
3495 /* ----------------------------------------------------------------------- */
3498 DrawLevelElement(jx, jy, Back[jx][jy]);
3499 else if (IS_ACTIVE_BOMB(element))
3500 DrawLevelElement(jx, jy, EL_EMPTY);
3503 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3505 int old_element = GfxElement[jx][jy];
3506 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3507 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3509 if (GFX_CRUMBLED(old_element))
3510 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3512 DrawGraphic(sx, sy, old_graphic, frame);
3514 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3515 player_is_opaque = TRUE;
3519 GfxElement[jx][jy] = EL_UNDEFINED;
3521 /* make sure that pushed elements are drawn with correct frame rate */
3522 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3524 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3525 GfxFrame[jx][jy] = player->StepFrame;
3527 DrawLevelField(jx, jy);
3531 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3532 /* ----------------------------------------------------------------------- */
3533 /* draw player himself */
3534 /* ----------------------------------------------------------------------- */
3536 graphic = getPlayerGraphic(player, move_dir);
3538 /* in the case of changed player action or direction, prevent the current
3539 animation frame from being restarted for identical animations */
3540 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3541 player->Frame = last_player_frame;
3543 frame = getGraphicAnimationFrame(graphic, player->Frame);
3547 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3548 sxx = player->GfxPos;
3550 syy = player->GfxPos;
3553 if (player_is_opaque)
3554 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3556 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3558 if (SHIELD_ON(player))
3560 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3561 IMG_SHIELD_NORMAL_ACTIVE);
3562 int frame = getGraphicAnimationFrame(graphic, -1);
3564 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3568 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3571 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3572 sxx = player->GfxPos;
3574 syy = player->GfxPos;
3578 /* ----------------------------------------------------------------------- */
3579 /* draw things the player is pushing, if needed */
3580 /* ----------------------------------------------------------------------- */
3582 if (player->is_pushing && player->is_moving)
3584 int px = SCREENX(jx), py = SCREENY(jy);
3585 int pxx = (TILEX - ABS(sxx)) * dx;
3586 int pyy = (TILEY - ABS(syy)) * dy;
3587 int gfx_frame = GfxFrame[jx][jy];
3593 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3595 element = Feld[next_jx][next_jy];
3596 gfx_frame = GfxFrame[next_jx][next_jy];
3599 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3601 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3602 frame = getGraphicAnimationFrame(graphic, sync_frame);
3604 /* draw background element under pushed element (like the Sokoban field) */
3605 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3607 /* this allows transparent pushing animation over non-black background */
3610 DrawLevelElement(jx, jy, Back[jx][jy]);
3612 DrawLevelElement(jx, jy, EL_EMPTY);
3614 if (Back[next_jx][next_jy])
3615 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3617 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3619 else if (Back[next_jx][next_jy])
3620 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3623 /* do not draw (EM style) pushing animation when pushing is finished */
3624 /* (two-tile animations usually do not contain start and end frame) */
3625 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3626 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3628 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3630 /* masked drawing is needed for EMC style (double) movement graphics */
3631 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3632 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3636 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3637 /* ----------------------------------------------------------------------- */
3638 /* draw player himself */
3639 /* ----------------------------------------------------------------------- */
3641 graphic = getPlayerGraphic(player, move_dir);
3643 /* in the case of changed player action or direction, prevent the current
3644 animation frame from being restarted for identical animations */
3645 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3646 player->Frame = last_player_frame;
3648 frame = getGraphicAnimationFrame(graphic, player->Frame);
3652 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3653 sxx = player->GfxPos;
3655 syy = player->GfxPos;
3658 if (player_is_opaque)
3659 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3661 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3663 if (SHIELD_ON(player))
3665 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3666 IMG_SHIELD_NORMAL_ACTIVE);
3667 int frame = getGraphicAnimationFrame(graphic, -1);
3669 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3673 /* ----------------------------------------------------------------------- */
3674 /* draw things in front of player (active dynamite or dynabombs) */
3675 /* ----------------------------------------------------------------------- */
3677 if (IS_ACTIVE_BOMB(element))
3679 graphic = el2img(element);
3680 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3682 if (game.emulation == EMU_SUPAPLEX)
3683 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3685 DrawGraphicThruMask(sx, sy, graphic, frame);
3688 if (player_is_moving && last_element == EL_EXPLOSION)
3690 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3691 GfxElement[last_jx][last_jy] : EL_EMPTY);
3692 int graphic = el_act2img(element, ACTION_EXPLODING);
3693 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3694 int phase = ExplodePhase[last_jx][last_jy] - 1;
3695 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3698 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3701 /* ----------------------------------------------------------------------- */
3702 /* draw elements the player is just walking/passing through/under */
3703 /* ----------------------------------------------------------------------- */
3705 if (player_is_moving)
3707 /* handle the field the player is leaving ... */
3708 if (IS_ACCESSIBLE_INSIDE(last_element))
3709 DrawLevelField(last_jx, last_jy);
3710 else if (IS_ACCESSIBLE_UNDER(last_element))
3711 DrawLevelFieldThruMask(last_jx, last_jy);
3714 /* do not redraw accessible elements if the player is just pushing them */
3715 if (!player_is_moving || !player->is_pushing)
3717 /* ... and the field the player is entering */
3718 if (IS_ACCESSIBLE_INSIDE(element))
3719 DrawLevelField(jx, jy);
3720 else if (IS_ACCESSIBLE_UNDER(element))
3721 DrawLevelFieldThruMask(jx, jy);
3724 MarkTileDirty(sx, sy);
3727 /* ------------------------------------------------------------------------- */
3729 void WaitForEventToContinue()
3731 boolean still_wait = TRUE;
3733 /* simulate releasing mouse button over last gadget, if still pressed */
3735 HandleGadgets(-1, -1, 0);
3737 button_status = MB_RELEASED;
3751 case EVENT_BUTTONPRESS:
3752 case EVENT_KEYPRESS:
3756 case EVENT_KEYRELEASE:
3757 ClearPlayerAction();
3761 HandleOtherEvents(&event);
3765 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3774 #define MAX_REQUEST_LINES 13
3775 #define MAX_REQUEST_LINE_FONT1_LEN 7
3776 #define MAX_REQUEST_LINE_FONT2_LEN 10
3778 static int RequestHandleEvents(unsigned int req_state)
3780 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3781 local_player->LevelSolved_GameEnd);
3782 int width = request.width;
3783 int height = request.height;
3787 setRequestPosition(&sx, &sy, FALSE);
3789 button_status = MB_RELEASED;
3791 request_gadget_id = -1;
3798 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3800 HandleGameActions();
3802 SetDrawtoField(DRAW_TO_BACKBUFFER);
3804 if (global.use_envelope_request)
3806 /* copy current state of request area to middle of playfield area */
3807 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
3815 while (NextValidEvent(&event))
3819 case EVENT_BUTTONPRESS:
3820 case EVENT_BUTTONRELEASE:
3821 case EVENT_MOTIONNOTIFY:
3825 if (event.type == EVENT_MOTIONNOTIFY)
3830 motion_status = TRUE;
3831 mx = ((MotionEvent *) &event)->x;
3832 my = ((MotionEvent *) &event)->y;
3836 motion_status = FALSE;
3837 mx = ((ButtonEvent *) &event)->x;
3838 my = ((ButtonEvent *) &event)->y;
3839 if (event.type == EVENT_BUTTONPRESS)
3840 button_status = ((ButtonEvent *) &event)->button;
3842 button_status = MB_RELEASED;
3845 /* this sets 'request_gadget_id' */
3846 HandleGadgets(mx, my, button_status);
3848 switch (request_gadget_id)
3850 case TOOL_CTRL_ID_YES:
3853 case TOOL_CTRL_ID_NO:
3856 case TOOL_CTRL_ID_CONFIRM:
3857 result = TRUE | FALSE;
3860 case TOOL_CTRL_ID_PLAYER_1:
3863 case TOOL_CTRL_ID_PLAYER_2:
3866 case TOOL_CTRL_ID_PLAYER_3:
3869 case TOOL_CTRL_ID_PLAYER_4:
3880 case EVENT_KEYPRESS:
3882 Key key = GetEventKey((KeyEvent *)&event, TRUE);
3887 if (req_state & REQ_CONFIRM)
3892 #if defined(TARGET_SDL2)
3899 #if defined(TARGET_SDL2)
3906 HandleKeysDebug(key);
3910 if (req_state & REQ_PLAYER)
3916 case EVENT_KEYRELEASE:
3917 ClearPlayerAction();
3921 HandleOtherEvents(&event);
3926 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3928 int joy = AnyJoystick();
3930 if (joy & JOY_BUTTON_1)
3932 else if (joy & JOY_BUTTON_2)
3938 if (global.use_envelope_request)
3940 /* copy back current state of pressed buttons inside request area */
3941 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
3951 static boolean RequestDoor(char *text, unsigned int req_state)
3953 unsigned int old_door_state;
3954 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3955 int font_nr = FONT_TEXT_2;
3960 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3962 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3963 font_nr = FONT_TEXT_1;
3966 if (game_status == GAME_MODE_PLAYING)
3967 BlitScreenToBitmap(backbuffer);
3969 /* disable deactivated drawing when quick-loading level tape recording */
3970 if (tape.playing && tape.deactivate_display)
3971 TapeDeactivateDisplayOff(TRUE);
3973 SetMouseCursor(CURSOR_DEFAULT);
3975 #if defined(NETWORK_AVALIABLE)
3976 /* pause network game while waiting for request to answer */
3977 if (options.network &&
3978 game_status == GAME_MODE_PLAYING &&
3979 req_state & REQUEST_WAIT_FOR_INPUT)
3980 SendToServer_PausePlaying();
3983 old_door_state = GetDoorState();
3985 /* simulate releasing mouse button over last gadget, if still pressed */
3987 HandleGadgets(-1, -1, 0);
3991 /* draw released gadget before proceeding */
3994 if (old_door_state & DOOR_OPEN_1)
3996 CloseDoor(DOOR_CLOSE_1);
3998 /* save old door content */
3999 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4000 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
4003 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4004 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4006 /* clear door drawing field */
4007 DrawBackground(DX, DY, DXSIZE, DYSIZE);
4009 /* force DOOR font inside door area */
4010 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
4012 /* write text for request */
4013 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
4015 char text_line[max_request_line_len + 1];
4021 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
4023 tc = *(text_ptr + tx);
4024 // if (!tc || tc == ' ')
4025 if (!tc || tc == ' ' || tc == '?' || tc == '!')
4029 if ((tc == '?' || tc == '!') && tl == 0)
4039 strncpy(text_line, text_ptr, tl);
4042 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
4043 DY + 8 + ty * (getFontHeight(font_nr) + 2),
4044 text_line, font_nr);
4046 text_ptr += tl + (tc == ' ' ? 1 : 0);
4047 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
4052 if (req_state & REQ_ASK)
4054 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
4055 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
4057 else if (req_state & REQ_CONFIRM)
4059 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
4061 else if (req_state & REQ_PLAYER)
4063 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
4064 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
4065 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
4066 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
4069 /* copy request gadgets to door backbuffer */
4070 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4072 OpenDoor(DOOR_OPEN_1);
4074 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4076 if (game_status == GAME_MODE_PLAYING)
4078 SetPanelBackground();
4079 SetDrawBackgroundMask(REDRAW_DOOR_1);
4083 SetDrawBackgroundMask(REDRAW_FIELD);
4089 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4091 // ---------- handle request buttons ----------
4092 result = RequestHandleEvents(req_state);
4096 if (!(req_state & REQ_STAY_OPEN))
4098 CloseDoor(DOOR_CLOSE_1);
4100 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
4101 (req_state & REQ_REOPEN))
4102 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
4107 if (game_status == GAME_MODE_PLAYING)
4109 SetPanelBackground();
4110 SetDrawBackgroundMask(REDRAW_DOOR_1);
4114 SetDrawBackgroundMask(REDRAW_FIELD);
4117 #if defined(NETWORK_AVALIABLE)
4118 /* continue network game after request */
4119 if (options.network &&
4120 game_status == GAME_MODE_PLAYING &&
4121 req_state & REQUEST_WAIT_FOR_INPUT)
4122 SendToServer_ContinuePlaying();
4125 /* restore deactivated drawing when quick-loading level tape recording */
4126 if (tape.playing && tape.deactivate_display)
4127 TapeDeactivateDisplayOn();
4132 static boolean RequestEnvelope(char *text, unsigned int req_state)
4136 if (game_status == GAME_MODE_PLAYING)
4137 BlitScreenToBitmap(backbuffer);
4139 /* disable deactivated drawing when quick-loading level tape recording */
4140 if (tape.playing && tape.deactivate_display)
4141 TapeDeactivateDisplayOff(TRUE);
4143 SetMouseCursor(CURSOR_DEFAULT);
4145 #if defined(NETWORK_AVALIABLE)
4146 /* pause network game while waiting for request to answer */
4147 if (options.network &&
4148 game_status == GAME_MODE_PLAYING &&
4149 req_state & REQUEST_WAIT_FOR_INPUT)
4150 SendToServer_PausePlaying();
4153 /* simulate releasing mouse button over last gadget, if still pressed */
4155 HandleGadgets(-1, -1, 0);
4159 // (replace with setting corresponding request background)
4160 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
4161 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4163 /* clear door drawing field */
4164 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
4166 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
4168 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
4170 if (game_status == GAME_MODE_PLAYING)
4172 SetPanelBackground();
4173 SetDrawBackgroundMask(REDRAW_DOOR_1);
4177 SetDrawBackgroundMask(REDRAW_FIELD);
4183 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
4185 // ---------- handle request buttons ----------
4186 result = RequestHandleEvents(req_state);
4190 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
4194 if (game_status == GAME_MODE_PLAYING)
4196 SetPanelBackground();
4197 SetDrawBackgroundMask(REDRAW_DOOR_1);
4201 SetDrawBackgroundMask(REDRAW_FIELD);
4204 #if defined(NETWORK_AVALIABLE)
4205 /* continue network game after request */
4206 if (options.network &&
4207 game_status == GAME_MODE_PLAYING &&
4208 req_state & REQUEST_WAIT_FOR_INPUT)
4209 SendToServer_ContinuePlaying();
4212 /* restore deactivated drawing when quick-loading level tape recording */
4213 if (tape.playing && tape.deactivate_display)
4214 TapeDeactivateDisplayOn();
4219 boolean Request(char *text, unsigned int req_state)
4221 if (global.use_envelope_request)
4222 return RequestEnvelope(text, req_state);
4224 return RequestDoor(text, req_state);
4227 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4229 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4230 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4233 if (dpo1->sort_priority != dpo2->sort_priority)
4234 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4236 compare_result = dpo1->nr - dpo2->nr;
4238 return compare_result;
4241 void InitGraphicCompatibilityInfo_Doors()
4247 struct DoorInfo *door;
4251 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4252 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4254 { -1, -1, -1, NULL }
4256 struct Rect door_rect_list[] =
4258 { DX, DY, DXSIZE, DYSIZE },
4259 { VX, VY, VXSIZE, VYSIZE }
4263 for (i = 0; doors[i].door_token != -1; i++)
4265 int door_token = doors[i].door_token;
4266 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4267 int part_1 = doors[i].part_1;
4268 int part_8 = doors[i].part_8;
4269 int part_2 = part_1 + 1;
4270 int part_3 = part_1 + 2;
4271 struct DoorInfo *door = doors[i].door;
4272 struct Rect *door_rect = &door_rect_list[door_index];
4273 boolean door_gfx_redefined = FALSE;
4275 /* check if any door part graphic definitions have been redefined */
4277 for (j = 0; door_part_controls[j].door_token != -1; j++)
4279 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4280 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4282 if (dpc->door_token == door_token && fi->redefined)
4283 door_gfx_redefined = TRUE;
4286 /* check for old-style door graphic/animation modifications */
4288 if (!door_gfx_redefined)
4290 if (door->anim_mode & ANIM_STATIC_PANEL)
4292 door->panel.step_xoffset = 0;
4293 door->panel.step_yoffset = 0;
4296 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4298 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4299 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4300 int num_door_steps, num_panel_steps;
4302 /* remove door part graphics other than the two default wings */
4304 for (j = 0; door_part_controls[j].door_token != -1; j++)
4306 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4307 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4309 if (dpc->graphic >= part_3 &&
4310 dpc->graphic <= part_8)
4314 /* set graphics and screen positions of the default wings */
4316 g_part_1->width = door_rect->width;
4317 g_part_1->height = door_rect->height;
4318 g_part_2->width = door_rect->width;
4319 g_part_2->height = door_rect->height;
4320 g_part_2->src_x = door_rect->width;
4321 g_part_2->src_y = g_part_1->src_y;
4323 door->part_2.x = door->part_1.x;
4324 door->part_2.y = door->part_1.y;
4326 if (door->width != -1)
4328 g_part_1->width = door->width;
4329 g_part_2->width = door->width;
4331 // special treatment for graphics and screen position of right wing
4332 g_part_2->src_x += door_rect->width - door->width;
4333 door->part_2.x += door_rect->width - door->width;
4336 if (door->height != -1)
4338 g_part_1->height = door->height;
4339 g_part_2->height = door->height;
4341 // special treatment for graphics and screen position of bottom wing
4342 g_part_2->src_y += door_rect->height - door->height;
4343 door->part_2.y += door_rect->height - door->height;
4346 /* set animation delays for the default wings and panels */
4348 door->part_1.step_delay = door->step_delay;
4349 door->part_2.step_delay = door->step_delay;
4350 door->panel.step_delay = door->step_delay;
4352 /* set animation draw order for the default wings */
4354 door->part_1.sort_priority = 2; /* draw left wing over ... */
4355 door->part_2.sort_priority = 1; /* ... right wing */
4357 /* set animation draw offset for the default wings */
4359 if (door->anim_mode & ANIM_HORIZONTAL)
4361 door->part_1.step_xoffset = door->step_offset;
4362 door->part_1.step_yoffset = 0;
4363 door->part_2.step_xoffset = door->step_offset * -1;
4364 door->part_2.step_yoffset = 0;
4366 num_door_steps = g_part_1->width / door->step_offset;
4368 else // ANIM_VERTICAL
4370 door->part_1.step_xoffset = 0;
4371 door->part_1.step_yoffset = door->step_offset;
4372 door->part_2.step_xoffset = 0;
4373 door->part_2.step_yoffset = door->step_offset * -1;
4375 num_door_steps = g_part_1->height / door->step_offset;
4378 /* set animation draw offset for the default panels */
4380 if (door->step_offset > 1)
4382 num_panel_steps = 2 * door_rect->height / door->step_offset;
4383 door->panel.start_step = num_panel_steps - num_door_steps;
4384 door->panel.start_step_closing = door->panel.start_step;
4388 num_panel_steps = door_rect->height / door->step_offset;
4389 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4390 door->panel.start_step_closing = door->panel.start_step;
4391 door->panel.step_delay *= 2;
4402 for (i = 0; door_part_controls[i].door_token != -1; i++)
4404 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4405 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4407 /* initialize "start_step_opening" and "start_step_closing", if needed */
4408 if (dpc->pos->start_step_opening == 0 &&
4409 dpc->pos->start_step_closing == 0)
4411 // dpc->pos->start_step_opening = dpc->pos->start_step;
4412 dpc->pos->start_step_closing = dpc->pos->start_step;
4415 /* fill structure for door part draw order (sorted below) */
4417 dpo->sort_priority = dpc->pos->sort_priority;
4420 /* sort door part controls according to sort_priority and graphic number */
4421 qsort(door_part_order, MAX_DOOR_PARTS,
4422 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4425 unsigned int OpenDoor(unsigned int door_state)
4427 if (door_state & DOOR_COPY_BACK)
4429 if (door_state & DOOR_OPEN_1)
4430 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4431 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4433 if (door_state & DOOR_OPEN_2)
4434 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4435 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4437 door_state &= ~DOOR_COPY_BACK;
4440 return MoveDoor(door_state);
4443 unsigned int CloseDoor(unsigned int door_state)
4445 unsigned int old_door_state = GetDoorState();
4447 if (!(door_state & DOOR_NO_COPY_BACK))
4449 if (old_door_state & DOOR_OPEN_1)
4450 BlitBitmap(backbuffer, bitmap_db_door_1,
4451 DX, DY, DXSIZE, DYSIZE, 0, 0);
4453 if (old_door_state & DOOR_OPEN_2)
4454 BlitBitmap(backbuffer, bitmap_db_door_2,
4455 VX, VY, VXSIZE, VYSIZE, 0, 0);
4457 door_state &= ~DOOR_NO_COPY_BACK;
4460 return MoveDoor(door_state);
4463 unsigned int GetDoorState()
4465 return MoveDoor(DOOR_GET_STATE);
4468 unsigned int SetDoorState(unsigned int door_state)
4470 return MoveDoor(door_state | DOOR_SET_STATE);
4473 int euclid(int a, int b)
4475 return (b ? euclid(b, a % b) : a);
4478 unsigned int MoveDoor(unsigned int door_state)
4480 struct Rect door_rect_list[] =
4482 { DX, DY, DXSIZE, DYSIZE },
4483 { VX, VY, VXSIZE, VYSIZE }
4485 static int door1 = DOOR_CLOSE_1;
4486 static int door2 = DOOR_CLOSE_2;
4487 unsigned int door_delay = 0;
4488 unsigned int door_delay_value;
4491 if (door_state == DOOR_GET_STATE)
4492 return (door1 | door2);
4494 if (door_state & DOOR_SET_STATE)
4496 if (door_state & DOOR_ACTION_1)
4497 door1 = door_state & DOOR_ACTION_1;
4498 if (door_state & DOOR_ACTION_2)
4499 door2 = door_state & DOOR_ACTION_2;
4501 return (door1 | door2);
4504 if (!(door_state & DOOR_FORCE_REDRAW))
4506 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4507 door_state &= ~DOOR_OPEN_1;
4508 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4509 door_state &= ~DOOR_CLOSE_1;
4510 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4511 door_state &= ~DOOR_OPEN_2;
4512 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4513 door_state &= ~DOOR_CLOSE_2;
4516 if (global.autoplay_leveldir)
4518 door_state |= DOOR_NO_DELAY;
4519 door_state &= ~DOOR_CLOSE_ALL;
4522 if (game_status == GAME_MODE_EDITOR)
4523 door_state |= DOOR_NO_DELAY;
4525 if (door_state & DOOR_ACTION)
4527 boolean door_panel_drawn[NUM_DOORS];
4528 boolean panel_has_doors[NUM_DOORS];
4529 boolean door_part_skip[MAX_DOOR_PARTS];
4530 boolean door_part_done[MAX_DOOR_PARTS];
4531 boolean door_part_done_all;
4532 int num_steps[MAX_DOOR_PARTS];
4533 int max_move_delay = 0; // delay for complete animations of all doors
4534 int max_step_delay = 0; // delay (ms) between two animation frames
4535 int num_move_steps = 0; // number of animation steps for all doors
4536 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4537 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4538 int current_move_delay = 0;
4542 for (i = 0; i < NUM_DOORS; i++)
4543 panel_has_doors[i] = FALSE;
4545 for (i = 0; i < MAX_DOOR_PARTS; i++)
4547 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4548 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4549 int door_token = dpc->door_token;
4551 door_part_done[i] = FALSE;
4552 door_part_skip[i] = (!(door_state & door_token) ||
4556 for (i = 0; i < MAX_DOOR_PARTS; i++)
4558 int nr = door_part_order[i].nr;
4559 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4560 struct DoorPartPosInfo *pos = dpc->pos;
4561 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4562 int door_token = dpc->door_token;
4563 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4564 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4565 int step_xoffset = ABS(pos->step_xoffset);
4566 int step_yoffset = ABS(pos->step_yoffset);
4567 int step_delay = pos->step_delay;
4568 int current_door_state = door_state & door_token;
4569 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4570 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4571 boolean part_opening = (is_panel ? door_closing : door_opening);
4572 int start_step = (part_opening ? pos->start_step_opening :
4573 pos->start_step_closing);
4574 float move_xsize = (step_xoffset ? g->width : 0);
4575 float move_ysize = (step_yoffset ? g->height : 0);
4576 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4577 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4578 int move_steps = (move_xsteps && move_ysteps ?
4579 MIN(move_xsteps, move_ysteps) :
4580 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4581 int move_delay = move_steps * step_delay;
4583 if (door_part_skip[nr])
4586 max_move_delay = MAX(max_move_delay, move_delay);
4587 max_step_delay = (max_step_delay == 0 ? step_delay :
4588 euclid(max_step_delay, step_delay));
4589 num_steps[nr] = move_steps;
4593 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4595 panel_has_doors[door_index] = TRUE;
4599 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4601 num_move_steps = max_move_delay / max_step_delay;
4602 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4604 door_delay_value = max_step_delay;
4606 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4608 start = num_move_steps - 1;
4612 /* opening door sound has priority over simultaneously closing door */
4613 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4614 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4615 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4616 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4619 for (k = start; k < num_move_steps; k++)
4621 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4623 door_part_done_all = TRUE;
4625 for (i = 0; i < NUM_DOORS; i++)
4626 door_panel_drawn[i] = FALSE;
4628 for (i = 0; i < MAX_DOOR_PARTS; i++)
4630 int nr = door_part_order[i].nr;
4631 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4632 struct DoorPartPosInfo *pos = dpc->pos;
4633 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4634 int door_token = dpc->door_token;
4635 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4636 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4637 boolean is_panel_and_door_has_closed = FALSE;
4638 struct Rect *door_rect = &door_rect_list[door_index];
4639 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4641 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4642 int current_door_state = door_state & door_token;
4643 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4644 boolean door_closing = !door_opening;
4645 boolean part_opening = (is_panel ? door_closing : door_opening);
4646 boolean part_closing = !part_opening;
4647 int start_step = (part_opening ? pos->start_step_opening :
4648 pos->start_step_closing);
4649 int step_delay = pos->step_delay;
4650 int step_factor = step_delay / max_step_delay;
4651 int k1 = (step_factor ? k / step_factor + 1 : k);
4652 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4653 int kk = MAX(0, k2);
4656 int src_x, src_y, src_xx, src_yy;
4657 int dst_x, dst_y, dst_xx, dst_yy;
4660 if (door_part_skip[nr])
4663 if (!(door_state & door_token))
4671 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4672 int kk_door = MAX(0, k2_door);
4673 int sync_frame = kk_door * door_delay_value;
4674 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4676 getFixedGraphicSource(dpc->graphic, frame, &bitmap,
4677 &g_src_x, &g_src_y);
4682 if (!door_panel_drawn[door_index])
4684 ClearRectangle(drawto, door_rect->x, door_rect->y,
4685 door_rect->width, door_rect->height);
4687 door_panel_drawn[door_index] = TRUE;
4690 // draw opening or closing door parts
4692 if (pos->step_xoffset < 0) // door part on right side
4695 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4698 if (dst_xx + width > door_rect->width)
4699 width = door_rect->width - dst_xx;
4701 else // door part on left side
4704 dst_xx = pos->x - kk * pos->step_xoffset;
4708 src_xx = ABS(dst_xx);
4712 width = g->width - src_xx;
4714 if (width > door_rect->width)
4715 width = door_rect->width;
4717 // printf("::: k == %d [%d] \n", k, start_step);
4720 if (pos->step_yoffset < 0) // door part on bottom side
4723 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4726 if (dst_yy + height > door_rect->height)
4727 height = door_rect->height - dst_yy;
4729 else // door part on top side
4732 dst_yy = pos->y - kk * pos->step_yoffset;
4736 src_yy = ABS(dst_yy);
4740 height = g->height - src_yy;
4743 src_x = g_src_x + src_xx;
4744 src_y = g_src_y + src_yy;
4746 dst_x = door_rect->x + dst_xx;
4747 dst_y = door_rect->y + dst_yy;
4749 is_panel_and_door_has_closed =
4752 panel_has_doors[door_index] &&
4753 k >= num_move_steps_doors_only - 1);
4755 if (width >= 0 && width <= g->width &&
4756 height >= 0 && height <= g->height &&
4757 !is_panel_and_door_has_closed)
4759 if (is_panel || !pos->draw_masked)
4760 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4763 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4767 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4769 if ((part_opening && (width < 0 || height < 0)) ||
4770 (part_closing && (width >= g->width && height >= g->height)))
4771 door_part_done[nr] = TRUE;
4773 // continue door part animations, but not panel after door has closed
4774 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4775 door_part_done_all = FALSE;
4778 if (!(door_state & DOOR_NO_DELAY))
4782 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4784 current_move_delay += max_step_delay;
4787 if (door_part_done_all)
4792 if (door_state & DOOR_ACTION_1)
4793 door1 = door_state & DOOR_ACTION_1;
4794 if (door_state & DOOR_ACTION_2)
4795 door2 = door_state & DOOR_ACTION_2;
4797 // draw masked border over door area
4798 DrawMaskedBorder(REDRAW_DOOR_1);
4799 DrawMaskedBorder(REDRAW_DOOR_2);
4801 return (door1 | door2);
4804 static boolean useSpecialEditorDoor()
4806 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4807 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4809 // do not draw special editor door if editor border defined or redefined
4810 if (graphic_info[graphic].bitmap != NULL || redefined)
4813 // do not draw special editor door if global border defined to be empty
4814 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4817 // do not draw special editor door if viewport definitions do not match
4821 EY + EYSIZE != VY + VYSIZE)
4827 void DrawSpecialEditorDoor()
4829 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4830 int top_border_width = gfx1->width;
4831 int top_border_height = gfx1->height;
4832 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4833 int ex = EX - outer_border;
4834 int ey = EY - outer_border;
4835 int vy = VY - outer_border;
4836 int exsize = EXSIZE + 2 * outer_border;
4838 if (!useSpecialEditorDoor())
4841 /* draw bigger level editor toolbox window */
4842 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4843 top_border_width, top_border_height, ex, ey - top_border_height);
4844 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4845 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4847 redraw_mask |= REDRAW_ALL;
4850 void UndrawSpecialEditorDoor()
4852 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4853 int top_border_width = gfx1->width;
4854 int top_border_height = gfx1->height;
4855 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4856 int ex = EX - outer_border;
4857 int ey = EY - outer_border;
4858 int ey_top = ey - top_border_height;
4859 int exsize = EXSIZE + 2 * outer_border;
4860 int eysize = EYSIZE + 2 * outer_border;
4862 if (!useSpecialEditorDoor())
4865 /* draw normal tape recorder window */
4866 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4868 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4869 ex, ey_top, top_border_width, top_border_height,
4871 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4872 ex, ey, exsize, eysize, ex, ey);
4876 // if screen background is set to "[NONE]", clear editor toolbox window
4877 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4878 ClearRectangle(drawto, ex, ey, exsize, eysize);
4881 redraw_mask |= REDRAW_ALL;
4885 /* ---------- new tool button stuff ---------------------------------------- */
4890 struct TextPosInfo *pos;
4893 } toolbutton_info[NUM_TOOL_BUTTONS] =
4896 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
4897 TOOL_CTRL_ID_YES, "yes"
4900 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
4901 TOOL_CTRL_ID_NO, "no"
4904 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
4905 TOOL_CTRL_ID_CONFIRM, "confirm"
4908 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
4909 TOOL_CTRL_ID_PLAYER_1, "player 1"
4912 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
4913 TOOL_CTRL_ID_PLAYER_2, "player 2"
4916 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
4917 TOOL_CTRL_ID_PLAYER_3, "player 3"
4920 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
4921 TOOL_CTRL_ID_PLAYER_4, "player 4"
4925 void CreateToolButtons()
4929 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4931 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4932 struct TextPosInfo *pos = toolbutton_info[i].pos;
4933 struct GadgetInfo *gi;
4934 Bitmap *deco_bitmap = None;
4935 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4936 unsigned int event_mask = GD_EVENT_RELEASED;
4939 int gd_x = gfx->src_x;
4940 int gd_y = gfx->src_y;
4941 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4942 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4945 if (global.use_envelope_request)
4946 setRequestPosition(&dx, &dy, TRUE);
4948 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4950 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4952 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4953 pos->size, &deco_bitmap, &deco_x, &deco_y);
4954 deco_xpos = (gfx->width - pos->size) / 2;
4955 deco_ypos = (gfx->height - pos->size) / 2;
4958 gi = CreateGadget(GDI_CUSTOM_ID, id,
4959 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4960 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4961 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4962 GDI_WIDTH, gfx->width,
4963 GDI_HEIGHT, gfx->height,
4964 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4965 GDI_STATE, GD_BUTTON_UNPRESSED,
4966 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4967 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4968 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4969 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4970 GDI_DECORATION_SIZE, pos->size, pos->size,
4971 GDI_DECORATION_SHIFTING, 1, 1,
4972 GDI_DIRECT_DRAW, FALSE,
4973 GDI_EVENT_MASK, event_mask,
4974 GDI_CALLBACK_ACTION, HandleToolButtons,
4978 Error(ERR_EXIT, "cannot create gadget");
4980 tool_gadget[id] = gi;
4984 void FreeToolButtons()
4988 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4989 FreeGadget(tool_gadget[i]);
4992 static void UnmapToolButtons()
4996 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4997 UnmapGadget(tool_gadget[i]);
5000 static void HandleToolButtons(struct GadgetInfo *gi)
5002 request_gadget_id = gi->custom_id;
5005 static struct Mapping_EM_to_RND_object
5008 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
5009 boolean is_backside; /* backside of moving element */
5015 em_object_mapping_list[] =
5018 Xblank, TRUE, FALSE,
5022 Yacid_splash_eB, FALSE, FALSE,
5023 EL_ACID_SPLASH_RIGHT, -1, -1
5026 Yacid_splash_wB, FALSE, FALSE,
5027 EL_ACID_SPLASH_LEFT, -1, -1
5030 #ifdef EM_ENGINE_BAD_ROLL
5032 Xstone_force_e, FALSE, FALSE,
5033 EL_ROCK, -1, MV_BIT_RIGHT
5036 Xstone_force_w, FALSE, FALSE,
5037 EL_ROCK, -1, MV_BIT_LEFT
5040 Xnut_force_e, FALSE, FALSE,
5041 EL_NUT, -1, MV_BIT_RIGHT
5044 Xnut_force_w, FALSE, FALSE,
5045 EL_NUT, -1, MV_BIT_LEFT
5048 Xspring_force_e, FALSE, FALSE,
5049 EL_SPRING, -1, MV_BIT_RIGHT
5052 Xspring_force_w, FALSE, FALSE,
5053 EL_SPRING, -1, MV_BIT_LEFT
5056 Xemerald_force_e, FALSE, FALSE,
5057 EL_EMERALD, -1, MV_BIT_RIGHT
5060 Xemerald_force_w, FALSE, FALSE,
5061 EL_EMERALD, -1, MV_BIT_LEFT
5064 Xdiamond_force_e, FALSE, FALSE,
5065 EL_DIAMOND, -1, MV_BIT_RIGHT
5068 Xdiamond_force_w, FALSE, FALSE,
5069 EL_DIAMOND, -1, MV_BIT_LEFT
5072 Xbomb_force_e, FALSE, FALSE,
5073 EL_BOMB, -1, MV_BIT_RIGHT
5076 Xbomb_force_w, FALSE, FALSE,
5077 EL_BOMB, -1, MV_BIT_LEFT
5079 #endif /* EM_ENGINE_BAD_ROLL */
5082 Xstone, TRUE, FALSE,
5086 Xstone_pause, FALSE, FALSE,
5090 Xstone_fall, FALSE, FALSE,
5094 Ystone_s, FALSE, FALSE,
5095 EL_ROCK, ACTION_FALLING, -1
5098 Ystone_sB, FALSE, TRUE,
5099 EL_ROCK, ACTION_FALLING, -1
5102 Ystone_e, FALSE, FALSE,
5103 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5106 Ystone_eB, FALSE, TRUE,
5107 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
5110 Ystone_w, FALSE, FALSE,
5111 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5114 Ystone_wB, FALSE, TRUE,
5115 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
5122 Xnut_pause, FALSE, FALSE,
5126 Xnut_fall, FALSE, FALSE,
5130 Ynut_s, FALSE, FALSE,
5131 EL_NUT, ACTION_FALLING, -1
5134 Ynut_sB, FALSE, TRUE,
5135 EL_NUT, ACTION_FALLING, -1
5138 Ynut_e, FALSE, FALSE,
5139 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5142 Ynut_eB, FALSE, TRUE,
5143 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
5146 Ynut_w, FALSE, FALSE,
5147 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5150 Ynut_wB, FALSE, TRUE,
5151 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
5154 Xbug_n, TRUE, FALSE,
5158 Xbug_e, TRUE, FALSE,
5159 EL_BUG_RIGHT, -1, -1
5162 Xbug_s, TRUE, FALSE,
5166 Xbug_w, TRUE, FALSE,
5170 Xbug_gon, FALSE, FALSE,
5174 Xbug_goe, FALSE, FALSE,
5175 EL_BUG_RIGHT, -1, -1
5178 Xbug_gos, FALSE, FALSE,
5182 Xbug_gow, FALSE, FALSE,
5186 Ybug_n, FALSE, FALSE,
5187 EL_BUG, ACTION_MOVING, MV_BIT_UP
5190 Ybug_nB, FALSE, TRUE,
5191 EL_BUG, ACTION_MOVING, MV_BIT_UP
5194 Ybug_e, FALSE, FALSE,
5195 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5198 Ybug_eB, FALSE, TRUE,
5199 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
5202 Ybug_s, FALSE, FALSE,
5203 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5206 Ybug_sB, FALSE, TRUE,
5207 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5210 Ybug_w, FALSE, FALSE,
5211 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5214 Ybug_wB, FALSE, TRUE,
5215 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5218 Ybug_w_n, FALSE, FALSE,
5219 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5222 Ybug_n_e, FALSE, FALSE,
5223 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5226 Ybug_e_s, FALSE, FALSE,
5227 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5230 Ybug_s_w, FALSE, FALSE,
5231 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5234 Ybug_e_n, FALSE, FALSE,
5235 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5238 Ybug_s_e, FALSE, FALSE,
5239 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5242 Ybug_w_s, FALSE, FALSE,
5243 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5246 Ybug_n_w, FALSE, FALSE,
5247 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5250 Ybug_stone, FALSE, FALSE,
5251 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5254 Ybug_spring, FALSE, FALSE,
5255 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5258 Xtank_n, TRUE, FALSE,
5259 EL_SPACESHIP_UP, -1, -1
5262 Xtank_e, TRUE, FALSE,
5263 EL_SPACESHIP_RIGHT, -1, -1
5266 Xtank_s, TRUE, FALSE,
5267 EL_SPACESHIP_DOWN, -1, -1
5270 Xtank_w, TRUE, FALSE,
5271 EL_SPACESHIP_LEFT, -1, -1
5274 Xtank_gon, FALSE, FALSE,
5275 EL_SPACESHIP_UP, -1, -1
5278 Xtank_goe, FALSE, FALSE,
5279 EL_SPACESHIP_RIGHT, -1, -1
5282 Xtank_gos, FALSE, FALSE,
5283 EL_SPACESHIP_DOWN, -1, -1
5286 Xtank_gow, FALSE, FALSE,
5287 EL_SPACESHIP_LEFT, -1, -1
5290 Ytank_n, FALSE, FALSE,
5291 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5294 Ytank_nB, FALSE, TRUE,
5295 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5298 Ytank_e, FALSE, FALSE,
5299 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5302 Ytank_eB, FALSE, TRUE,
5303 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5306 Ytank_s, FALSE, FALSE,
5307 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5310 Ytank_sB, FALSE, TRUE,
5311 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5314 Ytank_w, FALSE, FALSE,
5315 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5318 Ytank_wB, FALSE, TRUE,
5319 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5322 Ytank_w_n, FALSE, FALSE,
5323 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5326 Ytank_n_e, FALSE, FALSE,
5327 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5330 Ytank_e_s, FALSE, FALSE,
5331 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5334 Ytank_s_w, FALSE, FALSE,
5335 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5338 Ytank_e_n, FALSE, FALSE,
5339 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5342 Ytank_s_e, FALSE, FALSE,
5343 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5346 Ytank_w_s, FALSE, FALSE,
5347 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5350 Ytank_n_w, FALSE, FALSE,
5351 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5354 Ytank_stone, FALSE, FALSE,
5355 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5358 Ytank_spring, FALSE, FALSE,
5359 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5362 Xandroid, TRUE, FALSE,
5363 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5366 Xandroid_1_n, FALSE, FALSE,
5367 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5370 Xandroid_2_n, FALSE, FALSE,
5371 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5374 Xandroid_1_e, FALSE, FALSE,
5375 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5378 Xandroid_2_e, FALSE, FALSE,
5379 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5382 Xandroid_1_w, FALSE, FALSE,
5383 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5386 Xandroid_2_w, FALSE, FALSE,
5387 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5390 Xandroid_1_s, FALSE, FALSE,
5391 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5394 Xandroid_2_s, FALSE, FALSE,
5395 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5398 Yandroid_n, FALSE, FALSE,
5399 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5402 Yandroid_nB, FALSE, TRUE,
5403 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5406 Yandroid_ne, FALSE, FALSE,
5407 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5410 Yandroid_neB, FALSE, TRUE,
5411 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5414 Yandroid_e, FALSE, FALSE,
5415 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5418 Yandroid_eB, FALSE, TRUE,
5419 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5422 Yandroid_se, FALSE, FALSE,
5423 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5426 Yandroid_seB, FALSE, TRUE,
5427 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5430 Yandroid_s, FALSE, FALSE,
5431 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5434 Yandroid_sB, FALSE, TRUE,
5435 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5438 Yandroid_sw, FALSE, FALSE,
5439 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5442 Yandroid_swB, FALSE, TRUE,
5443 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5446 Yandroid_w, FALSE, FALSE,
5447 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5450 Yandroid_wB, FALSE, TRUE,
5451 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5454 Yandroid_nw, FALSE, FALSE,
5455 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5458 Yandroid_nwB, FALSE, TRUE,
5459 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5462 Xspring, TRUE, FALSE,
5466 Xspring_pause, FALSE, FALSE,
5470 Xspring_e, FALSE, FALSE,
5474 Xspring_w, FALSE, FALSE,
5478 Xspring_fall, FALSE, FALSE,
5482 Yspring_s, FALSE, FALSE,
5483 EL_SPRING, ACTION_FALLING, -1
5486 Yspring_sB, FALSE, TRUE,
5487 EL_SPRING, ACTION_FALLING, -1
5490 Yspring_e, FALSE, FALSE,
5491 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5494 Yspring_eB, FALSE, TRUE,
5495 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5498 Yspring_w, FALSE, FALSE,
5499 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5502 Yspring_wB, FALSE, TRUE,
5503 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5506 Yspring_kill_e, FALSE, FALSE,
5507 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5510 Yspring_kill_eB, FALSE, TRUE,
5511 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5514 Yspring_kill_w, FALSE, FALSE,
5515 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5518 Yspring_kill_wB, FALSE, TRUE,
5519 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5522 Xeater_n, TRUE, FALSE,
5523 EL_YAMYAM_UP, -1, -1
5526 Xeater_e, TRUE, FALSE,
5527 EL_YAMYAM_RIGHT, -1, -1
5530 Xeater_w, TRUE, FALSE,
5531 EL_YAMYAM_LEFT, -1, -1
5534 Xeater_s, TRUE, FALSE,
5535 EL_YAMYAM_DOWN, -1, -1
5538 Yeater_n, FALSE, FALSE,
5539 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5542 Yeater_nB, FALSE, TRUE,
5543 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5546 Yeater_e, FALSE, FALSE,
5547 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5550 Yeater_eB, FALSE, TRUE,
5551 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5554 Yeater_s, FALSE, FALSE,
5555 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5558 Yeater_sB, FALSE, TRUE,
5559 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5562 Yeater_w, FALSE, FALSE,
5563 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5566 Yeater_wB, FALSE, TRUE,
5567 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5570 Yeater_stone, FALSE, FALSE,
5571 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5574 Yeater_spring, FALSE, FALSE,
5575 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5578 Xalien, TRUE, FALSE,
5582 Xalien_pause, FALSE, FALSE,
5586 Yalien_n, FALSE, FALSE,
5587 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5590 Yalien_nB, FALSE, TRUE,
5591 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5594 Yalien_e, FALSE, FALSE,
5595 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5598 Yalien_eB, FALSE, TRUE,
5599 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5602 Yalien_s, FALSE, FALSE,
5603 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5606 Yalien_sB, FALSE, TRUE,
5607 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5610 Yalien_w, FALSE, FALSE,
5611 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5614 Yalien_wB, FALSE, TRUE,
5615 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5618 Yalien_stone, FALSE, FALSE,
5619 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5622 Yalien_spring, FALSE, FALSE,
5623 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5626 Xemerald, TRUE, FALSE,
5630 Xemerald_pause, FALSE, FALSE,
5634 Xemerald_fall, FALSE, FALSE,
5638 Xemerald_shine, FALSE, FALSE,
5639 EL_EMERALD, ACTION_TWINKLING, -1
5642 Yemerald_s, FALSE, FALSE,
5643 EL_EMERALD, ACTION_FALLING, -1
5646 Yemerald_sB, FALSE, TRUE,
5647 EL_EMERALD, ACTION_FALLING, -1
5650 Yemerald_e, FALSE, FALSE,
5651 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5654 Yemerald_eB, FALSE, TRUE,
5655 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5658 Yemerald_w, FALSE, FALSE,
5659 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5662 Yemerald_wB, FALSE, TRUE,
5663 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5666 Yemerald_eat, FALSE, FALSE,
5667 EL_EMERALD, ACTION_COLLECTING, -1
5670 Yemerald_stone, FALSE, FALSE,
5671 EL_NUT, ACTION_BREAKING, -1
5674 Xdiamond, TRUE, FALSE,
5678 Xdiamond_pause, FALSE, FALSE,
5682 Xdiamond_fall, FALSE, FALSE,
5686 Xdiamond_shine, FALSE, FALSE,
5687 EL_DIAMOND, ACTION_TWINKLING, -1
5690 Ydiamond_s, FALSE, FALSE,
5691 EL_DIAMOND, ACTION_FALLING, -1
5694 Ydiamond_sB, FALSE, TRUE,
5695 EL_DIAMOND, ACTION_FALLING, -1
5698 Ydiamond_e, FALSE, FALSE,
5699 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5702 Ydiamond_eB, FALSE, TRUE,
5703 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5706 Ydiamond_w, FALSE, FALSE,
5707 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5710 Ydiamond_wB, FALSE, TRUE,
5711 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5714 Ydiamond_eat, FALSE, FALSE,
5715 EL_DIAMOND, ACTION_COLLECTING, -1
5718 Ydiamond_stone, FALSE, FALSE,
5719 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5722 Xdrip_fall, TRUE, FALSE,
5723 EL_AMOEBA_DROP, -1, -1
5726 Xdrip_stretch, FALSE, FALSE,
5727 EL_AMOEBA_DROP, ACTION_FALLING, -1
5730 Xdrip_stretchB, FALSE, TRUE,
5731 EL_AMOEBA_DROP, ACTION_FALLING, -1
5734 Xdrip_eat, FALSE, FALSE,
5735 EL_AMOEBA_DROP, ACTION_GROWING, -1
5738 Ydrip_s1, FALSE, FALSE,
5739 EL_AMOEBA_DROP, ACTION_FALLING, -1
5742 Ydrip_s1B, FALSE, TRUE,
5743 EL_AMOEBA_DROP, ACTION_FALLING, -1
5746 Ydrip_s2, FALSE, FALSE,
5747 EL_AMOEBA_DROP, ACTION_FALLING, -1
5750 Ydrip_s2B, FALSE, TRUE,
5751 EL_AMOEBA_DROP, ACTION_FALLING, -1
5758 Xbomb_pause, FALSE, FALSE,
5762 Xbomb_fall, FALSE, FALSE,
5766 Ybomb_s, FALSE, FALSE,
5767 EL_BOMB, ACTION_FALLING, -1
5770 Ybomb_sB, FALSE, TRUE,
5771 EL_BOMB, ACTION_FALLING, -1
5774 Ybomb_e, FALSE, FALSE,
5775 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5778 Ybomb_eB, FALSE, TRUE,
5779 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5782 Ybomb_w, FALSE, FALSE,
5783 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5786 Ybomb_wB, FALSE, TRUE,
5787 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5790 Ybomb_eat, FALSE, FALSE,
5791 EL_BOMB, ACTION_ACTIVATING, -1
5794 Xballoon, TRUE, FALSE,
5798 Yballoon_n, FALSE, FALSE,
5799 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5802 Yballoon_nB, FALSE, TRUE,
5803 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5806 Yballoon_e, FALSE, FALSE,
5807 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5810 Yballoon_eB, FALSE, TRUE,
5811 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5814 Yballoon_s, FALSE, FALSE,
5815 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5818 Yballoon_sB, FALSE, TRUE,
5819 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5822 Yballoon_w, FALSE, FALSE,
5823 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5826 Yballoon_wB, FALSE, TRUE,
5827 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5830 Xgrass, TRUE, FALSE,
5831 EL_EMC_GRASS, -1, -1
5834 Ygrass_nB, FALSE, FALSE,
5835 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5838 Ygrass_eB, FALSE, FALSE,
5839 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5842 Ygrass_sB, FALSE, FALSE,
5843 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5846 Ygrass_wB, FALSE, FALSE,
5847 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5854 Ydirt_nB, FALSE, FALSE,
5855 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5858 Ydirt_eB, FALSE, FALSE,
5859 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5862 Ydirt_sB, FALSE, FALSE,
5863 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5866 Ydirt_wB, FALSE, FALSE,
5867 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5870 Xacid_ne, TRUE, FALSE,
5871 EL_ACID_POOL_TOPRIGHT, -1, -1
5874 Xacid_se, TRUE, FALSE,
5875 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5878 Xacid_s, TRUE, FALSE,
5879 EL_ACID_POOL_BOTTOM, -1, -1
5882 Xacid_sw, TRUE, FALSE,
5883 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5886 Xacid_nw, TRUE, FALSE,
5887 EL_ACID_POOL_TOPLEFT, -1, -1
5890 Xacid_1, TRUE, FALSE,
5894 Xacid_2, FALSE, FALSE,
5898 Xacid_3, FALSE, FALSE,
5902 Xacid_4, FALSE, FALSE,
5906 Xacid_5, FALSE, FALSE,
5910 Xacid_6, FALSE, FALSE,
5914 Xacid_7, FALSE, FALSE,
5918 Xacid_8, FALSE, FALSE,
5922 Xball_1, TRUE, FALSE,
5923 EL_EMC_MAGIC_BALL, -1, -1
5926 Xball_1B, FALSE, FALSE,
5927 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5930 Xball_2, FALSE, FALSE,
5931 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5934 Xball_2B, FALSE, FALSE,
5935 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5938 Yball_eat, FALSE, FALSE,
5939 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5942 Ykey_1_eat, FALSE, FALSE,
5943 EL_EM_KEY_1, ACTION_COLLECTING, -1
5946 Ykey_2_eat, FALSE, FALSE,
5947 EL_EM_KEY_2, ACTION_COLLECTING, -1
5950 Ykey_3_eat, FALSE, FALSE,
5951 EL_EM_KEY_3, ACTION_COLLECTING, -1
5954 Ykey_4_eat, FALSE, FALSE,
5955 EL_EM_KEY_4, ACTION_COLLECTING, -1
5958 Ykey_5_eat, FALSE, FALSE,
5959 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5962 Ykey_6_eat, FALSE, FALSE,
5963 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5966 Ykey_7_eat, FALSE, FALSE,
5967 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5970 Ykey_8_eat, FALSE, FALSE,
5971 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5974 Ylenses_eat, FALSE, FALSE,
5975 EL_EMC_LENSES, ACTION_COLLECTING, -1
5978 Ymagnify_eat, FALSE, FALSE,
5979 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5982 Ygrass_eat, FALSE, FALSE,
5983 EL_EMC_GRASS, ACTION_SNAPPING, -1
5986 Ydirt_eat, FALSE, FALSE,
5987 EL_SAND, ACTION_SNAPPING, -1
5990 Xgrow_ns, TRUE, FALSE,
5991 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5994 Ygrow_ns_eat, FALSE, FALSE,
5995 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5998 Xgrow_ew, TRUE, FALSE,
5999 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
6002 Ygrow_ew_eat, FALSE, FALSE,
6003 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
6006 Xwonderwall, TRUE, FALSE,
6007 EL_MAGIC_WALL, -1, -1
6010 XwonderwallB, FALSE, FALSE,
6011 EL_MAGIC_WALL, ACTION_ACTIVE, -1
6014 Xamoeba_1, TRUE, FALSE,
6015 EL_AMOEBA_DRY, ACTION_OTHER, -1
6018 Xamoeba_2, FALSE, FALSE,
6019 EL_AMOEBA_DRY, ACTION_OTHER, -1
6022 Xamoeba_3, FALSE, FALSE,
6023 EL_AMOEBA_DRY, ACTION_OTHER, -1
6026 Xamoeba_4, FALSE, FALSE,
6027 EL_AMOEBA_DRY, ACTION_OTHER, -1
6030 Xamoeba_5, TRUE, FALSE,
6031 EL_AMOEBA_WET, ACTION_OTHER, -1
6034 Xamoeba_6, FALSE, FALSE,
6035 EL_AMOEBA_WET, ACTION_OTHER, -1
6038 Xamoeba_7, FALSE, FALSE,
6039 EL_AMOEBA_WET, ACTION_OTHER, -1
6042 Xamoeba_8, FALSE, FALSE,
6043 EL_AMOEBA_WET, ACTION_OTHER, -1
6046 Xdoor_1, TRUE, FALSE,
6047 EL_EM_GATE_1, -1, -1
6050 Xdoor_2, TRUE, FALSE,
6051 EL_EM_GATE_2, -1, -1
6054 Xdoor_3, TRUE, FALSE,
6055 EL_EM_GATE_3, -1, -1
6058 Xdoor_4, TRUE, FALSE,
6059 EL_EM_GATE_4, -1, -1
6062 Xdoor_5, TRUE, FALSE,
6063 EL_EMC_GATE_5, -1, -1
6066 Xdoor_6, TRUE, FALSE,
6067 EL_EMC_GATE_6, -1, -1
6070 Xdoor_7, TRUE, FALSE,
6071 EL_EMC_GATE_7, -1, -1
6074 Xdoor_8, TRUE, FALSE,
6075 EL_EMC_GATE_8, -1, -1
6078 Xkey_1, TRUE, FALSE,
6082 Xkey_2, TRUE, FALSE,
6086 Xkey_3, TRUE, FALSE,
6090 Xkey_4, TRUE, FALSE,
6094 Xkey_5, TRUE, FALSE,
6095 EL_EMC_KEY_5, -1, -1
6098 Xkey_6, TRUE, FALSE,
6099 EL_EMC_KEY_6, -1, -1
6102 Xkey_7, TRUE, FALSE,
6103 EL_EMC_KEY_7, -1, -1
6106 Xkey_8, TRUE, FALSE,
6107 EL_EMC_KEY_8, -1, -1
6110 Xwind_n, TRUE, FALSE,
6111 EL_BALLOON_SWITCH_UP, -1, -1
6114 Xwind_e, TRUE, FALSE,
6115 EL_BALLOON_SWITCH_RIGHT, -1, -1
6118 Xwind_s, TRUE, FALSE,
6119 EL_BALLOON_SWITCH_DOWN, -1, -1
6122 Xwind_w, TRUE, FALSE,
6123 EL_BALLOON_SWITCH_LEFT, -1, -1
6126 Xwind_nesw, TRUE, FALSE,
6127 EL_BALLOON_SWITCH_ANY, -1, -1
6130 Xwind_stop, TRUE, FALSE,
6131 EL_BALLOON_SWITCH_NONE, -1, -1
6135 EL_EM_EXIT_CLOSED, -1, -1
6138 Xexit_1, TRUE, FALSE,
6139 EL_EM_EXIT_OPEN, -1, -1
6142 Xexit_2, FALSE, FALSE,
6143 EL_EM_EXIT_OPEN, -1, -1
6146 Xexit_3, FALSE, FALSE,
6147 EL_EM_EXIT_OPEN, -1, -1
6150 Xdynamite, TRUE, FALSE,
6151 EL_EM_DYNAMITE, -1, -1
6154 Ydynamite_eat, FALSE, FALSE,
6155 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
6158 Xdynamite_1, TRUE, FALSE,
6159 EL_EM_DYNAMITE_ACTIVE, -1, -1
6162 Xdynamite_2, FALSE, FALSE,
6163 EL_EM_DYNAMITE_ACTIVE, -1, -1
6166 Xdynamite_3, FALSE, FALSE,
6167 EL_EM_DYNAMITE_ACTIVE, -1, -1
6170 Xdynamite_4, FALSE, FALSE,
6171 EL_EM_DYNAMITE_ACTIVE, -1, -1
6174 Xbumper, TRUE, FALSE,
6175 EL_EMC_SPRING_BUMPER, -1, -1
6178 XbumperB, FALSE, FALSE,
6179 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
6182 Xwheel, TRUE, FALSE,
6183 EL_ROBOT_WHEEL, -1, -1
6186 XwheelB, FALSE, FALSE,
6187 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
6190 Xswitch, TRUE, FALSE,
6191 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
6194 XswitchB, FALSE, FALSE,
6195 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
6199 EL_QUICKSAND_EMPTY, -1, -1
6202 Xsand_stone, TRUE, FALSE,
6203 EL_QUICKSAND_FULL, -1, -1
6206 Xsand_stonein_1, FALSE, TRUE,
6207 EL_ROCK, ACTION_FILLING, -1
6210 Xsand_stonein_2, FALSE, TRUE,
6211 EL_ROCK, ACTION_FILLING, -1
6214 Xsand_stonein_3, FALSE, TRUE,
6215 EL_ROCK, ACTION_FILLING, -1
6218 Xsand_stonein_4, FALSE, TRUE,
6219 EL_ROCK, ACTION_FILLING, -1
6222 Xsand_stonesand_1, FALSE, FALSE,
6223 EL_QUICKSAND_EMPTYING, -1, -1
6226 Xsand_stonesand_2, FALSE, FALSE,
6227 EL_QUICKSAND_EMPTYING, -1, -1
6230 Xsand_stonesand_3, FALSE, FALSE,
6231 EL_QUICKSAND_EMPTYING, -1, -1
6234 Xsand_stonesand_4, FALSE, FALSE,
6235 EL_QUICKSAND_EMPTYING, -1, -1
6238 Xsand_stonesand_quickout_1, FALSE, FALSE,
6239 EL_QUICKSAND_EMPTYING, -1, -1
6242 Xsand_stonesand_quickout_2, FALSE, FALSE,
6243 EL_QUICKSAND_EMPTYING, -1, -1
6246 Xsand_stoneout_1, FALSE, FALSE,
6247 EL_ROCK, ACTION_EMPTYING, -1
6250 Xsand_stoneout_2, FALSE, FALSE,
6251 EL_ROCK, ACTION_EMPTYING, -1
6254 Xsand_sandstone_1, FALSE, FALSE,
6255 EL_QUICKSAND_FILLING, -1, -1
6258 Xsand_sandstone_2, FALSE, FALSE,
6259 EL_QUICKSAND_FILLING, -1, -1
6262 Xsand_sandstone_3, FALSE, FALSE,
6263 EL_QUICKSAND_FILLING, -1, -1
6266 Xsand_sandstone_4, FALSE, FALSE,
6267 EL_QUICKSAND_FILLING, -1, -1
6270 Xplant, TRUE, FALSE,
6271 EL_EMC_PLANT, -1, -1
6274 Yplant, FALSE, FALSE,
6275 EL_EMC_PLANT, -1, -1
6278 Xlenses, TRUE, FALSE,
6279 EL_EMC_LENSES, -1, -1
6282 Xmagnify, TRUE, FALSE,
6283 EL_EMC_MAGNIFIER, -1, -1
6286 Xdripper, TRUE, FALSE,
6287 EL_EMC_DRIPPER, -1, -1
6290 XdripperB, FALSE, FALSE,
6291 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6294 Xfake_blank, TRUE, FALSE,
6295 EL_INVISIBLE_WALL, -1, -1
6298 Xfake_blankB, FALSE, FALSE,
6299 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6302 Xfake_grass, TRUE, FALSE,
6303 EL_EMC_FAKE_GRASS, -1, -1
6306 Xfake_grassB, FALSE, FALSE,
6307 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6310 Xfake_door_1, TRUE, FALSE,
6311 EL_EM_GATE_1_GRAY, -1, -1
6314 Xfake_door_2, TRUE, FALSE,
6315 EL_EM_GATE_2_GRAY, -1, -1
6318 Xfake_door_3, TRUE, FALSE,
6319 EL_EM_GATE_3_GRAY, -1, -1
6322 Xfake_door_4, TRUE, FALSE,
6323 EL_EM_GATE_4_GRAY, -1, -1
6326 Xfake_door_5, TRUE, FALSE,
6327 EL_EMC_GATE_5_GRAY, -1, -1
6330 Xfake_door_6, TRUE, FALSE,
6331 EL_EMC_GATE_6_GRAY, -1, -1
6334 Xfake_door_7, TRUE, FALSE,
6335 EL_EMC_GATE_7_GRAY, -1, -1
6338 Xfake_door_8, TRUE, FALSE,
6339 EL_EMC_GATE_8_GRAY, -1, -1
6342 Xfake_acid_1, TRUE, FALSE,
6343 EL_EMC_FAKE_ACID, -1, -1
6346 Xfake_acid_2, FALSE, FALSE,
6347 EL_EMC_FAKE_ACID, -1, -1
6350 Xfake_acid_3, FALSE, FALSE,
6351 EL_EMC_FAKE_ACID, -1, -1
6354 Xfake_acid_4, FALSE, FALSE,
6355 EL_EMC_FAKE_ACID, -1, -1
6358 Xfake_acid_5, FALSE, FALSE,
6359 EL_EMC_FAKE_ACID, -1, -1
6362 Xfake_acid_6, FALSE, FALSE,
6363 EL_EMC_FAKE_ACID, -1, -1
6366 Xfake_acid_7, FALSE, FALSE,
6367 EL_EMC_FAKE_ACID, -1, -1
6370 Xfake_acid_8, FALSE, FALSE,
6371 EL_EMC_FAKE_ACID, -1, -1
6374 Xsteel_1, TRUE, FALSE,
6375 EL_STEELWALL, -1, -1
6378 Xsteel_2, TRUE, FALSE,
6379 EL_EMC_STEELWALL_2, -1, -1
6382 Xsteel_3, TRUE, FALSE,
6383 EL_EMC_STEELWALL_3, -1, -1
6386 Xsteel_4, TRUE, FALSE,
6387 EL_EMC_STEELWALL_4, -1, -1
6390 Xwall_1, TRUE, FALSE,
6394 Xwall_2, TRUE, FALSE,
6395 EL_EMC_WALL_14, -1, -1
6398 Xwall_3, TRUE, FALSE,
6399 EL_EMC_WALL_15, -1, -1
6402 Xwall_4, TRUE, FALSE,
6403 EL_EMC_WALL_16, -1, -1
6406 Xround_wall_1, TRUE, FALSE,
6407 EL_WALL_SLIPPERY, -1, -1
6410 Xround_wall_2, TRUE, FALSE,
6411 EL_EMC_WALL_SLIPPERY_2, -1, -1
6414 Xround_wall_3, TRUE, FALSE,
6415 EL_EMC_WALL_SLIPPERY_3, -1, -1
6418 Xround_wall_4, TRUE, FALSE,
6419 EL_EMC_WALL_SLIPPERY_4, -1, -1
6422 Xdecor_1, TRUE, FALSE,
6423 EL_EMC_WALL_8, -1, -1
6426 Xdecor_2, TRUE, FALSE,
6427 EL_EMC_WALL_6, -1, -1
6430 Xdecor_3, TRUE, FALSE,
6431 EL_EMC_WALL_4, -1, -1
6434 Xdecor_4, TRUE, FALSE,
6435 EL_EMC_WALL_7, -1, -1
6438 Xdecor_5, TRUE, FALSE,
6439 EL_EMC_WALL_5, -1, -1
6442 Xdecor_6, TRUE, FALSE,
6443 EL_EMC_WALL_9, -1, -1
6446 Xdecor_7, TRUE, FALSE,
6447 EL_EMC_WALL_10, -1, -1
6450 Xdecor_8, TRUE, FALSE,
6451 EL_EMC_WALL_1, -1, -1
6454 Xdecor_9, TRUE, FALSE,
6455 EL_EMC_WALL_2, -1, -1
6458 Xdecor_10, TRUE, FALSE,
6459 EL_EMC_WALL_3, -1, -1
6462 Xdecor_11, TRUE, FALSE,
6463 EL_EMC_WALL_11, -1, -1
6466 Xdecor_12, TRUE, FALSE,
6467 EL_EMC_WALL_12, -1, -1
6470 Xalpha_0, TRUE, FALSE,
6471 EL_CHAR('0'), -1, -1
6474 Xalpha_1, TRUE, FALSE,
6475 EL_CHAR('1'), -1, -1
6478 Xalpha_2, TRUE, FALSE,
6479 EL_CHAR('2'), -1, -1
6482 Xalpha_3, TRUE, FALSE,
6483 EL_CHAR('3'), -1, -1
6486 Xalpha_4, TRUE, FALSE,
6487 EL_CHAR('4'), -1, -1
6490 Xalpha_5, TRUE, FALSE,
6491 EL_CHAR('5'), -1, -1
6494 Xalpha_6, TRUE, FALSE,
6495 EL_CHAR('6'), -1, -1
6498 Xalpha_7, TRUE, FALSE,
6499 EL_CHAR('7'), -1, -1
6502 Xalpha_8, TRUE, FALSE,
6503 EL_CHAR('8'), -1, -1
6506 Xalpha_9, TRUE, FALSE,
6507 EL_CHAR('9'), -1, -1
6510 Xalpha_excla, TRUE, FALSE,
6511 EL_CHAR('!'), -1, -1
6514 Xalpha_quote, TRUE, FALSE,
6515 EL_CHAR('"'), -1, -1
6518 Xalpha_comma, TRUE, FALSE,
6519 EL_CHAR(','), -1, -1
6522 Xalpha_minus, TRUE, FALSE,
6523 EL_CHAR('-'), -1, -1
6526 Xalpha_perio, TRUE, FALSE,
6527 EL_CHAR('.'), -1, -1
6530 Xalpha_colon, TRUE, FALSE,
6531 EL_CHAR(':'), -1, -1
6534 Xalpha_quest, TRUE, FALSE,
6535 EL_CHAR('?'), -1, -1
6538 Xalpha_a, TRUE, FALSE,
6539 EL_CHAR('A'), -1, -1
6542 Xalpha_b, TRUE, FALSE,
6543 EL_CHAR('B'), -1, -1
6546 Xalpha_c, TRUE, FALSE,
6547 EL_CHAR('C'), -1, -1
6550 Xalpha_d, TRUE, FALSE,
6551 EL_CHAR('D'), -1, -1
6554 Xalpha_e, TRUE, FALSE,
6555 EL_CHAR('E'), -1, -1
6558 Xalpha_f, TRUE, FALSE,
6559 EL_CHAR('F'), -1, -1
6562 Xalpha_g, TRUE, FALSE,
6563 EL_CHAR('G'), -1, -1
6566 Xalpha_h, TRUE, FALSE,
6567 EL_CHAR('H'), -1, -1
6570 Xalpha_i, TRUE, FALSE,
6571 EL_CHAR('I'), -1, -1
6574 Xalpha_j, TRUE, FALSE,
6575 EL_CHAR('J'), -1, -1
6578 Xalpha_k, TRUE, FALSE,
6579 EL_CHAR('K'), -1, -1
6582 Xalpha_l, TRUE, FALSE,
6583 EL_CHAR('L'), -1, -1
6586 Xalpha_m, TRUE, FALSE,
6587 EL_CHAR('M'), -1, -1
6590 Xalpha_n, TRUE, FALSE,
6591 EL_CHAR('N'), -1, -1
6594 Xalpha_o, TRUE, FALSE,
6595 EL_CHAR('O'), -1, -1
6598 Xalpha_p, TRUE, FALSE,
6599 EL_CHAR('P'), -1, -1
6602 Xalpha_q, TRUE, FALSE,
6603 EL_CHAR('Q'), -1, -1
6606 Xalpha_r, TRUE, FALSE,
6607 EL_CHAR('R'), -1, -1
6610 Xalpha_s, TRUE, FALSE,
6611 EL_CHAR('S'), -1, -1
6614 Xalpha_t, TRUE, FALSE,
6615 EL_CHAR('T'), -1, -1
6618 Xalpha_u, TRUE, FALSE,
6619 EL_CHAR('U'), -1, -1
6622 Xalpha_v, TRUE, FALSE,
6623 EL_CHAR('V'), -1, -1
6626 Xalpha_w, TRUE, FALSE,
6627 EL_CHAR('W'), -1, -1
6630 Xalpha_x, TRUE, FALSE,
6631 EL_CHAR('X'), -1, -1
6634 Xalpha_y, TRUE, FALSE,
6635 EL_CHAR('Y'), -1, -1
6638 Xalpha_z, TRUE, FALSE,
6639 EL_CHAR('Z'), -1, -1
6642 Xalpha_arrow_e, TRUE, FALSE,
6643 EL_CHAR('>'), -1, -1
6646 Xalpha_arrow_w, TRUE, FALSE,
6647 EL_CHAR('<'), -1, -1
6650 Xalpha_copyr, TRUE, FALSE,
6651 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6655 Xboom_bug, FALSE, FALSE,
6656 EL_BUG, ACTION_EXPLODING, -1
6659 Xboom_bomb, FALSE, FALSE,
6660 EL_BOMB, ACTION_EXPLODING, -1
6663 Xboom_android, FALSE, FALSE,
6664 EL_EMC_ANDROID, ACTION_OTHER, -1
6667 Xboom_1, FALSE, FALSE,
6668 EL_DEFAULT, ACTION_EXPLODING, -1
6671 Xboom_2, FALSE, FALSE,
6672 EL_DEFAULT, ACTION_EXPLODING, -1
6675 Znormal, FALSE, FALSE,
6679 Zdynamite, FALSE, FALSE,
6683 Zplayer, FALSE, FALSE,
6687 ZBORDER, FALSE, FALSE,
6697 static struct Mapping_EM_to_RND_player
6706 em_player_mapping_list[] =
6710 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6714 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6718 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6722 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6726 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6730 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6734 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6738 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6742 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6746 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6750 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6754 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6758 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6762 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6766 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6770 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6774 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6778 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6782 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6786 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6790 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6794 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6798 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6802 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6806 EL_PLAYER_1, ACTION_DEFAULT, -1,
6810 EL_PLAYER_2, ACTION_DEFAULT, -1,
6814 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6818 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6822 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6826 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6830 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6834 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6838 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6842 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6846 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6850 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6854 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6858 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6862 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6866 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6870 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6874 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6878 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6882 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6886 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6890 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6894 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6898 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6902 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6906 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6910 EL_PLAYER_3, ACTION_DEFAULT, -1,
6914 EL_PLAYER_4, ACTION_DEFAULT, -1,
6923 int map_element_RND_to_EM(int element_rnd)
6925 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6926 static boolean mapping_initialized = FALSE;
6928 if (!mapping_initialized)
6932 /* return "Xalpha_quest" for all undefined elements in mapping array */
6933 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6934 mapping_RND_to_EM[i] = Xalpha_quest;
6936 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6937 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6938 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6939 em_object_mapping_list[i].element_em;
6941 mapping_initialized = TRUE;
6944 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6945 return mapping_RND_to_EM[element_rnd];
6947 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6952 int map_element_EM_to_RND(int element_em)
6954 static unsigned short mapping_EM_to_RND[TILE_MAX];
6955 static boolean mapping_initialized = FALSE;
6957 if (!mapping_initialized)
6961 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6962 for (i = 0; i < TILE_MAX; i++)
6963 mapping_EM_to_RND[i] = EL_UNKNOWN;
6965 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6966 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6967 em_object_mapping_list[i].element_rnd;
6969 mapping_initialized = TRUE;
6972 if (element_em >= 0 && element_em < TILE_MAX)
6973 return mapping_EM_to_RND[element_em];
6975 Error(ERR_WARN, "invalid EM level element %d", element_em);
6980 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6982 struct LevelInfo_EM *level_em = level->native_em_level;
6983 struct LEVEL *lev = level_em->lev;
6986 for (i = 0; i < TILE_MAX; i++)
6987 lev->android_array[i] = Xblank;
6989 for (i = 0; i < level->num_android_clone_elements; i++)
6991 int element_rnd = level->android_clone_element[i];
6992 int element_em = map_element_RND_to_EM(element_rnd);
6994 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6995 if (em_object_mapping_list[j].element_rnd == element_rnd)
6996 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
7000 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
7002 struct LevelInfo_EM *level_em = level->native_em_level;
7003 struct LEVEL *lev = level_em->lev;
7006 level->num_android_clone_elements = 0;
7008 for (i = 0; i < TILE_MAX; i++)
7010 int element_em = lev->android_array[i];
7012 boolean element_found = FALSE;
7014 if (element_em == Xblank)
7017 element_rnd = map_element_EM_to_RND(element_em);
7019 for (j = 0; j < level->num_android_clone_elements; j++)
7020 if (level->android_clone_element[j] == element_rnd)
7021 element_found = TRUE;
7025 level->android_clone_element[level->num_android_clone_elements++] =
7028 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
7033 if (level->num_android_clone_elements == 0)
7035 level->num_android_clone_elements = 1;
7036 level->android_clone_element[0] = EL_EMPTY;
7040 int map_direction_RND_to_EM(int direction)
7042 return (direction == MV_UP ? 0 :
7043 direction == MV_RIGHT ? 1 :
7044 direction == MV_DOWN ? 2 :
7045 direction == MV_LEFT ? 3 :
7049 int map_direction_EM_to_RND(int direction)
7051 return (direction == 0 ? MV_UP :
7052 direction == 1 ? MV_RIGHT :
7053 direction == 2 ? MV_DOWN :
7054 direction == 3 ? MV_LEFT :
7058 int map_element_RND_to_SP(int element_rnd)
7060 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
7062 if (element_rnd >= EL_SP_START &&
7063 element_rnd <= EL_SP_END)
7064 element_sp = element_rnd - EL_SP_START;
7065 else if (element_rnd == EL_EMPTY_SPACE)
7067 else if (element_rnd == EL_INVISIBLE_WALL)
7073 int map_element_SP_to_RND(int element_sp)
7075 int element_rnd = EL_UNKNOWN;
7077 if (element_sp >= 0x00 &&
7079 element_rnd = EL_SP_START + element_sp;
7080 else if (element_sp == 0x28)
7081 element_rnd = EL_INVISIBLE_WALL;
7086 int map_action_SP_to_RND(int action_sp)
7090 case actActive: return ACTION_ACTIVE;
7091 case actImpact: return ACTION_IMPACT;
7092 case actExploding: return ACTION_EXPLODING;
7093 case actDigging: return ACTION_DIGGING;
7094 case actSnapping: return ACTION_SNAPPING;
7095 case actCollecting: return ACTION_COLLECTING;
7096 case actPassing: return ACTION_PASSING;
7097 case actPushing: return ACTION_PUSHING;
7098 case actDropping: return ACTION_DROPPING;
7100 default: return ACTION_DEFAULT;
7104 int get_next_element(int element)
7108 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
7109 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
7110 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
7111 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
7112 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
7113 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
7114 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
7115 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
7116 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
7117 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
7118 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
7120 default: return element;
7124 int el_act_dir2img(int element, int action, int direction)
7126 element = GFX_ELEMENT(element);
7127 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7129 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7130 return element_info[element].direction_graphic[action][direction];
7133 static int el_act_dir2crm(int element, int action, int direction)
7135 element = GFX_ELEMENT(element);
7136 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
7138 /* direction_graphic[][] == graphic[] for undefined direction graphics */
7139 return element_info[element].direction_crumbled[action][direction];
7142 int el_act2img(int element, int action)
7144 element = GFX_ELEMENT(element);
7146 return element_info[element].graphic[action];
7149 int el_act2crm(int element, int action)
7151 element = GFX_ELEMENT(element);
7153 return element_info[element].crumbled[action];
7156 int el_dir2img(int element, int direction)
7158 element = GFX_ELEMENT(element);
7160 return el_act_dir2img(element, ACTION_DEFAULT, direction);
7163 int el2baseimg(int element)
7165 return element_info[element].graphic[ACTION_DEFAULT];
7168 int el2img(int element)
7170 element = GFX_ELEMENT(element);
7172 return element_info[element].graphic[ACTION_DEFAULT];
7175 int el2edimg(int element)
7177 element = GFX_ELEMENT(element);
7179 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
7182 int el2preimg(int element)
7184 element = GFX_ELEMENT(element);
7186 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
7189 int el2panelimg(int element)
7191 element = GFX_ELEMENT(element);
7193 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
7196 int font2baseimg(int font_nr)
7198 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
7201 int getBeltNrFromBeltElement(int element)
7203 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7204 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7205 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
7208 int getBeltNrFromBeltActiveElement(int element)
7210 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
7211 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
7212 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
7215 int getBeltNrFromBeltSwitchElement(int element)
7217 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
7218 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
7219 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
7222 int getBeltDirNrFromBeltElement(int element)
7224 static int belt_base_element[4] =
7226 EL_CONVEYOR_BELT_1_LEFT,
7227 EL_CONVEYOR_BELT_2_LEFT,
7228 EL_CONVEYOR_BELT_3_LEFT,
7229 EL_CONVEYOR_BELT_4_LEFT
7232 int belt_nr = getBeltNrFromBeltElement(element);
7233 int belt_dir_nr = element - belt_base_element[belt_nr];
7235 return (belt_dir_nr % 3);
7238 int getBeltDirNrFromBeltSwitchElement(int element)
7240 static int belt_base_element[4] =
7242 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7243 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7244 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7245 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7248 int belt_nr = getBeltNrFromBeltSwitchElement(element);
7249 int belt_dir_nr = element - belt_base_element[belt_nr];
7251 return (belt_dir_nr % 3);
7254 int getBeltDirFromBeltElement(int element)
7256 static int belt_move_dir[3] =
7263 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7265 return belt_move_dir[belt_dir_nr];
7268 int getBeltDirFromBeltSwitchElement(int element)
7270 static int belt_move_dir[3] =
7277 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7279 return belt_move_dir[belt_dir_nr];
7282 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7284 static int belt_base_element[4] =
7286 EL_CONVEYOR_BELT_1_LEFT,
7287 EL_CONVEYOR_BELT_2_LEFT,
7288 EL_CONVEYOR_BELT_3_LEFT,
7289 EL_CONVEYOR_BELT_4_LEFT
7292 return belt_base_element[belt_nr] + belt_dir_nr;
7295 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7297 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7299 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7302 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7304 static int belt_base_element[4] =
7306 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7307 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7308 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7309 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7312 return belt_base_element[belt_nr] + belt_dir_nr;
7315 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7317 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7319 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7322 boolean getTeamMode_EM()
7324 return game.team_mode;
7327 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7329 int game_frame_delay_value;
7331 game_frame_delay_value =
7332 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7333 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7336 if (tape.playing && tape.warp_forward && !tape.pausing)
7337 game_frame_delay_value = 0;
7339 return game_frame_delay_value;
7342 unsigned int InitRND(int seed)
7344 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7345 return InitEngineRandom_EM(seed);
7346 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7347 return InitEngineRandom_SP(seed);
7349 return InitEngineRandom_RND(seed);
7352 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7353 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7355 inline static int get_effective_element_EM(int tile, int frame_em)
7357 int element = object_mapping[tile].element_rnd;
7358 int action = object_mapping[tile].action;
7359 boolean is_backside = object_mapping[tile].is_backside;
7360 boolean action_removing = (action == ACTION_DIGGING ||
7361 action == ACTION_SNAPPING ||
7362 action == ACTION_COLLECTING);
7368 case Yacid_splash_eB:
7369 case Yacid_splash_wB:
7370 return (frame_em > 5 ? EL_EMPTY : element);
7376 else /* frame_em == 7 */
7380 case Yacid_splash_eB:
7381 case Yacid_splash_wB:
7384 case Yemerald_stone:
7387 case Ydiamond_stone:
7391 case Xdrip_stretchB:
7410 case Xsand_stonein_1:
7411 case Xsand_stonein_2:
7412 case Xsand_stonein_3:
7413 case Xsand_stonein_4:
7417 return (is_backside || action_removing ? EL_EMPTY : element);
7422 inline static boolean check_linear_animation_EM(int tile)
7426 case Xsand_stonesand_1:
7427 case Xsand_stonesand_quickout_1:
7428 case Xsand_sandstone_1:
7429 case Xsand_stonein_1:
7430 case Xsand_stoneout_1:
7449 case Yacid_splash_eB:
7450 case Yacid_splash_wB:
7451 case Yemerald_stone:
7458 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7459 boolean has_crumbled_graphics,
7460 int crumbled, int sync_frame)
7462 /* if element can be crumbled, but certain action graphics are just empty
7463 space (like instantly snapping sand to empty space in 1 frame), do not
7464 treat these empty space graphics as crumbled graphics in EMC engine */
7465 if (crumbled == IMG_EMPTY_SPACE)
7466 has_crumbled_graphics = FALSE;
7468 if (has_crumbled_graphics)
7470 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7471 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7472 g_crumbled->anim_delay,
7473 g_crumbled->anim_mode,
7474 g_crumbled->anim_start_frame,
7477 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7478 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7480 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7481 g_em->crumbled_tile_size = graphic_info[crumbled].tile_size;
7483 g_em->has_crumbled_graphics = TRUE;
7487 g_em->crumbled_bitmap = NULL;
7488 g_em->crumbled_src_x = 0;
7489 g_em->crumbled_src_y = 0;
7490 g_em->crumbled_border_size = 0;
7491 g_em->crumbled_tile_size = 0;
7493 g_em->has_crumbled_graphics = FALSE;
7497 void ResetGfxAnimation_EM(int x, int y, int tile)
7502 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7503 int tile, int frame_em, int x, int y)
7505 int action = object_mapping[tile].action;
7506 int direction = object_mapping[tile].direction;
7507 int effective_element = get_effective_element_EM(tile, frame_em);
7508 int graphic = (direction == MV_NONE ?
7509 el_act2img(effective_element, action) :
7510 el_act_dir2img(effective_element, action, direction));
7511 struct GraphicInfo *g = &graphic_info[graphic];
7513 boolean action_removing = (action == ACTION_DIGGING ||
7514 action == ACTION_SNAPPING ||
7515 action == ACTION_COLLECTING);
7516 boolean action_moving = (action == ACTION_FALLING ||
7517 action == ACTION_MOVING ||
7518 action == ACTION_PUSHING ||
7519 action == ACTION_EATING ||
7520 action == ACTION_FILLING ||
7521 action == ACTION_EMPTYING);
7522 boolean action_falling = (action == ACTION_FALLING ||
7523 action == ACTION_FILLING ||
7524 action == ACTION_EMPTYING);
7526 /* special case: graphic uses "2nd movement tile" and has defined
7527 7 frames for movement animation (or less) => use default graphic
7528 for last (8th) frame which ends the movement animation */
7529 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7531 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7532 graphic = (direction == MV_NONE ?
7533 el_act2img(effective_element, action) :
7534 el_act_dir2img(effective_element, action, direction));
7536 g = &graphic_info[graphic];
7539 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7543 else if (action_moving)
7545 boolean is_backside = object_mapping[tile].is_backside;
7549 int direction = object_mapping[tile].direction;
7550 int move_dir = (action_falling ? MV_DOWN : direction);
7555 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7556 if (g->double_movement && frame_em == 0)
7560 if (move_dir == MV_LEFT)
7561 GfxFrame[x - 1][y] = GfxFrame[x][y];
7562 else if (move_dir == MV_RIGHT)
7563 GfxFrame[x + 1][y] = GfxFrame[x][y];
7564 else if (move_dir == MV_UP)
7565 GfxFrame[x][y - 1] = GfxFrame[x][y];
7566 else if (move_dir == MV_DOWN)
7567 GfxFrame[x][y + 1] = GfxFrame[x][y];
7574 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7575 if (tile == Xsand_stonesand_quickout_1 ||
7576 tile == Xsand_stonesand_quickout_2)
7580 if (graphic_info[graphic].anim_global_sync)
7581 sync_frame = FrameCounter;
7582 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7583 sync_frame = GfxFrame[x][y];
7585 sync_frame = 0; /* playfield border (pseudo steel) */
7587 SetRandomAnimationValue(x, y);
7589 int frame = getAnimationFrame(g->anim_frames,
7592 g->anim_start_frame,
7595 g_em->unique_identifier =
7596 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7599 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7600 int tile, int frame_em, int x, int y)
7602 int action = object_mapping[tile].action;
7603 int direction = object_mapping[tile].direction;
7604 boolean is_backside = object_mapping[tile].is_backside;
7605 int effective_element = get_effective_element_EM(tile, frame_em);
7606 int effective_action = action;
7607 int graphic = (direction == MV_NONE ?
7608 el_act2img(effective_element, effective_action) :
7609 el_act_dir2img(effective_element, effective_action,
7611 int crumbled = (direction == MV_NONE ?
7612 el_act2crm(effective_element, effective_action) :
7613 el_act_dir2crm(effective_element, effective_action,
7615 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7616 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7617 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7618 struct GraphicInfo *g = &graphic_info[graphic];
7621 /* special case: graphic uses "2nd movement tile" and has defined
7622 7 frames for movement animation (or less) => use default graphic
7623 for last (8th) frame which ends the movement animation */
7624 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7626 effective_action = ACTION_DEFAULT;
7627 graphic = (direction == MV_NONE ?
7628 el_act2img(effective_element, effective_action) :
7629 el_act_dir2img(effective_element, effective_action,
7631 crumbled = (direction == MV_NONE ?
7632 el_act2crm(effective_element, effective_action) :
7633 el_act_dir2crm(effective_element, effective_action,
7636 g = &graphic_info[graphic];
7639 if (graphic_info[graphic].anim_global_sync)
7640 sync_frame = FrameCounter;
7641 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7642 sync_frame = GfxFrame[x][y];
7644 sync_frame = 0; /* playfield border (pseudo steel) */
7646 SetRandomAnimationValue(x, y);
7648 int frame = getAnimationFrame(g->anim_frames,
7651 g->anim_start_frame,
7654 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7655 g->double_movement && is_backside);
7657 /* (updating the "crumbled" graphic definitions is probably not really needed,
7658 as animations for crumbled graphics can't be longer than one EMC cycle) */
7659 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7663 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7664 int player_nr, int anim, int frame_em)
7666 int element = player_mapping[player_nr][anim].element_rnd;
7667 int action = player_mapping[player_nr][anim].action;
7668 int direction = player_mapping[player_nr][anim].direction;
7669 int graphic = (direction == MV_NONE ?
7670 el_act2img(element, action) :
7671 el_act_dir2img(element, action, direction));
7672 struct GraphicInfo *g = &graphic_info[graphic];
7675 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7677 stored_player[player_nr].StepFrame = frame_em;
7679 sync_frame = stored_player[player_nr].Frame;
7681 int frame = getAnimationFrame(g->anim_frames,
7684 g->anim_start_frame,
7687 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7688 &g_em->src_x, &g_em->src_y, FALSE);
7691 void InitGraphicInfo_EM(void)
7696 int num_em_gfx_errors = 0;
7698 if (graphic_info_em_object[0][0].bitmap == NULL)
7700 /* EM graphics not yet initialized in em_open_all() */
7705 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7708 /* always start with reliable default values */
7709 for (i = 0; i < TILE_MAX; i++)
7711 object_mapping[i].element_rnd = EL_UNKNOWN;
7712 object_mapping[i].is_backside = FALSE;
7713 object_mapping[i].action = ACTION_DEFAULT;
7714 object_mapping[i].direction = MV_NONE;
7717 /* always start with reliable default values */
7718 for (p = 0; p < MAX_PLAYERS; p++)
7720 for (i = 0; i < SPR_MAX; i++)
7722 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7723 player_mapping[p][i].action = ACTION_DEFAULT;
7724 player_mapping[p][i].direction = MV_NONE;
7728 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7730 int e = em_object_mapping_list[i].element_em;
7732 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7733 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7735 if (em_object_mapping_list[i].action != -1)
7736 object_mapping[e].action = em_object_mapping_list[i].action;
7738 if (em_object_mapping_list[i].direction != -1)
7739 object_mapping[e].direction =
7740 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7743 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7745 int a = em_player_mapping_list[i].action_em;
7746 int p = em_player_mapping_list[i].player_nr;
7748 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7750 if (em_player_mapping_list[i].action != -1)
7751 player_mapping[p][a].action = em_player_mapping_list[i].action;
7753 if (em_player_mapping_list[i].direction != -1)
7754 player_mapping[p][a].direction =
7755 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7758 for (i = 0; i < TILE_MAX; i++)
7760 int element = object_mapping[i].element_rnd;
7761 int action = object_mapping[i].action;
7762 int direction = object_mapping[i].direction;
7763 boolean is_backside = object_mapping[i].is_backside;
7764 boolean action_exploding = ((action == ACTION_EXPLODING ||
7765 action == ACTION_SMASHED_BY_ROCK ||
7766 action == ACTION_SMASHED_BY_SPRING) &&
7767 element != EL_DIAMOND);
7768 boolean action_active = (action == ACTION_ACTIVE);
7769 boolean action_other = (action == ACTION_OTHER);
7771 for (j = 0; j < 8; j++)
7773 int effective_element = get_effective_element_EM(i, j);
7774 int effective_action = (j < 7 ? action :
7775 i == Xdrip_stretch ? action :
7776 i == Xdrip_stretchB ? action :
7777 i == Ydrip_s1 ? action :
7778 i == Ydrip_s1B ? action :
7779 i == Xball_1B ? action :
7780 i == Xball_2 ? action :
7781 i == Xball_2B ? action :
7782 i == Yball_eat ? action :
7783 i == Ykey_1_eat ? action :
7784 i == Ykey_2_eat ? action :
7785 i == Ykey_3_eat ? action :
7786 i == Ykey_4_eat ? action :
7787 i == Ykey_5_eat ? action :
7788 i == Ykey_6_eat ? action :
7789 i == Ykey_7_eat ? action :
7790 i == Ykey_8_eat ? action :
7791 i == Ylenses_eat ? action :
7792 i == Ymagnify_eat ? action :
7793 i == Ygrass_eat ? action :
7794 i == Ydirt_eat ? action :
7795 i == Xsand_stonein_1 ? action :
7796 i == Xsand_stonein_2 ? action :
7797 i == Xsand_stonein_3 ? action :
7798 i == Xsand_stonein_4 ? action :
7799 i == Xsand_stoneout_1 ? action :
7800 i == Xsand_stoneout_2 ? action :
7801 i == Xboom_android ? ACTION_EXPLODING :
7802 action_exploding ? ACTION_EXPLODING :
7803 action_active ? action :
7804 action_other ? action :
7806 int graphic = (el_act_dir2img(effective_element, effective_action,
7808 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7810 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7811 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7812 boolean has_action_graphics = (graphic != base_graphic);
7813 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7814 struct GraphicInfo *g = &graphic_info[graphic];
7815 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7818 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7819 boolean special_animation = (action != ACTION_DEFAULT &&
7820 g->anim_frames == 3 &&
7821 g->anim_delay == 2 &&
7822 g->anim_mode & ANIM_LINEAR);
7823 int sync_frame = (i == Xdrip_stretch ? 7 :
7824 i == Xdrip_stretchB ? 7 :
7825 i == Ydrip_s2 ? j + 8 :
7826 i == Ydrip_s2B ? j + 8 :
7835 i == Xfake_acid_1 ? 0 :
7836 i == Xfake_acid_2 ? 10 :
7837 i == Xfake_acid_3 ? 20 :
7838 i == Xfake_acid_4 ? 30 :
7839 i == Xfake_acid_5 ? 40 :
7840 i == Xfake_acid_6 ? 50 :
7841 i == Xfake_acid_7 ? 60 :
7842 i == Xfake_acid_8 ? 70 :
7844 i == Xball_2B ? j + 8 :
7845 i == Yball_eat ? j + 1 :
7846 i == Ykey_1_eat ? j + 1 :
7847 i == Ykey_2_eat ? j + 1 :
7848 i == Ykey_3_eat ? j + 1 :
7849 i == Ykey_4_eat ? j + 1 :
7850 i == Ykey_5_eat ? j + 1 :
7851 i == Ykey_6_eat ? j + 1 :
7852 i == Ykey_7_eat ? j + 1 :
7853 i == Ykey_8_eat ? j + 1 :
7854 i == Ylenses_eat ? j + 1 :
7855 i == Ymagnify_eat ? j + 1 :
7856 i == Ygrass_eat ? j + 1 :
7857 i == Ydirt_eat ? j + 1 :
7858 i == Xamoeba_1 ? 0 :
7859 i == Xamoeba_2 ? 1 :
7860 i == Xamoeba_3 ? 2 :
7861 i == Xamoeba_4 ? 3 :
7862 i == Xamoeba_5 ? 0 :
7863 i == Xamoeba_6 ? 1 :
7864 i == Xamoeba_7 ? 2 :
7865 i == Xamoeba_8 ? 3 :
7866 i == Xexit_2 ? j + 8 :
7867 i == Xexit_3 ? j + 16 :
7868 i == Xdynamite_1 ? 0 :
7869 i == Xdynamite_2 ? 8 :
7870 i == Xdynamite_3 ? 16 :
7871 i == Xdynamite_4 ? 24 :
7872 i == Xsand_stonein_1 ? j + 1 :
7873 i == Xsand_stonein_2 ? j + 9 :
7874 i == Xsand_stonein_3 ? j + 17 :
7875 i == Xsand_stonein_4 ? j + 25 :
7876 i == Xsand_stoneout_1 && j == 0 ? 0 :
7877 i == Xsand_stoneout_1 && j == 1 ? 0 :
7878 i == Xsand_stoneout_1 && j == 2 ? 1 :
7879 i == Xsand_stoneout_1 && j == 3 ? 2 :
7880 i == Xsand_stoneout_1 && j == 4 ? 2 :
7881 i == Xsand_stoneout_1 && j == 5 ? 3 :
7882 i == Xsand_stoneout_1 && j == 6 ? 4 :
7883 i == Xsand_stoneout_1 && j == 7 ? 4 :
7884 i == Xsand_stoneout_2 && j == 0 ? 5 :
7885 i == Xsand_stoneout_2 && j == 1 ? 6 :
7886 i == Xsand_stoneout_2 && j == 2 ? 7 :
7887 i == Xsand_stoneout_2 && j == 3 ? 8 :
7888 i == Xsand_stoneout_2 && j == 4 ? 9 :
7889 i == Xsand_stoneout_2 && j == 5 ? 11 :
7890 i == Xsand_stoneout_2 && j == 6 ? 13 :
7891 i == Xsand_stoneout_2 && j == 7 ? 15 :
7892 i == Xboom_bug && j == 1 ? 2 :
7893 i == Xboom_bug && j == 2 ? 2 :
7894 i == Xboom_bug && j == 3 ? 4 :
7895 i == Xboom_bug && j == 4 ? 4 :
7896 i == Xboom_bug && j == 5 ? 2 :
7897 i == Xboom_bug && j == 6 ? 2 :
7898 i == Xboom_bug && j == 7 ? 0 :
7899 i == Xboom_bomb && j == 1 ? 2 :
7900 i == Xboom_bomb && j == 2 ? 2 :
7901 i == Xboom_bomb && j == 3 ? 4 :
7902 i == Xboom_bomb && j == 4 ? 4 :
7903 i == Xboom_bomb && j == 5 ? 2 :
7904 i == Xboom_bomb && j == 6 ? 2 :
7905 i == Xboom_bomb && j == 7 ? 0 :
7906 i == Xboom_android && j == 7 ? 6 :
7907 i == Xboom_1 && j == 1 ? 2 :
7908 i == Xboom_1 && j == 2 ? 2 :
7909 i == Xboom_1 && j == 3 ? 4 :
7910 i == Xboom_1 && j == 4 ? 4 :
7911 i == Xboom_1 && j == 5 ? 6 :
7912 i == Xboom_1 && j == 6 ? 6 :
7913 i == Xboom_1 && j == 7 ? 8 :
7914 i == Xboom_2 && j == 0 ? 8 :
7915 i == Xboom_2 && j == 1 ? 8 :
7916 i == Xboom_2 && j == 2 ? 10 :
7917 i == Xboom_2 && j == 3 ? 10 :
7918 i == Xboom_2 && j == 4 ? 10 :
7919 i == Xboom_2 && j == 5 ? 12 :
7920 i == Xboom_2 && j == 6 ? 12 :
7921 i == Xboom_2 && j == 7 ? 12 :
7922 special_animation && j == 4 ? 3 :
7923 effective_action != action ? 0 :
7927 Bitmap *debug_bitmap = g_em->bitmap;
7928 int debug_src_x = g_em->src_x;
7929 int debug_src_y = g_em->src_y;
7932 int frame = getAnimationFrame(g->anim_frames,
7935 g->anim_start_frame,
7938 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7939 g->double_movement && is_backside);
7941 g_em->bitmap = src_bitmap;
7942 g_em->src_x = src_x;
7943 g_em->src_y = src_y;
7944 g_em->src_offset_x = 0;
7945 g_em->src_offset_y = 0;
7946 g_em->dst_offset_x = 0;
7947 g_em->dst_offset_y = 0;
7948 g_em->width = TILEX;
7949 g_em->height = TILEY;
7951 g_em->preserve_background = FALSE;
7953 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7956 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7957 effective_action == ACTION_MOVING ||
7958 effective_action == ACTION_PUSHING ||
7959 effective_action == ACTION_EATING)) ||
7960 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7961 effective_action == ACTION_EMPTYING)))
7964 (effective_action == ACTION_FALLING ||
7965 effective_action == ACTION_FILLING ||
7966 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7967 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7968 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7969 int num_steps = (i == Ydrip_s1 ? 16 :
7970 i == Ydrip_s1B ? 16 :
7971 i == Ydrip_s2 ? 16 :
7972 i == Ydrip_s2B ? 16 :
7973 i == Xsand_stonein_1 ? 32 :
7974 i == Xsand_stonein_2 ? 32 :
7975 i == Xsand_stonein_3 ? 32 :
7976 i == Xsand_stonein_4 ? 32 :
7977 i == Xsand_stoneout_1 ? 16 :
7978 i == Xsand_stoneout_2 ? 16 : 8);
7979 int cx = ABS(dx) * (TILEX / num_steps);
7980 int cy = ABS(dy) * (TILEY / num_steps);
7981 int step_frame = (i == Ydrip_s2 ? j + 8 :
7982 i == Ydrip_s2B ? j + 8 :
7983 i == Xsand_stonein_2 ? j + 8 :
7984 i == Xsand_stonein_3 ? j + 16 :
7985 i == Xsand_stonein_4 ? j + 24 :
7986 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7987 int step = (is_backside ? step_frame : num_steps - step_frame);
7989 if (is_backside) /* tile where movement starts */
7991 if (dx < 0 || dy < 0)
7993 g_em->src_offset_x = cx * step;
7994 g_em->src_offset_y = cy * step;
7998 g_em->dst_offset_x = cx * step;
7999 g_em->dst_offset_y = cy * step;
8002 else /* tile where movement ends */
8004 if (dx < 0 || dy < 0)
8006 g_em->dst_offset_x = cx * step;
8007 g_em->dst_offset_y = cy * step;
8011 g_em->src_offset_x = cx * step;
8012 g_em->src_offset_y = cy * step;
8016 g_em->width = TILEX - cx * step;
8017 g_em->height = TILEY - cy * step;
8020 /* create unique graphic identifier to decide if tile must be redrawn */
8021 /* bit 31 - 16 (16 bit): EM style graphic
8022 bit 15 - 12 ( 4 bit): EM style frame
8023 bit 11 - 6 ( 6 bit): graphic width
8024 bit 5 - 0 ( 6 bit): graphic height */
8025 g_em->unique_identifier =
8026 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
8030 /* skip check for EMC elements not contained in original EMC artwork */
8031 if (element == EL_EMC_FAKE_ACID)
8034 if (g_em->bitmap != debug_bitmap ||
8035 g_em->src_x != debug_src_x ||
8036 g_em->src_y != debug_src_y ||
8037 g_em->src_offset_x != 0 ||
8038 g_em->src_offset_y != 0 ||
8039 g_em->dst_offset_x != 0 ||
8040 g_em->dst_offset_y != 0 ||
8041 g_em->width != TILEX ||
8042 g_em->height != TILEY)
8044 static int last_i = -1;
8052 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
8053 i, element, element_info[element].token_name,
8054 element_action_info[effective_action].suffix, direction);
8056 if (element != effective_element)
8057 printf(" [%d ('%s')]",
8059 element_info[effective_element].token_name);
8063 if (g_em->bitmap != debug_bitmap)
8064 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
8065 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
8067 if (g_em->src_x != debug_src_x ||
8068 g_em->src_y != debug_src_y)
8069 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8070 j, (is_backside ? 'B' : 'F'),
8071 g_em->src_x, g_em->src_y,
8072 g_em->src_x / 32, g_em->src_y / 32,
8073 debug_src_x, debug_src_y,
8074 debug_src_x / 32, debug_src_y / 32);
8076 if (g_em->src_offset_x != 0 ||
8077 g_em->src_offset_y != 0 ||
8078 g_em->dst_offset_x != 0 ||
8079 g_em->dst_offset_y != 0)
8080 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
8082 g_em->src_offset_x, g_em->src_offset_y,
8083 g_em->dst_offset_x, g_em->dst_offset_y);
8085 if (g_em->width != TILEX ||
8086 g_em->height != TILEY)
8087 printf(" %d (%d): size %d,%d should be %d,%d\n",
8089 g_em->width, g_em->height, TILEX, TILEY);
8091 num_em_gfx_errors++;
8098 for (i = 0; i < TILE_MAX; i++)
8100 for (j = 0; j < 8; j++)
8102 int element = object_mapping[i].element_rnd;
8103 int action = object_mapping[i].action;
8104 int direction = object_mapping[i].direction;
8105 boolean is_backside = object_mapping[i].is_backside;
8106 int graphic_action = el_act_dir2img(element, action, direction);
8107 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
8109 if ((action == ACTION_SMASHED_BY_ROCK ||
8110 action == ACTION_SMASHED_BY_SPRING ||
8111 action == ACTION_EATING) &&
8112 graphic_action == graphic_default)
8114 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
8115 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
8116 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
8117 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
8120 /* no separate animation for "smashed by rock" -- use rock instead */
8121 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
8122 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
8124 g_em->bitmap = g_xx->bitmap;
8125 g_em->src_x = g_xx->src_x;
8126 g_em->src_y = g_xx->src_y;
8127 g_em->src_offset_x = g_xx->src_offset_x;
8128 g_em->src_offset_y = g_xx->src_offset_y;
8129 g_em->dst_offset_x = g_xx->dst_offset_x;
8130 g_em->dst_offset_y = g_xx->dst_offset_y;
8131 g_em->width = g_xx->width;
8132 g_em->height = g_xx->height;
8133 g_em->unique_identifier = g_xx->unique_identifier;
8136 g_em->preserve_background = TRUE;
8141 for (p = 0; p < MAX_PLAYERS; p++)
8143 for (i = 0; i < SPR_MAX; i++)
8145 int element = player_mapping[p][i].element_rnd;
8146 int action = player_mapping[p][i].action;
8147 int direction = player_mapping[p][i].direction;
8149 for (j = 0; j < 8; j++)
8151 int effective_element = element;
8152 int effective_action = action;
8153 int graphic = (direction == MV_NONE ?
8154 el_act2img(effective_element, effective_action) :
8155 el_act_dir2img(effective_element, effective_action,
8157 struct GraphicInfo *g = &graphic_info[graphic];
8158 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
8164 Bitmap *debug_bitmap = g_em->bitmap;
8165 int debug_src_x = g_em->src_x;
8166 int debug_src_y = g_em->src_y;
8169 int frame = getAnimationFrame(g->anim_frames,
8172 g->anim_start_frame,
8175 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
8177 g_em->bitmap = src_bitmap;
8178 g_em->src_x = src_x;
8179 g_em->src_y = src_y;
8180 g_em->src_offset_x = 0;
8181 g_em->src_offset_y = 0;
8182 g_em->dst_offset_x = 0;
8183 g_em->dst_offset_y = 0;
8184 g_em->width = TILEX;
8185 g_em->height = TILEY;
8189 /* skip check for EMC elements not contained in original EMC artwork */
8190 if (element == EL_PLAYER_3 ||
8191 element == EL_PLAYER_4)
8194 if (g_em->bitmap != debug_bitmap ||
8195 g_em->src_x != debug_src_x ||
8196 g_em->src_y != debug_src_y)
8198 static int last_i = -1;
8206 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
8207 p, i, element, element_info[element].token_name,
8208 element_action_info[effective_action].suffix, direction);
8210 if (element != effective_element)
8211 printf(" [%d ('%s')]",
8213 element_info[effective_element].token_name);
8217 if (g_em->bitmap != debug_bitmap)
8218 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
8219 j, (int)(g_em->bitmap), (int)(debug_bitmap));
8221 if (g_em->src_x != debug_src_x ||
8222 g_em->src_y != debug_src_y)
8223 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8225 g_em->src_x, g_em->src_y,
8226 g_em->src_x / 32, g_em->src_y / 32,
8227 debug_src_x, debug_src_y,
8228 debug_src_x / 32, debug_src_y / 32);
8230 num_em_gfx_errors++;
8240 printf("::: [%d errors found]\n", num_em_gfx_errors);
8246 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
8247 boolean any_player_moving,
8248 boolean any_player_snapping,
8249 boolean any_player_dropping)
8251 if (frame == 0 && !any_player_dropping)
8253 if (!local_player->was_waiting)
8255 if (!CheckSaveEngineSnapshotToList())
8258 local_player->was_waiting = TRUE;
8261 else if (any_player_moving || any_player_snapping || any_player_dropping)
8263 local_player->was_waiting = FALSE;
8267 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8268 boolean murphy_is_dropping)
8270 if (murphy_is_waiting)
8272 if (!local_player->was_waiting)
8274 if (!CheckSaveEngineSnapshotToList())
8277 local_player->was_waiting = TRUE;
8282 local_player->was_waiting = FALSE;
8286 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8287 boolean any_player_moving,
8288 boolean any_player_snapping,
8289 boolean any_player_dropping)
8291 if (tape.single_step && tape.recording && !tape.pausing)
8292 if (frame == 0 && !any_player_dropping)
8293 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8295 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8296 any_player_snapping, any_player_dropping);
8299 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8300 boolean murphy_is_dropping)
8302 if (tape.single_step && tape.recording && !tape.pausing)
8303 if (murphy_is_waiting)
8304 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8306 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8309 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8310 int graphic, int sync_frame, int x, int y)
8312 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8314 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8317 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8319 return (IS_NEXT_FRAME(sync_frame, graphic));
8322 int getGraphicInfo_Delay(int graphic)
8324 return graphic_info[graphic].anim_delay;
8327 void PlayMenuSoundExt(int sound)
8329 if (sound == SND_UNDEFINED)
8332 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8333 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8336 if (IS_LOOP_SOUND(sound))
8337 PlaySoundLoop(sound);
8342 void PlayMenuSound()
8344 PlayMenuSoundExt(menu.sound[game_status]);
8347 void PlayMenuSoundStereo(int sound, int stereo_position)
8349 if (sound == SND_UNDEFINED)
8352 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8353 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8356 if (IS_LOOP_SOUND(sound))
8357 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8359 PlaySoundStereo(sound, stereo_position);
8362 void PlayMenuSoundIfLoopExt(int sound)
8364 if (sound == SND_UNDEFINED)
8367 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8368 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8371 if (IS_LOOP_SOUND(sound))
8372 PlaySoundLoop(sound);
8375 void PlayMenuSoundIfLoop()
8377 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8380 void PlayMenuMusicExt(int music)
8382 if (music == MUS_UNDEFINED)
8385 if (!setup.sound_music)
8391 void PlayMenuMusic()
8393 PlayMenuMusicExt(menu.music[game_status]);
8396 void PlaySoundActivating()
8399 PlaySound(SND_MENU_ITEM_ACTIVATING);
8403 void PlaySoundSelecting()
8406 PlaySound(SND_MENU_ITEM_SELECTING);
8410 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8412 boolean change_fullscreen = (setup.fullscreen !=
8413 video.fullscreen_enabled);
8414 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8415 setup.window_scaling_percent !=
8416 video.window_scaling_percent);
8418 if (change_window_scaling_percent && video.fullscreen_enabled)
8421 if (!change_window_scaling_percent && !video.fullscreen_available)
8424 #if defined(TARGET_SDL2)
8425 if (change_window_scaling_percent)
8427 SDLSetWindowScaling(setup.window_scaling_percent);
8431 else if (change_fullscreen)
8433 SDLSetWindowFullscreen(setup.fullscreen);
8435 /* set setup value according to successfully changed fullscreen mode */
8436 setup.fullscreen = video.fullscreen_enabled;
8442 if (change_fullscreen ||
8443 change_window_scaling_percent)
8445 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8447 /* save backbuffer content which gets lost when toggling fullscreen mode */
8448 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8450 if (change_window_scaling_percent)
8452 /* keep window mode, but change window scaling */
8453 video.fullscreen_enabled = TRUE; /* force new window scaling */
8456 /* toggle fullscreen */
8457 ChangeVideoModeIfNeeded(setup.fullscreen);
8459 /* set setup value according to successfully changed fullscreen mode */
8460 setup.fullscreen = video.fullscreen_enabled;
8462 /* restore backbuffer content from temporary backbuffer backup bitmap */
8463 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8465 FreeBitmap(tmp_backbuffer);
8467 /* update visible window/screen */
8468 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8472 void JoinRectangles(int *x, int *y, int *width, int *height,
8473 int x2, int y2, int width2, int height2)
8475 // do not join with "off-screen" rectangle
8476 if (x2 == -1 || y2 == -1)
8481 *width = MAX(*width, width2);
8482 *height = MAX(*height, height2);
8485 void SetAnimStatus(int anim_status_new)
8487 if (anim_status_new == GAME_MODE_MAIN)
8488 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
8490 global.anim_status_next = anim_status_new;
8492 // directly set screen modes that are entered without fading
8493 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
8494 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
8495 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
8496 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
8497 global.anim_status = global.anim_status_next;
8500 void SetGameStatus(int game_status_new)
8502 game_status = game_status_new;
8504 SetAnimStatus(game_status_new);
8507 void SetFontStatus(int game_status_new)
8509 static int last_game_status = -1;
8511 if (game_status_new != -1)
8513 // set game status for font use after storing last game status
8514 last_game_status = game_status;
8515 game_status = game_status_new;
8519 // reset game status after font use from last stored game status
8520 game_status = last_game_status;
8524 void ResetFontStatus()
8529 void ChangeViewportPropertiesIfNeeded()
8531 int gfx_game_mode = game_status;
8532 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8534 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
8535 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8536 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8537 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8538 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8539 int new_win_xsize = vp_window->width;
8540 int new_win_ysize = vp_window->height;
8541 int border_size = vp_playfield->border_size;
8542 int new_sx = vp_playfield->x + border_size;
8543 int new_sy = vp_playfield->y + border_size;
8544 int new_sxsize = vp_playfield->width - 2 * border_size;
8545 int new_sysize = vp_playfield->height - 2 * border_size;
8546 int new_real_sx = vp_playfield->x;
8547 int new_real_sy = vp_playfield->y;
8548 int new_full_sxsize = vp_playfield->width;
8549 int new_full_sysize = vp_playfield->height;
8550 int new_dx = vp_door_1->x;
8551 int new_dy = vp_door_1->y;
8552 int new_dxsize = vp_door_1->width;
8553 int new_dysize = vp_door_1->height;
8554 int new_vx = vp_door_2->x;
8555 int new_vy = vp_door_2->y;
8556 int new_vxsize = vp_door_2->width;
8557 int new_vysize = vp_door_2->height;
8558 int new_ex = vp_door_3->x;
8559 int new_ey = vp_door_3->y;
8560 int new_exsize = vp_door_3->width;
8561 int new_eysize = vp_door_3->height;
8562 int new_tilesize_var =
8563 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8565 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8566 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8567 int new_scr_fieldx = new_sxsize / tilesize;
8568 int new_scr_fieldy = new_sysize / tilesize;
8569 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8570 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8571 boolean init_gfx_buffers = FALSE;
8572 boolean init_video_buffer = FALSE;
8573 boolean init_gadgets_and_anims = FALSE;
8574 boolean init_em_graphics = FALSE;
8576 if (new_win_xsize != WIN_XSIZE ||
8577 new_win_ysize != WIN_YSIZE)
8579 WIN_XSIZE = new_win_xsize;
8580 WIN_YSIZE = new_win_ysize;
8582 init_video_buffer = TRUE;
8583 init_gfx_buffers = TRUE;
8584 init_gadgets_and_anims = TRUE;
8586 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8589 if (new_scr_fieldx != SCR_FIELDX ||
8590 new_scr_fieldy != SCR_FIELDY)
8592 /* this always toggles between MAIN and GAME when using small tile size */
8594 SCR_FIELDX = new_scr_fieldx;
8595 SCR_FIELDY = new_scr_fieldy;
8597 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8608 new_sxsize != SXSIZE ||
8609 new_sysize != SYSIZE ||
8610 new_dxsize != DXSIZE ||
8611 new_dysize != DYSIZE ||
8612 new_vxsize != VXSIZE ||
8613 new_vysize != VYSIZE ||
8614 new_exsize != EXSIZE ||
8615 new_eysize != EYSIZE ||
8616 new_real_sx != REAL_SX ||
8617 new_real_sy != REAL_SY ||
8618 new_full_sxsize != FULL_SXSIZE ||
8619 new_full_sysize != FULL_SYSIZE ||
8620 new_tilesize_var != TILESIZE_VAR
8623 // ------------------------------------------------------------------------
8624 // determine next fading area for changed viewport definitions
8625 // ------------------------------------------------------------------------
8627 // start with current playfield area (default fading area)
8630 FADE_SXSIZE = FULL_SXSIZE;
8631 FADE_SYSIZE = FULL_SYSIZE;
8633 // add new playfield area if position or size has changed
8634 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8635 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8637 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8638 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8641 // add current and new door 1 area if position or size has changed
8642 if (new_dx != DX || new_dy != DY ||
8643 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8645 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8646 DX, DY, DXSIZE, DYSIZE);
8647 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8648 new_dx, new_dy, new_dxsize, new_dysize);
8651 // add current and new door 2 area if position or size has changed
8652 if (new_dx != VX || new_dy != VY ||
8653 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8655 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8656 VX, VY, VXSIZE, VYSIZE);
8657 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8658 new_vx, new_vy, new_vxsize, new_vysize);
8661 // ------------------------------------------------------------------------
8662 // handle changed tile size
8663 // ------------------------------------------------------------------------
8665 if (new_tilesize_var != TILESIZE_VAR)
8667 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8669 // changing tile size invalidates scroll values of engine snapshots
8670 FreeEngineSnapshotSingle();
8672 // changing tile size requires update of graphic mapping for EM engine
8673 init_em_graphics = TRUE;
8684 SXSIZE = new_sxsize;
8685 SYSIZE = new_sysize;
8686 DXSIZE = new_dxsize;
8687 DYSIZE = new_dysize;
8688 VXSIZE = new_vxsize;
8689 VYSIZE = new_vysize;
8690 EXSIZE = new_exsize;
8691 EYSIZE = new_eysize;
8692 REAL_SX = new_real_sx;
8693 REAL_SY = new_real_sy;
8694 FULL_SXSIZE = new_full_sxsize;
8695 FULL_SYSIZE = new_full_sysize;
8696 TILESIZE_VAR = new_tilesize_var;
8698 init_gfx_buffers = TRUE;
8699 init_gadgets_and_anims = TRUE;
8701 // printf("::: viewports: init_gfx_buffers\n");
8702 // printf("::: viewports: init_gadgets_and_anims\n");
8705 if (init_gfx_buffers)
8707 // printf("::: init_gfx_buffers\n");
8709 SCR_FIELDX = new_scr_fieldx_buffers;
8710 SCR_FIELDY = new_scr_fieldy_buffers;
8714 SCR_FIELDX = new_scr_fieldx;
8715 SCR_FIELDY = new_scr_fieldy;
8717 SetDrawDeactivationMask(REDRAW_NONE);
8718 SetDrawBackgroundMask(REDRAW_FIELD);
8721 if (init_video_buffer)
8723 // printf("::: init_video_buffer\n");
8725 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8726 InitImageTextures();
8729 if (init_gadgets_and_anims)
8731 // printf("::: init_gadgets_and_anims\n");
8734 InitGlobalAnimations();
8737 if (init_em_graphics)
8739 InitGraphicInfo_EM();