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 void DumpTile(int x, int y)
200 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
206 printf_line("-", 79);
207 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
208 printf_line("-", 79);
210 if (!IN_LEV_FIELD(x, y))
212 printf("(not in level field)\n");
218 printf(" Feld: %d\t['%s']\n", Feld[x][y],
219 element_info[Feld[x][y]].token_name);
220 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
221 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
222 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
223 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
224 printf(" MovPos: %d\n", MovPos[x][y]);
225 printf(" MovDir: %d\n", MovDir[x][y]);
226 printf(" MovDelay: %d\n", MovDelay[x][y]);
227 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
228 printf(" CustomValue: %d\n", CustomValue[x][y]);
229 printf(" GfxElement: %d\n", GfxElement[x][y]);
230 printf(" GfxAction: %d\n", GfxAction[x][y]);
231 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
235 void SetDrawtoField(int mode)
237 if (mode == DRAW_TO_FIELDBUFFER)
243 BX2 = SCR_FIELDX + 1;
244 BY2 = SCR_FIELDY + 1;
246 drawto_field = fieldbuffer;
248 else /* DRAW_TO_BACKBUFFER */
254 BX2 = SCR_FIELDX - 1;
255 BY2 = SCR_FIELDY - 1;
257 drawto_field = backbuffer;
261 static void RedrawPlayfield_RND()
263 if (game.envelope_active)
266 DrawLevel(REDRAW_ALL);
270 void RedrawPlayfield()
272 if (game_status != GAME_MODE_PLAYING)
275 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
276 RedrawPlayfield_EM(TRUE);
277 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
278 RedrawPlayfield_SP(TRUE);
279 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
280 RedrawPlayfield_RND();
282 BlitScreenToBitmap(backbuffer);
284 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
288 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
291 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
292 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
294 if (x == -1 && y == -1)
297 if (draw_target == DRAW_TO_SCREEN)
298 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
300 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
303 static void DrawMaskedBorderExt_FIELD(int draw_target)
305 if (global.border_status >= GAME_MODE_MAIN &&
306 global.border_status <= GAME_MODE_PLAYING &&
307 border.draw_masked[global.border_status])
308 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
312 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
314 // when drawing to backbuffer, never draw border over open doors
315 if (draw_target == DRAW_TO_BACKBUFFER &&
316 (GetDoorState() & DOOR_OPEN_1))
319 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
320 (global.border_status != GAME_MODE_EDITOR ||
321 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
322 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
325 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
327 // when drawing to backbuffer, never draw border over open doors
328 if (draw_target == DRAW_TO_BACKBUFFER &&
329 (GetDoorState() & DOOR_OPEN_2))
332 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
333 global.border_status != GAME_MODE_EDITOR)
334 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
337 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
339 /* currently not available */
342 static void DrawMaskedBorderExt_ALL(int draw_target)
344 DrawMaskedBorderExt_FIELD(draw_target);
345 DrawMaskedBorderExt_DOOR_1(draw_target);
346 DrawMaskedBorderExt_DOOR_2(draw_target);
347 DrawMaskedBorderExt_DOOR_3(draw_target);
350 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
352 /* never draw masked screen borders on borderless screens */
353 if (global.border_status == GAME_MODE_LOADING ||
354 global.border_status == GAME_MODE_TITLE)
357 if (redraw_mask & REDRAW_ALL)
358 DrawMaskedBorderExt_ALL(draw_target);
361 if (redraw_mask & REDRAW_FIELD)
362 DrawMaskedBorderExt_FIELD(draw_target);
363 if (redraw_mask & REDRAW_DOOR_1)
364 DrawMaskedBorderExt_DOOR_1(draw_target);
365 if (redraw_mask & REDRAW_DOOR_2)
366 DrawMaskedBorderExt_DOOR_2(draw_target);
367 if (redraw_mask & REDRAW_DOOR_3)
368 DrawMaskedBorderExt_DOOR_3(draw_target);
372 void DrawMaskedBorder_FIELD()
374 DrawMaskedBorderExt_FIELD(DRAW_TO_BACKBUFFER);
377 void DrawMaskedBorder(int redraw_mask)
379 DrawMaskedBorderExt(redraw_mask, DRAW_TO_BACKBUFFER);
382 void DrawMaskedBorderToTarget(int draw_target)
384 if (draw_target == DRAW_TO_BACKBUFFER ||
385 draw_target == DRAW_TO_SCREEN)
387 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
391 int last_border_status = global.border_status;
393 if (draw_target == DRAW_TO_FADE_SOURCE)
395 global.border_status = gfx.fade_border_source_status;
396 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_source;
398 else if (draw_target == DRAW_TO_FADE_TARGET)
400 global.border_status = gfx.fade_border_target_status;
401 gfx.masked_border_bitmap_ptr = gfx.fade_bitmap_target;
404 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
406 global.border_status = last_border_status;
407 gfx.masked_border_bitmap_ptr = backbuffer;
411 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
413 int fx = FX, fy = FY;
414 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
415 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
417 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
418 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
419 int dx_var = dx * TILESIZE_VAR / TILESIZE;
420 int dy_var = dy * TILESIZE_VAR / TILESIZE;
423 ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
424 ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
426 if (EVEN(SCR_FIELDX))
428 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
429 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
431 fx += (dx_var > 0 ? TILEX_VAR : 0);
438 if (EVEN(SCR_FIELDY))
440 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
441 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
443 fy += (dy_var > 0 ? TILEY_VAR : 0);
450 if (full_lev_fieldx <= SCR_FIELDX)
452 if (EVEN(SCR_FIELDX))
453 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
455 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
458 if (full_lev_fieldy <= SCR_FIELDY)
460 if (EVEN(SCR_FIELDY))
461 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
463 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
466 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
469 void BlitScreenToBitmap(Bitmap *target_bitmap)
471 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
472 BlitScreenToBitmap_EM(target_bitmap);
473 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
474 BlitScreenToBitmap_SP(target_bitmap);
475 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
476 BlitScreenToBitmap_RND(target_bitmap);
478 redraw_mask |= REDRAW_FIELD;
481 void DrawFramesPerSecond()
484 int font_nr = FONT_TEXT_2;
485 int font_width = getFontWidth(font_nr);
487 sprintf(text, "%04.1f fps", global.frames_per_second);
489 DrawTextExt(backbuffer, WIN_XSIZE - font_width * strlen(text), 0, text,
490 font_nr, BLIT_OPAQUE);
494 static void PrintFrameTimeDebugging()
496 static unsigned int last_counter = 0;
497 unsigned int counter = Counter();
498 int diff_1 = counter - last_counter;
499 int diff_2 = diff_1 - GAME_FRAME_DELAY;
501 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
502 char diff_bar[2 * diff_2_max + 5];
506 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
508 for (i = 0; i < diff_2_max; i++)
509 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
510 i >= diff_2_max - diff_2_cut ? '-' : ' ');
512 diff_bar[pos++] = '|';
514 for (i = 0; i < diff_2_max; i++)
515 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
517 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
519 diff_bar[pos++] = '\0';
521 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
524 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
527 last_counter = counter;
533 static int last_redraw_mask = REDRAW_NONE;
535 // force screen redraw in every frame to continue drawing global animations
536 // (but always use the last redraw mask to prevent unwanted side effects)
537 if (redraw_mask == REDRAW_NONE)
538 redraw_mask = last_redraw_mask;
540 last_redraw_mask = redraw_mask;
543 // masked border now drawn immediately when blitting backbuffer to window
545 // draw masked border to all viewports, if defined
546 DrawMaskedBorder(redraw_mask);
549 // draw frames per second (only if debug mode is enabled)
550 if (redraw_mask & REDRAW_FPS)
551 DrawFramesPerSecond();
553 // redraw complete window if both playfield and (some) doors need redraw
554 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
555 redraw_mask = REDRAW_ALL;
557 /* although redrawing the whole window would be fine for normal gameplay,
558 being able to only redraw the playfield is required for deactivating
559 certain drawing areas (mainly playfield) to work, which is needed for
560 warp-forward to be fast enough (by skipping redraw of most frames) */
562 if (redraw_mask & REDRAW_ALL)
564 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
566 else if (redraw_mask & REDRAW_FIELD)
568 BlitBitmap(backbuffer, window,
569 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
571 else if (redraw_mask & REDRAW_DOORS)
573 if (redraw_mask & REDRAW_DOOR_1)
574 BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
576 if (redraw_mask & REDRAW_DOOR_2)
577 BlitBitmap(backbuffer, window, VX, VY, VXSIZE, VYSIZE, VX, VY);
579 if (redraw_mask & REDRAW_DOOR_3)
580 BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
583 redraw_mask = REDRAW_NONE;
586 PrintFrameTimeDebugging();
590 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
592 unsigned int frame_delay_value_old = GetVideoFrameDelay();
594 SetVideoFrameDelay(frame_delay_value);
598 SetVideoFrameDelay(frame_delay_value_old);
601 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
603 static int fade_type_skip = FADE_TYPE_NONE;
604 void (*draw_border_function)(void) = NULL;
605 int x, y, width, height;
606 int fade_delay, post_delay;
608 if (fade_type == FADE_TYPE_FADE_OUT)
610 if (fade_type_skip != FADE_TYPE_NONE)
612 /* skip all fade operations until specified fade operation */
613 if (fade_type & fade_type_skip)
614 fade_type_skip = FADE_TYPE_NONE;
619 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
623 redraw_mask |= fade_mask;
625 if (fade_type == FADE_TYPE_SKIP)
627 fade_type_skip = fade_mode;
632 fade_delay = fading.fade_delay;
633 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
635 if (fade_type_skip != FADE_TYPE_NONE)
637 /* skip all fade operations until specified fade operation */
638 if (fade_type & fade_type_skip)
639 fade_type_skip = FADE_TYPE_NONE;
644 if (global.autoplay_leveldir)
649 if (fade_mask == REDRAW_FIELD)
654 height = FADE_SYSIZE;
656 if (border.draw_masked_when_fading)
657 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
659 DrawMaskedBorder_FIELD(); /* draw once */
661 else /* REDRAW_ALL */
669 if (!setup.fade_screens ||
671 fading.fade_mode == FADE_MODE_NONE)
673 if (fade_mode == FADE_MODE_FADE_OUT)
676 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
678 redraw_mask &= ~fade_mask;
683 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
684 draw_border_function);
686 redraw_mask &= ~fade_mask;
689 static void SetScreenStates_BeforeFadingIn()
691 // temporarily set screen mode for animations to screen after fading in
692 global.anim_status = global.anim_status_next;
694 // store backbuffer with all animations that will be started after fading in
695 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
697 // set screen mode for animations back to fading
698 global.anim_status = GAME_MODE_PSEUDO_FADING;
701 static void SetScreenStates_AfterFadingIn()
703 // store new source screen (to use correct masked border for fading)
704 gfx.fade_border_source_status = global.border_status;
706 global.anim_status = global.anim_status_next;
708 // force update of global animation status in case of rapid screen changes
709 redraw_mask = REDRAW_ALL;
713 static void SetScreenStates_BeforeFadingOut()
715 // store new target screen (to use correct masked border for fading)
716 gfx.fade_border_target_status = game_status;
718 // set screen mode for animations to fading
719 global.anim_status = GAME_MODE_PSEUDO_FADING;
721 // store backbuffer with all animations that will be stopped for fading out
722 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
725 static void SetScreenStates_AfterFadingOut()
727 global.border_status = game_status;
730 void FadeIn(int fade_mask)
732 SetScreenStates_BeforeFadingIn();
735 DrawMaskedBorder(REDRAW_ALL);
738 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
739 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
741 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
745 FADE_SXSIZE = FULL_SXSIZE;
746 FADE_SYSIZE = FULL_SYSIZE;
748 SetScreenStates_AfterFadingIn();
751 void FadeOut(int fade_mask)
753 SetScreenStates_BeforeFadingOut();
756 DrawMaskedBorder(REDRAW_ALL);
759 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
760 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
762 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
764 SetScreenStates_AfterFadingOut();
767 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
769 static struct TitleFadingInfo fading_leave_stored;
772 fading_leave_stored = fading_leave;
774 fading = fading_leave_stored;
777 void FadeSetEnterMenu()
779 fading = menu.enter_menu;
781 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
784 void FadeSetLeaveMenu()
786 fading = menu.leave_menu;
788 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
791 void FadeSetEnterScreen()
793 fading = menu.enter_screen[game_status];
795 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
798 void FadeSetNextScreen()
800 fading = menu.next_screen[game_status];
802 // (do not overwrite fade mode set by FadeSetEnterScreen)
803 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
806 void FadeSetLeaveScreen()
808 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
811 void FadeSetFromType(int type)
813 if (type & TYPE_ENTER_SCREEN)
814 FadeSetEnterScreen();
815 else if (type & TYPE_ENTER)
817 else if (type & TYPE_LEAVE)
821 void FadeSetDisabled()
823 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
825 fading = fading_none;
828 void FadeSkipNextFadeIn()
830 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
833 void FadeSkipNextFadeOut()
835 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
838 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
840 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
842 return (graphic == IMG_UNDEFINED ? NULL :
843 graphic_info[graphic].bitmap != NULL || redefined ?
844 graphic_info[graphic].bitmap :
845 graphic_info[default_graphic].bitmap);
848 Bitmap *getBackgroundBitmap(int graphic)
850 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
853 Bitmap *getGlobalBorderBitmap(int graphic)
855 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
858 Bitmap *getGlobalBorderBitmapFromStatus(int status)
861 (status == GAME_MODE_MAIN ||
862 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
863 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
864 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
865 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
868 return getGlobalBorderBitmap(graphic);
871 void SetWindowBackgroundImageIfDefined(int graphic)
873 if (graphic_info[graphic].bitmap)
874 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
877 void SetMainBackgroundImageIfDefined(int graphic)
879 if (graphic_info[graphic].bitmap)
880 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
883 void SetDoorBackgroundImageIfDefined(int graphic)
885 if (graphic_info[graphic].bitmap)
886 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
889 void SetWindowBackgroundImage(int graphic)
891 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
894 void SetMainBackgroundImage(int graphic)
896 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
899 void SetDoorBackgroundImage(int graphic)
901 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
904 void SetPanelBackground()
906 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
908 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
909 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
911 SetDoorBackgroundBitmap(bitmap_db_panel);
914 void DrawBackground(int x, int y, int width, int height)
916 /* "drawto" might still point to playfield buffer here (hall of fame) */
917 ClearRectangleOnBackground(backbuffer, x, y, width, height);
919 if (IN_GFX_FIELD_FULL(x, y))
920 redraw_mask |= REDRAW_FIELD;
921 else if (IN_GFX_DOOR_1(x, y))
922 redraw_mask |= REDRAW_DOOR_1;
923 else if (IN_GFX_DOOR_2(x, y))
924 redraw_mask |= REDRAW_DOOR_2;
925 else if (IN_GFX_DOOR_3(x, y))
926 redraw_mask |= REDRAW_DOOR_3;
929 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
931 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
933 if (font->bitmap == NULL)
936 DrawBackground(x, y, width, height);
939 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
941 struct GraphicInfo *g = &graphic_info[graphic];
943 if (g->bitmap == NULL)
946 DrawBackground(x, y, width, height);
949 static int game_status_last = -1;
950 static Bitmap *global_border_bitmap_last = NULL;
951 static Bitmap *global_border_bitmap = NULL;
952 static int real_sx_last = -1, real_sy_last = -1;
953 static int full_sxsize_last = -1, full_sysize_last = -1;
954 static int dx_last = -1, dy_last = -1;
955 static int dxsize_last = -1, dysize_last = -1;
956 static int vx_last = -1, vy_last = -1;
957 static int vxsize_last = -1, vysize_last = -1;
959 boolean CheckIfGlobalBorderHasChanged()
961 // if game status has not changed, global border has not changed either
962 if (game_status == game_status_last)
965 // determine and store new global border bitmap for current game status
966 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
968 return (global_border_bitmap_last != global_border_bitmap);
971 boolean CheckIfGlobalBorderRedrawIsNeeded()
973 // if game status has not changed, nothing has to be redrawn
974 if (game_status == game_status_last)
977 // redraw if last screen was title screen
978 if (game_status_last == GAME_MODE_TITLE)
981 // redraw if global screen border has changed
982 if (CheckIfGlobalBorderHasChanged())
985 // redraw if position or size of playfield area has changed
986 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
987 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
990 // redraw if position or size of door area has changed
991 if (dx_last != DX || dy_last != DY ||
992 dxsize_last != DXSIZE || dysize_last != DYSIZE)
995 // redraw if position or size of tape area has changed
996 if (vx_last != VX || vy_last != VY ||
997 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1003 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1006 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1008 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1011 void RedrawGlobalBorder()
1013 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1015 RedrawGlobalBorderFromBitmap(bitmap);
1017 redraw_mask = REDRAW_ALL;
1020 static void RedrawGlobalBorderIfNeeded()
1022 if (game_status == game_status_last)
1025 // copy current draw buffer to later copy back areas that have not changed
1026 if (game_status_last != GAME_MODE_TITLE)
1027 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1029 if (CheckIfGlobalBorderRedrawIsNeeded())
1031 // redraw global screen border (or clear, if defined to be empty)
1032 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1034 // copy previous playfield and door areas, if they are defined on both
1035 // previous and current screen and if they still have the same size
1037 if (real_sx_last != -1 && real_sy_last != -1 &&
1038 REAL_SX != -1 && REAL_SY != -1 &&
1039 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1040 BlitBitmap(bitmap_db_store, backbuffer,
1041 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1044 if (dx_last != -1 && dy_last != -1 &&
1045 DX != -1 && DY != -1 &&
1046 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1047 BlitBitmap(bitmap_db_store, backbuffer,
1048 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1050 if (vx_last != -1 && vy_last != -1 &&
1051 VX != -1 && VY != -1 &&
1052 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1053 BlitBitmap(bitmap_db_store, backbuffer,
1054 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1056 redraw_mask = REDRAW_ALL;
1059 game_status_last = game_status;
1061 global_border_bitmap_last = global_border_bitmap;
1063 real_sx_last = REAL_SX;
1064 real_sy_last = REAL_SY;
1065 full_sxsize_last = FULL_SXSIZE;
1066 full_sysize_last = FULL_SYSIZE;
1069 dxsize_last = DXSIZE;
1070 dysize_last = DYSIZE;
1073 vxsize_last = VXSIZE;
1074 vysize_last = VYSIZE;
1079 RedrawGlobalBorderIfNeeded();
1081 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1082 /* (when entering hall of fame after playing) */
1083 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1085 /* !!! maybe this should be done before clearing the background !!! */
1086 if (game_status == GAME_MODE_PLAYING)
1088 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1089 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1093 SetDrawtoField(DRAW_TO_BACKBUFFER);
1097 void MarkTileDirty(int x, int y)
1099 redraw_mask |= REDRAW_FIELD;
1102 void SetBorderElement()
1106 BorderElement = EL_EMPTY;
1108 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1110 for (x = 0; x < lev_fieldx; x++)
1112 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1113 BorderElement = EL_STEELWALL;
1115 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1121 void FloodFillLevel(int from_x, int from_y, int fill_element,
1122 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1123 int max_fieldx, int max_fieldy)
1127 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1128 static int safety = 0;
1130 /* check if starting field still has the desired content */
1131 if (field[from_x][from_y] == fill_element)
1136 if (safety > max_fieldx * max_fieldy)
1137 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1139 old_element = field[from_x][from_y];
1140 field[from_x][from_y] = fill_element;
1142 for (i = 0; i < 4; i++)
1144 x = from_x + check[i][0];
1145 y = from_y + check[i][1];
1147 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1148 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1154 void SetRandomAnimationValue(int x, int y)
1156 gfx.anim_random_frame = GfxRandom[x][y];
1159 int getGraphicAnimationFrame(int graphic, int sync_frame)
1161 /* animation synchronized with global frame counter, not move position */
1162 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1163 sync_frame = FrameCounter;
1165 return getAnimationFrame(graphic_info[graphic].anim_frames,
1166 graphic_info[graphic].anim_delay,
1167 graphic_info[graphic].anim_mode,
1168 graphic_info[graphic].anim_start_frame,
1172 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1173 Bitmap **bitmap, int *x, int *y,
1174 boolean get_backside)
1176 struct GraphicInfo *g = &graphic_info[graphic];
1177 Bitmap *src_bitmap = g->bitmap;
1178 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1179 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1180 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1182 // if no in-game graphics defined, always use standard graphic size
1183 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1184 tilesize = TILESIZE;
1186 if (tilesize == gfx.standard_tile_size)
1187 src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1188 else if (tilesize == game.tile_size)
1189 src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
1191 src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1193 if (g->offset_y == 0) /* frames are ordered horizontally */
1195 int max_width = g->anim_frames_per_line * g->width;
1196 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1198 src_x = pos % max_width;
1199 src_y = src_y % g->height + pos / max_width * g->height;
1201 else if (g->offset_x == 0) /* frames are ordered vertically */
1203 int max_height = g->anim_frames_per_line * g->height;
1204 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1206 src_x = src_x % g->width + pos / max_height * g->width;
1207 src_y = pos % max_height;
1209 else /* frames are ordered diagonally */
1211 src_x = src_x + frame * g->offset_x;
1212 src_y = src_y + frame * g->offset_y;
1215 *bitmap = src_bitmap;
1216 *x = src_x * tilesize / g->tile_size;
1217 *y = src_y * tilesize / g->tile_size;
1220 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1221 int *x, int *y, boolean get_backside)
1223 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1227 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1228 Bitmap **bitmap, int *x, int *y)
1230 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1233 void getFixedGraphicSource(int graphic, int frame,
1234 Bitmap **bitmap, int *x, int *y)
1236 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1239 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1241 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1244 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1245 int *x, int *y, boolean get_backside)
1247 struct GraphicInfo *g = &graphic_info[graphic];
1248 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1249 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1251 if (TILESIZE_VAR != TILESIZE)
1252 return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1255 *bitmap = g->bitmap;
1257 if (g->offset_y == 0) /* frames are ordered horizontally */
1259 int max_width = g->anim_frames_per_line * g->width;
1260 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1262 *x = pos % max_width;
1263 *y = src_y % g->height + pos / max_width * g->height;
1265 else if (g->offset_x == 0) /* frames are ordered vertically */
1267 int max_height = g->anim_frames_per_line * g->height;
1268 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1270 *x = src_x % g->width + pos / max_height * g->width;
1271 *y = pos % max_height;
1273 else /* frames are ordered diagonally */
1275 *x = src_x + frame * g->offset_x;
1276 *y = src_y + frame * g->offset_y;
1279 *x = *x * TILESIZE_VAR / g->tile_size;
1280 *y = *y * TILESIZE_VAR / g->tile_size;
1283 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1285 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1288 void DrawGraphic(int x, int y, int graphic, int frame)
1291 if (!IN_SCR_FIELD(x, y))
1293 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1294 printf("DrawGraphic(): This should never happen!\n");
1299 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1302 MarkTileDirty(x, y);
1305 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1308 if (!IN_SCR_FIELD(x, y))
1310 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1311 printf("DrawGraphic(): This should never happen!\n");
1316 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1318 MarkTileDirty(x, y);
1321 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1327 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1329 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1332 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1338 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1339 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1342 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1345 if (!IN_SCR_FIELD(x, y))
1347 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1348 printf("DrawGraphicThruMask(): This should never happen!\n");
1353 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1356 MarkTileDirty(x, y);
1359 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1362 if (!IN_SCR_FIELD(x, y))
1364 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1365 printf("DrawGraphicThruMask(): This should never happen!\n");
1370 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1372 MarkTileDirty(x, y);
1375 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1381 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1383 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1387 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1388 int graphic, int frame)
1393 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1395 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1399 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1401 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1403 MarkTileDirty(x / tilesize, y / tilesize);
1406 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1412 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1413 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1416 void DrawMiniGraphic(int x, int y, int graphic)
1418 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1419 MarkTileDirty(x / 2, y / 2);
1422 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1427 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1428 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1431 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1432 int graphic, int frame,
1433 int cut_mode, int mask_mode)
1438 int width = TILEX, height = TILEY;
1441 if (dx || dy) /* shifted graphic */
1443 if (x < BX1) /* object enters playfield from the left */
1450 else if (x > BX2) /* object enters playfield from the right */
1456 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1462 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1464 else if (dx) /* general horizontal movement */
1465 MarkTileDirty(x + SIGN(dx), y);
1467 if (y < BY1) /* object enters playfield from the top */
1469 if (cut_mode == CUT_BELOW) /* object completely above top border */
1477 else if (y > BY2) /* object enters playfield from the bottom */
1483 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1489 else if (dy > 0 && cut_mode == CUT_ABOVE)
1491 if (y == BY2) /* object completely above bottom border */
1497 MarkTileDirty(x, y + 1);
1498 } /* object leaves playfield to the bottom */
1499 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1501 else if (dy) /* general vertical movement */
1502 MarkTileDirty(x, y + SIGN(dy));
1506 if (!IN_SCR_FIELD(x, y))
1508 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1509 printf("DrawGraphicShifted(): This should never happen!\n");
1514 width = width * TILESIZE_VAR / TILESIZE;
1515 height = height * TILESIZE_VAR / TILESIZE;
1516 cx = cx * TILESIZE_VAR / TILESIZE;
1517 cy = cy * TILESIZE_VAR / TILESIZE;
1518 dx = dx * TILESIZE_VAR / TILESIZE;
1519 dy = dy * TILESIZE_VAR / TILESIZE;
1521 if (width > 0 && height > 0)
1523 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1528 dst_x = FX + x * TILEX_VAR + dx;
1529 dst_y = FY + y * TILEY_VAR + dy;
1531 if (mask_mode == USE_MASKING)
1532 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1535 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1538 MarkTileDirty(x, y);
1542 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1543 int graphic, int frame,
1544 int cut_mode, int mask_mode)
1549 int width = TILEX_VAR, height = TILEY_VAR;
1552 int x2 = x + SIGN(dx);
1553 int y2 = y + SIGN(dy);
1555 /* movement with two-tile animations must be sync'ed with movement position,
1556 not with current GfxFrame (which can be higher when using slow movement) */
1557 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1558 int anim_frames = graphic_info[graphic].anim_frames;
1560 /* (we also need anim_delay here for movement animations with less frames) */
1561 int anim_delay = graphic_info[graphic].anim_delay;
1562 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1564 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1565 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1567 /* re-calculate animation frame for two-tile movement animation */
1568 frame = getGraphicAnimationFrame(graphic, sync_frame);
1570 /* check if movement start graphic inside screen area and should be drawn */
1571 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1573 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1575 dst_x = FX + x1 * TILEX_VAR;
1576 dst_y = FY + y1 * TILEY_VAR;
1578 if (mask_mode == USE_MASKING)
1579 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1582 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1585 MarkTileDirty(x1, y1);
1588 /* check if movement end graphic inside screen area and should be drawn */
1589 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1591 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1593 dst_x = FX + x2 * TILEX_VAR;
1594 dst_y = FY + y2 * TILEY_VAR;
1596 if (mask_mode == USE_MASKING)
1597 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1600 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1603 MarkTileDirty(x2, y2);
1607 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1608 int graphic, int frame,
1609 int cut_mode, int mask_mode)
1613 DrawGraphic(x, y, graphic, frame);
1618 if (graphic_info[graphic].double_movement) /* EM style movement images */
1619 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1621 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1624 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1625 int frame, int cut_mode)
1627 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1630 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1631 int cut_mode, int mask_mode)
1633 int lx = LEVELX(x), ly = LEVELY(y);
1637 if (IN_LEV_FIELD(lx, ly))
1639 SetRandomAnimationValue(lx, ly);
1641 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1642 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1644 /* do not use double (EM style) movement graphic when not moving */
1645 if (graphic_info[graphic].double_movement && !dx && !dy)
1647 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1648 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1651 else /* border element */
1653 graphic = el2img(element);
1654 frame = getGraphicAnimationFrame(graphic, -1);
1657 if (element == EL_EXPANDABLE_WALL)
1659 boolean left_stopped = FALSE, right_stopped = FALSE;
1661 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1662 left_stopped = TRUE;
1663 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1664 right_stopped = TRUE;
1666 if (left_stopped && right_stopped)
1668 else if (left_stopped)
1670 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1671 frame = graphic_info[graphic].anim_frames - 1;
1673 else if (right_stopped)
1675 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1676 frame = graphic_info[graphic].anim_frames - 1;
1681 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1682 else if (mask_mode == USE_MASKING)
1683 DrawGraphicThruMask(x, y, graphic, frame);
1685 DrawGraphic(x, y, graphic, frame);
1688 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1689 int cut_mode, int mask_mode)
1691 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1692 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1693 cut_mode, mask_mode);
1696 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1699 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1702 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1705 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1708 void DrawLevelElementThruMask(int x, int y, int element)
1710 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1713 void DrawLevelFieldThruMask(int x, int y)
1715 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1718 /* !!! implementation of quicksand is totally broken !!! */
1719 #define IS_CRUMBLED_TILE(x, y, e) \
1720 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1721 !IS_MOVING(x, y) || \
1722 (e) == EL_QUICKSAND_EMPTYING || \
1723 (e) == EL_QUICKSAND_FAST_EMPTYING))
1725 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1730 int width, height, cx, cy;
1731 int sx = SCREENX(x), sy = SCREENY(y);
1732 int crumbled_border_size = graphic_info[graphic].border_size;
1735 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1737 for (i = 1; i < 4; i++)
1739 int dxx = (i & 1 ? dx : 0);
1740 int dyy = (i & 2 ? dy : 0);
1743 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1746 /* check if neighbour field is of same crumble type */
1747 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1748 graphic_info[graphic].class ==
1749 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1751 /* return if check prevents inner corner */
1752 if (same == (dxx == dx && dyy == dy))
1756 /* if we reach this point, we have an inner corner */
1758 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1760 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1761 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1762 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1763 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1765 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1766 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1769 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1774 int width, height, bx, by, cx, cy;
1775 int sx = SCREENX(x), sy = SCREENY(y);
1776 int crumbled_border_size = graphic_info[graphic].border_size;
1777 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1778 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1781 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1783 /* draw simple, sloppy, non-corner-accurate crumbled border */
1785 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1786 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1787 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1788 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1790 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1791 FX + sx * TILEX_VAR + cx,
1792 FY + sy * TILEY_VAR + cy);
1794 /* (remaining middle border part must be at least as big as corner part) */
1795 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1796 crumbled_border_size >= TILESIZE / 3)
1799 /* correct corners of crumbled border, if needed */
1801 for (i = -1; i <= 1; i += 2)
1803 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1804 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1805 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1808 /* check if neighbour field is of same crumble type */
1809 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1810 graphic_info[graphic].class ==
1811 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1813 /* no crumbled corner, but continued crumbled border */
1815 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1816 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1817 int b1 = (i == 1 ? crumbled_border_size_var :
1818 TILESIZE_VAR - 2 * crumbled_border_size_var);
1820 width = crumbled_border_size_var;
1821 height = crumbled_border_size_var;
1823 if (dir == 1 || dir == 2)
1838 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1840 FX + sx * TILEX_VAR + cx,
1841 FY + sy * TILEY_VAR + cy);
1846 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1848 int sx = SCREENX(x), sy = SCREENY(y);
1851 static int xy[4][2] =
1859 if (!IN_LEV_FIELD(x, y))
1862 element = TILE_GFX_ELEMENT(x, y);
1864 /* crumble field itself */
1865 if (IS_CRUMBLED_TILE(x, y, element))
1867 if (!IN_SCR_FIELD(sx, sy))
1870 for (i = 0; i < 4; i++)
1872 int xx = x + xy[i][0];
1873 int yy = y + xy[i][1];
1875 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1878 /* check if neighbour field is of same crumble type */
1879 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1880 graphic_info[graphic].class ==
1881 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1884 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1887 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1888 graphic_info[graphic].anim_frames == 2)
1890 for (i = 0; i < 4; i++)
1892 int dx = (i & 1 ? +1 : -1);
1893 int dy = (i & 2 ? +1 : -1);
1895 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1899 MarkTileDirty(sx, sy);
1901 else /* center field not crumbled -- crumble neighbour fields */
1903 for (i = 0; i < 4; i++)
1905 int xx = x + xy[i][0];
1906 int yy = y + xy[i][1];
1907 int sxx = sx + xy[i][0];
1908 int syy = sy + xy[i][1];
1910 if (!IN_LEV_FIELD(xx, yy) ||
1911 !IN_SCR_FIELD(sxx, syy))
1914 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1917 element = TILE_GFX_ELEMENT(xx, yy);
1919 if (!IS_CRUMBLED_TILE(xx, yy, element))
1922 graphic = el_act2crm(element, ACTION_DEFAULT);
1924 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1926 MarkTileDirty(sxx, syy);
1931 void DrawLevelFieldCrumbled(int x, int y)
1935 if (!IN_LEV_FIELD(x, y))
1938 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1939 GfxElement[x][y] != EL_UNDEFINED &&
1940 GFX_CRUMBLED(GfxElement[x][y]))
1942 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1947 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1949 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1952 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1955 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1956 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1957 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1958 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1959 int sx = SCREENX(x), sy = SCREENY(y);
1961 DrawGraphic(sx, sy, graphic1, frame1);
1962 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1965 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1967 int sx = SCREENX(x), sy = SCREENY(y);
1968 static int xy[4][2] =
1977 for (i = 0; i < 4; i++)
1979 int xx = x + xy[i][0];
1980 int yy = y + xy[i][1];
1981 int sxx = sx + xy[i][0];
1982 int syy = sy + xy[i][1];
1984 if (!IN_LEV_FIELD(xx, yy) ||
1985 !IN_SCR_FIELD(sxx, syy) ||
1986 !GFX_CRUMBLED(Feld[xx][yy]) ||
1990 DrawLevelField(xx, yy);
1994 static int getBorderElement(int x, int y)
1998 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1999 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2000 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2001 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2002 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2003 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2004 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2006 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2007 int steel_position = (x == -1 && y == -1 ? 0 :
2008 x == lev_fieldx && y == -1 ? 1 :
2009 x == -1 && y == lev_fieldy ? 2 :
2010 x == lev_fieldx && y == lev_fieldy ? 3 :
2011 x == -1 || x == lev_fieldx ? 4 :
2012 y == -1 || y == lev_fieldy ? 5 : 6);
2014 return border[steel_position][steel_type];
2017 void DrawScreenElement(int x, int y, int element)
2019 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2020 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2023 void DrawLevelElement(int x, int y, int element)
2025 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2026 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2029 void DrawScreenField(int x, int y)
2031 int lx = LEVELX(x), ly = LEVELY(y);
2032 int element, content;
2034 if (!IN_LEV_FIELD(lx, ly))
2036 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2039 element = getBorderElement(lx, ly);
2041 DrawScreenElement(x, y, element);
2046 element = Feld[lx][ly];
2047 content = Store[lx][ly];
2049 if (IS_MOVING(lx, ly))
2051 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2052 boolean cut_mode = NO_CUTTING;
2054 if (element == EL_QUICKSAND_EMPTYING ||
2055 element == EL_QUICKSAND_FAST_EMPTYING ||
2056 element == EL_MAGIC_WALL_EMPTYING ||
2057 element == EL_BD_MAGIC_WALL_EMPTYING ||
2058 element == EL_DC_MAGIC_WALL_EMPTYING ||
2059 element == EL_AMOEBA_DROPPING)
2060 cut_mode = CUT_ABOVE;
2061 else if (element == EL_QUICKSAND_FILLING ||
2062 element == EL_QUICKSAND_FAST_FILLING ||
2063 element == EL_MAGIC_WALL_FILLING ||
2064 element == EL_BD_MAGIC_WALL_FILLING ||
2065 element == EL_DC_MAGIC_WALL_FILLING)
2066 cut_mode = CUT_BELOW;
2068 if (cut_mode == CUT_ABOVE)
2069 DrawScreenElement(x, y, element);
2071 DrawScreenElement(x, y, EL_EMPTY);
2074 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2075 else if (cut_mode == NO_CUTTING)
2076 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2079 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2081 if (cut_mode == CUT_BELOW &&
2082 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2083 DrawLevelElement(lx, ly + 1, element);
2086 if (content == EL_ACID)
2088 int dir = MovDir[lx][ly];
2089 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2090 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2092 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2095 else if (IS_BLOCKED(lx, ly))
2100 boolean cut_mode = NO_CUTTING;
2101 int element_old, content_old;
2103 Blocked2Moving(lx, ly, &oldx, &oldy);
2106 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2107 MovDir[oldx][oldy] == MV_RIGHT);
2109 element_old = Feld[oldx][oldy];
2110 content_old = Store[oldx][oldy];
2112 if (element_old == EL_QUICKSAND_EMPTYING ||
2113 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2114 element_old == EL_MAGIC_WALL_EMPTYING ||
2115 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2116 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2117 element_old == EL_AMOEBA_DROPPING)
2118 cut_mode = CUT_ABOVE;
2120 DrawScreenElement(x, y, EL_EMPTY);
2123 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2125 else if (cut_mode == NO_CUTTING)
2126 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2129 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2132 else if (IS_DRAWABLE(element))
2133 DrawScreenElement(x, y, element);
2135 DrawScreenElement(x, y, EL_EMPTY);
2138 void DrawLevelField(int x, int y)
2140 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2141 DrawScreenField(SCREENX(x), SCREENY(y));
2142 else if (IS_MOVING(x, y))
2146 Moving2Blocked(x, y, &newx, &newy);
2147 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2148 DrawScreenField(SCREENX(newx), SCREENY(newy));
2150 else if (IS_BLOCKED(x, y))
2154 Blocked2Moving(x, y, &oldx, &oldy);
2155 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2156 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2160 void DrawSizedElement(int x, int y, int element, int tilesize)
2164 graphic = el2edimg(element);
2165 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2168 void DrawMiniElement(int x, int y, int element)
2172 graphic = el2edimg(element);
2173 DrawMiniGraphic(x, y, graphic);
2176 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2179 int x = sx + scroll_x, y = sy + scroll_y;
2181 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2182 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2183 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2184 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2186 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2189 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2191 int x = sx + scroll_x, y = sy + scroll_y;
2193 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2194 DrawMiniElement(sx, sy, EL_EMPTY);
2195 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2196 DrawMiniElement(sx, sy, Feld[x][y]);
2198 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2201 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2202 int x, int y, int xsize, int ysize,
2203 int tile_width, int tile_height)
2207 int dst_x = startx + x * tile_width;
2208 int dst_y = starty + y * tile_height;
2209 int width = graphic_info[graphic].width;
2210 int height = graphic_info[graphic].height;
2211 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2212 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2213 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2214 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2215 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2216 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2217 boolean draw_masked = graphic_info[graphic].draw_masked;
2219 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2221 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2223 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2227 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2228 inner_sx + (x - 1) * tile_width % inner_width);
2229 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2230 inner_sy + (y - 1) * tile_height % inner_height);
2233 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2236 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2240 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2241 int x, int y, int xsize, int ysize, int font_nr)
2243 int font_width = getFontWidth(font_nr);
2244 int font_height = getFontHeight(font_nr);
2246 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2247 font_width, font_height);
2250 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2252 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2253 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2254 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2255 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2256 boolean no_delay = (tape.warp_forward);
2257 unsigned int anim_delay = 0;
2258 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2259 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2260 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2261 int font_width = getFontWidth(font_nr);
2262 int font_height = getFontHeight(font_nr);
2263 int max_xsize = level.envelope[envelope_nr].xsize;
2264 int max_ysize = level.envelope[envelope_nr].ysize;
2265 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2266 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2267 int xend = max_xsize;
2268 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2269 int xstep = (xstart < xend ? 1 : 0);
2270 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2272 int end = MAX(xend - xstart, yend - ystart);
2275 for (i = start; i <= end; i++)
2277 int last_frame = end; // last frame of this "for" loop
2278 int x = xstart + i * xstep;
2279 int y = ystart + i * ystep;
2280 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2281 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2282 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2283 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2286 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2288 BlitScreenToBitmap(backbuffer);
2290 SetDrawtoField(DRAW_TO_BACKBUFFER);
2292 for (yy = 0; yy < ysize; yy++)
2293 for (xx = 0; xx < xsize; xx++)
2294 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2296 DrawTextBuffer(sx + font_width, sy + font_height,
2297 level.envelope[envelope_nr].text, font_nr, max_xsize,
2298 xsize - 2, ysize - 2, 0, mask_mode,
2299 level.envelope[envelope_nr].autowrap,
2300 level.envelope[envelope_nr].centered, FALSE);
2302 redraw_mask |= REDRAW_FIELD;
2305 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2309 void ShowEnvelope(int envelope_nr)
2311 int element = EL_ENVELOPE_1 + envelope_nr;
2312 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2313 int sound_opening = element_info[element].sound[ACTION_OPENING];
2314 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2315 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2316 boolean no_delay = (tape.warp_forward);
2317 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2318 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2319 int anim_mode = graphic_info[graphic].anim_mode;
2320 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2321 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2323 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2325 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2327 if (anim_mode == ANIM_DEFAULT)
2328 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2330 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2333 Delay(wait_delay_value);
2335 WaitForEventToContinue();
2337 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2339 if (anim_mode != ANIM_NONE)
2340 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2342 if (anim_mode == ANIM_DEFAULT)
2343 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2345 game.envelope_active = FALSE;
2347 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2349 redraw_mask |= REDRAW_FIELD;
2353 static void setRequestBasePosition(int *x, int *y)
2355 int sx_base, sy_base;
2357 if (request.x != -1)
2358 sx_base = request.x;
2359 else if (request.align == ALIGN_LEFT)
2361 else if (request.align == ALIGN_RIGHT)
2362 sx_base = SX + SXSIZE;
2364 sx_base = SX + SXSIZE / 2;
2366 if (request.y != -1)
2367 sy_base = request.y;
2368 else if (request.valign == VALIGN_TOP)
2370 else if (request.valign == VALIGN_BOTTOM)
2371 sy_base = SY + SYSIZE;
2373 sy_base = SY + SYSIZE / 2;
2379 static void setRequestPositionExt(int *x, int *y, int width, int height,
2380 boolean add_border_size)
2382 int border_size = request.border_size;
2383 int sx_base, sy_base;
2386 setRequestBasePosition(&sx_base, &sy_base);
2388 if (request.align == ALIGN_LEFT)
2390 else if (request.align == ALIGN_RIGHT)
2391 sx = sx_base - width;
2393 sx = sx_base - width / 2;
2395 if (request.valign == VALIGN_TOP)
2397 else if (request.valign == VALIGN_BOTTOM)
2398 sy = sy_base - height;
2400 sy = sy_base - height / 2;
2402 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2403 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2405 if (add_border_size)
2415 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2417 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2420 void DrawEnvelopeRequest(char *text)
2422 char *text_final = text;
2423 char *text_door_style = NULL;
2424 int graphic = IMG_BACKGROUND_REQUEST;
2425 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2426 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2427 int font_nr = FONT_REQUEST;
2428 int font_width = getFontWidth(font_nr);
2429 int font_height = getFontHeight(font_nr);
2430 int border_size = request.border_size;
2431 int line_spacing = request.line_spacing;
2432 int line_height = font_height + line_spacing;
2433 int max_text_width = request.width - 2 * border_size;
2434 int max_text_height = request.height - 2 * border_size;
2435 int line_length = max_text_width / font_width;
2436 int max_lines = max_text_height / line_height;
2437 int text_width = line_length * font_width;
2438 int width = request.width;
2439 int height = request.height;
2440 int tile_size = MAX(request.step_offset, 1);
2441 int x_steps = width / tile_size;
2442 int y_steps = height / tile_size;
2443 int sx_offset = border_size;
2444 int sy_offset = border_size;
2448 if (request.centered)
2449 sx_offset = (request.width - text_width) / 2;
2451 if (request.wrap_single_words && !request.autowrap)
2453 char *src_text_ptr, *dst_text_ptr;
2455 text_door_style = checked_malloc(2 * strlen(text) + 1);
2457 src_text_ptr = text;
2458 dst_text_ptr = text_door_style;
2460 while (*src_text_ptr)
2462 if (*src_text_ptr == ' ' ||
2463 *src_text_ptr == '?' ||
2464 *src_text_ptr == '!')
2465 *dst_text_ptr++ = '\n';
2467 if (*src_text_ptr != ' ')
2468 *dst_text_ptr++ = *src_text_ptr;
2473 *dst_text_ptr = '\0';
2475 text_final = text_door_style;
2478 setRequestPosition(&sx, &sy, FALSE);
2480 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2482 for (y = 0; y < y_steps; y++)
2483 for (x = 0; x < x_steps; x++)
2484 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2485 x, y, x_steps, y_steps,
2486 tile_size, tile_size);
2488 /* force DOOR font inside door area */
2489 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2491 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2492 line_length, -1, max_lines, line_spacing, mask_mode,
2493 request.autowrap, request.centered, FALSE);
2497 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2498 RedrawGadget(tool_gadget[i]);
2500 // store readily prepared envelope request for later use when animating
2501 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2503 if (text_door_style)
2504 free(text_door_style);
2507 void AnimateEnvelopeRequest(int anim_mode, int action)
2509 int graphic = IMG_BACKGROUND_REQUEST;
2510 boolean draw_masked = graphic_info[graphic].draw_masked;
2511 int delay_value_normal = request.step_delay;
2512 int delay_value_fast = delay_value_normal / 2;
2513 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2514 boolean no_delay = (tape.warp_forward);
2515 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2516 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2517 unsigned int anim_delay = 0;
2519 int tile_size = MAX(request.step_offset, 1);
2520 int max_xsize = request.width / tile_size;
2521 int max_ysize = request.height / tile_size;
2522 int max_xsize_inner = max_xsize - 2;
2523 int max_ysize_inner = max_ysize - 2;
2525 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2526 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2527 int xend = max_xsize_inner;
2528 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2529 int xstep = (xstart < xend ? 1 : 0);
2530 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2532 int end = MAX(xend - xstart, yend - ystart);
2535 if (setup.quick_doors)
2542 for (i = start; i <= end; i++)
2544 int last_frame = end; // last frame of this "for" loop
2545 int x = xstart + i * xstep;
2546 int y = ystart + i * ystep;
2547 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2548 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2549 int xsize_size_left = (xsize - 1) * tile_size;
2550 int ysize_size_top = (ysize - 1) * tile_size;
2551 int max_xsize_pos = (max_xsize - 1) * tile_size;
2552 int max_ysize_pos = (max_ysize - 1) * tile_size;
2553 int width = xsize * tile_size;
2554 int height = ysize * tile_size;
2559 setRequestPosition(&src_x, &src_y, FALSE);
2560 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2562 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2564 for (yy = 0; yy < 2; yy++)
2566 for (xx = 0; xx < 2; xx++)
2568 int src_xx = src_x + xx * max_xsize_pos;
2569 int src_yy = src_y + yy * max_ysize_pos;
2570 int dst_xx = dst_x + xx * xsize_size_left;
2571 int dst_yy = dst_y + yy * ysize_size_top;
2572 int xx_size = (xx ? tile_size : xsize_size_left);
2573 int yy_size = (yy ? tile_size : ysize_size_top);
2576 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2577 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2579 BlitBitmap(bitmap_db_cross, backbuffer,
2580 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2584 redraw_mask |= REDRAW_FIELD;
2588 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2592 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2594 int graphic = IMG_BACKGROUND_REQUEST;
2595 int sound_opening = SND_REQUEST_OPENING;
2596 int sound_closing = SND_REQUEST_CLOSING;
2597 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2598 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2599 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2600 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2601 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2603 if (game_status == GAME_MODE_PLAYING)
2604 BlitScreenToBitmap(backbuffer);
2606 SetDrawtoField(DRAW_TO_BACKBUFFER);
2608 // SetDrawBackgroundMask(REDRAW_NONE);
2610 if (action == ACTION_OPENING)
2612 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2614 if (req_state & REQ_ASK)
2616 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2617 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2619 else if (req_state & REQ_CONFIRM)
2621 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2623 else if (req_state & REQ_PLAYER)
2625 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2626 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2627 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2628 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2631 DrawEnvelopeRequest(text);
2634 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2636 if (action == ACTION_OPENING)
2638 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2640 if (anim_mode == ANIM_DEFAULT)
2641 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2643 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2647 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2649 if (anim_mode != ANIM_NONE)
2650 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2652 if (anim_mode == ANIM_DEFAULT)
2653 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2656 game.envelope_active = FALSE;
2658 if (action == ACTION_CLOSING)
2659 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2661 // SetDrawBackgroundMask(last_draw_background_mask);
2663 redraw_mask |= REDRAW_FIELD;
2667 if (action == ACTION_CLOSING &&
2668 game_status == GAME_MODE_PLAYING &&
2669 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2670 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2673 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2677 int graphic = el2preimg(element);
2679 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2680 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2683 void DrawLevel(int draw_background_mask)
2687 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2688 SetDrawBackgroundMask(draw_background_mask);
2692 for (x = BX1; x <= BX2; x++)
2693 for (y = BY1; y <= BY2; y++)
2694 DrawScreenField(x, y);
2696 redraw_mask |= REDRAW_FIELD;
2699 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2704 for (x = 0; x < size_x; x++)
2705 for (y = 0; y < size_y; y++)
2706 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2708 redraw_mask |= REDRAW_FIELD;
2711 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2715 for (x = 0; x < size_x; x++)
2716 for (y = 0; y < size_y; y++)
2717 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2719 redraw_mask |= REDRAW_FIELD;
2722 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2724 boolean show_level_border = (BorderElement != EL_EMPTY);
2725 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2726 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2727 int tile_size = preview.tile_size;
2728 int preview_width = preview.xsize * tile_size;
2729 int preview_height = preview.ysize * tile_size;
2730 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2731 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2732 int real_preview_width = real_preview_xsize * tile_size;
2733 int real_preview_height = real_preview_ysize * tile_size;
2734 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2735 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2738 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2741 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2743 dst_x += (preview_width - real_preview_width) / 2;
2744 dst_y += (preview_height - real_preview_height) / 2;
2746 for (x = 0; x < real_preview_xsize; x++)
2748 for (y = 0; y < real_preview_ysize; y++)
2750 int lx = from_x + x + (show_level_border ? -1 : 0);
2751 int ly = from_y + y + (show_level_border ? -1 : 0);
2752 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2753 getBorderElement(lx, ly));
2755 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2756 element, tile_size);
2760 redraw_mask |= REDRAW_FIELD;
2763 #define MICROLABEL_EMPTY 0
2764 #define MICROLABEL_LEVEL_NAME 1
2765 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2766 #define MICROLABEL_LEVEL_AUTHOR 3
2767 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2768 #define MICROLABEL_IMPORTED_FROM 5
2769 #define MICROLABEL_IMPORTED_BY_HEAD 6
2770 #define MICROLABEL_IMPORTED_BY 7
2772 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2774 int max_text_width = SXSIZE;
2775 int font_width = getFontWidth(font_nr);
2777 if (pos->align == ALIGN_CENTER)
2778 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2779 else if (pos->align == ALIGN_RIGHT)
2780 max_text_width = pos->x;
2782 max_text_width = SXSIZE - pos->x;
2784 return max_text_width / font_width;
2787 static void DrawPreviewLevelLabelExt(int mode)
2789 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2790 char label_text[MAX_OUTPUT_LINESIZE + 1];
2791 int max_len_label_text;
2792 int font_nr = pos->font;
2795 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2798 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2799 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2800 mode == MICROLABEL_IMPORTED_BY_HEAD)
2801 font_nr = pos->font_alt;
2803 max_len_label_text = getMaxTextLength(pos, font_nr);
2805 if (pos->size != -1)
2806 max_len_label_text = pos->size;
2808 for (i = 0; i < max_len_label_text; i++)
2809 label_text[i] = ' ';
2810 label_text[max_len_label_text] = '\0';
2812 if (strlen(label_text) > 0)
2813 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2816 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2817 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2818 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2819 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2820 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2821 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2822 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2823 max_len_label_text);
2824 label_text[max_len_label_text] = '\0';
2826 if (strlen(label_text) > 0)
2827 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2829 redraw_mask |= REDRAW_FIELD;
2832 static void DrawPreviewLevelExt(boolean restart)
2834 static unsigned int scroll_delay = 0;
2835 static unsigned int label_delay = 0;
2836 static int from_x, from_y, scroll_direction;
2837 static int label_state, label_counter;
2838 unsigned int scroll_delay_value = preview.step_delay;
2839 boolean show_level_border = (BorderElement != EL_EMPTY);
2840 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2841 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2848 if (preview.anim_mode == ANIM_CENTERED)
2850 if (level_xsize > preview.xsize)
2851 from_x = (level_xsize - preview.xsize) / 2;
2852 if (level_ysize > preview.ysize)
2853 from_y = (level_ysize - preview.ysize) / 2;
2856 from_x += preview.xoffset;
2857 from_y += preview.yoffset;
2859 scroll_direction = MV_RIGHT;
2863 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2864 DrawPreviewLevelLabelExt(label_state);
2866 /* initialize delay counters */
2867 DelayReached(&scroll_delay, 0);
2868 DelayReached(&label_delay, 0);
2870 if (leveldir_current->name)
2872 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2873 char label_text[MAX_OUTPUT_LINESIZE + 1];
2874 int font_nr = pos->font;
2875 int max_len_label_text = getMaxTextLength(pos, font_nr);
2877 if (pos->size != -1)
2878 max_len_label_text = pos->size;
2880 strncpy(label_text, leveldir_current->name, max_len_label_text);
2881 label_text[max_len_label_text] = '\0';
2883 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2884 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2890 /* scroll preview level, if needed */
2891 if (preview.anim_mode != ANIM_NONE &&
2892 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2893 DelayReached(&scroll_delay, scroll_delay_value))
2895 switch (scroll_direction)
2900 from_x -= preview.step_offset;
2901 from_x = (from_x < 0 ? 0 : from_x);
2904 scroll_direction = MV_UP;
2908 if (from_x < level_xsize - preview.xsize)
2910 from_x += preview.step_offset;
2911 from_x = (from_x > level_xsize - preview.xsize ?
2912 level_xsize - preview.xsize : from_x);
2915 scroll_direction = MV_DOWN;
2921 from_y -= preview.step_offset;
2922 from_y = (from_y < 0 ? 0 : from_y);
2925 scroll_direction = MV_RIGHT;
2929 if (from_y < level_ysize - preview.ysize)
2931 from_y += preview.step_offset;
2932 from_y = (from_y > level_ysize - preview.ysize ?
2933 level_ysize - preview.ysize : from_y);
2936 scroll_direction = MV_LEFT;
2943 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2946 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2947 /* redraw micro level label, if needed */
2948 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2949 !strEqual(level.author, ANONYMOUS_NAME) &&
2950 !strEqual(level.author, leveldir_current->name) &&
2951 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2953 int max_label_counter = 23;
2955 if (leveldir_current->imported_from != NULL &&
2956 strlen(leveldir_current->imported_from) > 0)
2957 max_label_counter += 14;
2958 if (leveldir_current->imported_by != NULL &&
2959 strlen(leveldir_current->imported_by) > 0)
2960 max_label_counter += 14;
2962 label_counter = (label_counter + 1) % max_label_counter;
2963 label_state = (label_counter >= 0 && label_counter <= 7 ?
2964 MICROLABEL_LEVEL_NAME :
2965 label_counter >= 9 && label_counter <= 12 ?
2966 MICROLABEL_LEVEL_AUTHOR_HEAD :
2967 label_counter >= 14 && label_counter <= 21 ?
2968 MICROLABEL_LEVEL_AUTHOR :
2969 label_counter >= 23 && label_counter <= 26 ?
2970 MICROLABEL_IMPORTED_FROM_HEAD :
2971 label_counter >= 28 && label_counter <= 35 ?
2972 MICROLABEL_IMPORTED_FROM :
2973 label_counter >= 37 && label_counter <= 40 ?
2974 MICROLABEL_IMPORTED_BY_HEAD :
2975 label_counter >= 42 && label_counter <= 49 ?
2976 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2978 if (leveldir_current->imported_from == NULL &&
2979 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2980 label_state == MICROLABEL_IMPORTED_FROM))
2981 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2982 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2984 DrawPreviewLevelLabelExt(label_state);
2988 void DrawPreviewLevelInitial()
2990 DrawPreviewLevelExt(TRUE);
2993 void DrawPreviewLevelAnimation()
2995 DrawPreviewLevelExt(FALSE);
2998 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2999 int graphic, int sync_frame,
3002 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3004 if (mask_mode == USE_MASKING)
3005 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3007 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3010 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3011 int graphic, int sync_frame, int mask_mode)
3013 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3015 if (mask_mode == USE_MASKING)
3016 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3018 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3021 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3023 int lx = LEVELX(x), ly = LEVELY(y);
3025 if (!IN_SCR_FIELD(x, y))
3028 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3029 graphic, GfxFrame[lx][ly], NO_MASKING);
3031 MarkTileDirty(x, y);
3034 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3036 int lx = LEVELX(x), ly = LEVELY(y);
3038 if (!IN_SCR_FIELD(x, y))
3041 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3042 graphic, GfxFrame[lx][ly], NO_MASKING);
3043 MarkTileDirty(x, y);
3046 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3048 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3051 void DrawLevelElementAnimation(int x, int y, int element)
3053 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3055 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3058 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3060 int sx = SCREENX(x), sy = SCREENY(y);
3062 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3065 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3068 DrawGraphicAnimation(sx, sy, graphic);
3071 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3072 DrawLevelFieldCrumbled(x, y);
3074 if (GFX_CRUMBLED(Feld[x][y]))
3075 DrawLevelFieldCrumbled(x, y);
3079 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3081 int sx = SCREENX(x), sy = SCREENY(y);
3084 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3087 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3089 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3092 DrawGraphicAnimation(sx, sy, graphic);
3094 if (GFX_CRUMBLED(element))
3095 DrawLevelFieldCrumbled(x, y);
3098 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3100 if (player->use_murphy)
3102 /* this works only because currently only one player can be "murphy" ... */
3103 static int last_horizontal_dir = MV_LEFT;
3104 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3106 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3107 last_horizontal_dir = move_dir;
3109 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3111 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3113 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3119 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3122 static boolean equalGraphics(int graphic1, int graphic2)
3124 struct GraphicInfo *g1 = &graphic_info[graphic1];
3125 struct GraphicInfo *g2 = &graphic_info[graphic2];
3127 return (g1->bitmap == g2->bitmap &&
3128 g1->src_x == g2->src_x &&
3129 g1->src_y == g2->src_y &&
3130 g1->anim_frames == g2->anim_frames &&
3131 g1->anim_delay == g2->anim_delay &&
3132 g1->anim_mode == g2->anim_mode);
3135 void DrawAllPlayers()
3139 for (i = 0; i < MAX_PLAYERS; i++)
3140 if (stored_player[i].active)
3141 DrawPlayer(&stored_player[i]);
3144 void DrawPlayerField(int x, int y)
3146 if (!IS_PLAYER(x, y))
3149 DrawPlayer(PLAYERINFO(x, y));
3152 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3154 void DrawPlayer(struct PlayerInfo *player)
3156 int jx = player->jx;
3157 int jy = player->jy;
3158 int move_dir = player->MovDir;
3159 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3160 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3161 int last_jx = (player->is_moving ? jx - dx : jx);
3162 int last_jy = (player->is_moving ? jy - dy : jy);
3163 int next_jx = jx + dx;
3164 int next_jy = jy + dy;
3165 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3166 boolean player_is_opaque = FALSE;
3167 int sx = SCREENX(jx), sy = SCREENY(jy);
3168 int sxx = 0, syy = 0;
3169 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3171 int action = ACTION_DEFAULT;
3172 int last_player_graphic = getPlayerGraphic(player, move_dir);
3173 int last_player_frame = player->Frame;
3176 /* GfxElement[][] is set to the element the player is digging or collecting;
3177 remove also for off-screen player if the player is not moving anymore */
3178 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3179 GfxElement[jx][jy] = EL_UNDEFINED;
3181 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3185 if (!IN_LEV_FIELD(jx, jy))
3187 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3188 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3189 printf("DrawPlayerField(): This should never happen!\n");
3194 if (element == EL_EXPLOSION)
3197 action = (player->is_pushing ? ACTION_PUSHING :
3198 player->is_digging ? ACTION_DIGGING :
3199 player->is_collecting ? ACTION_COLLECTING :
3200 player->is_moving ? ACTION_MOVING :
3201 player->is_snapping ? ACTION_SNAPPING :
3202 player->is_dropping ? ACTION_DROPPING :
3203 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3205 if (player->is_waiting)
3206 move_dir = player->dir_waiting;
3208 InitPlayerGfxAnimation(player, action, move_dir);
3210 /* ----------------------------------------------------------------------- */
3211 /* draw things in the field the player is leaving, if needed */
3212 /* ----------------------------------------------------------------------- */
3214 if (player->is_moving)
3216 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3218 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3220 if (last_element == EL_DYNAMITE_ACTIVE ||
3221 last_element == EL_EM_DYNAMITE_ACTIVE ||
3222 last_element == EL_SP_DISK_RED_ACTIVE)
3223 DrawDynamite(last_jx, last_jy);
3225 DrawLevelFieldThruMask(last_jx, last_jy);
3227 else if (last_element == EL_DYNAMITE_ACTIVE ||
3228 last_element == EL_EM_DYNAMITE_ACTIVE ||
3229 last_element == EL_SP_DISK_RED_ACTIVE)
3230 DrawDynamite(last_jx, last_jy);
3232 /* !!! this is not enough to prevent flickering of players which are
3233 moving next to each others without a free tile between them -- this
3234 can only be solved by drawing all players layer by layer (first the
3235 background, then the foreground etc.) !!! => TODO */
3236 else if (!IS_PLAYER(last_jx, last_jy))
3237 DrawLevelField(last_jx, last_jy);
3240 DrawLevelField(last_jx, last_jy);
3243 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3244 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3247 if (!IN_SCR_FIELD(sx, sy))
3250 /* ----------------------------------------------------------------------- */
3251 /* draw things behind the player, if needed */
3252 /* ----------------------------------------------------------------------- */
3255 DrawLevelElement(jx, jy, Back[jx][jy]);
3256 else if (IS_ACTIVE_BOMB(element))
3257 DrawLevelElement(jx, jy, EL_EMPTY);
3260 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3262 int old_element = GfxElement[jx][jy];
3263 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3264 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3266 if (GFX_CRUMBLED(old_element))
3267 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3269 DrawGraphic(sx, sy, old_graphic, frame);
3271 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3272 player_is_opaque = TRUE;
3276 GfxElement[jx][jy] = EL_UNDEFINED;
3278 /* make sure that pushed elements are drawn with correct frame rate */
3279 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3281 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3282 GfxFrame[jx][jy] = player->StepFrame;
3284 DrawLevelField(jx, jy);
3288 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3289 /* ----------------------------------------------------------------------- */
3290 /* draw player himself */
3291 /* ----------------------------------------------------------------------- */
3293 graphic = getPlayerGraphic(player, move_dir);
3295 /* in the case of changed player action or direction, prevent the current
3296 animation frame from being restarted for identical animations */
3297 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3298 player->Frame = last_player_frame;
3300 frame = getGraphicAnimationFrame(graphic, player->Frame);
3304 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3305 sxx = player->GfxPos;
3307 syy = player->GfxPos;
3310 if (player_is_opaque)
3311 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3313 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3315 if (SHIELD_ON(player))
3317 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3318 IMG_SHIELD_NORMAL_ACTIVE);
3319 int frame = getGraphicAnimationFrame(graphic, -1);
3321 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3325 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3328 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3329 sxx = player->GfxPos;
3331 syy = player->GfxPos;
3335 /* ----------------------------------------------------------------------- */
3336 /* draw things the player is pushing, if needed */
3337 /* ----------------------------------------------------------------------- */
3339 if (player->is_pushing && player->is_moving)
3341 int px = SCREENX(jx), py = SCREENY(jy);
3342 int pxx = (TILEX - ABS(sxx)) * dx;
3343 int pyy = (TILEY - ABS(syy)) * dy;
3344 int gfx_frame = GfxFrame[jx][jy];
3350 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3352 element = Feld[next_jx][next_jy];
3353 gfx_frame = GfxFrame[next_jx][next_jy];
3356 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3358 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3359 frame = getGraphicAnimationFrame(graphic, sync_frame);
3361 /* draw background element under pushed element (like the Sokoban field) */
3362 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3364 /* this allows transparent pushing animation over non-black background */
3367 DrawLevelElement(jx, jy, Back[jx][jy]);
3369 DrawLevelElement(jx, jy, EL_EMPTY);
3371 if (Back[next_jx][next_jy])
3372 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3374 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3376 else if (Back[next_jx][next_jy])
3377 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3380 /* do not draw (EM style) pushing animation when pushing is finished */
3381 /* (two-tile animations usually do not contain start and end frame) */
3382 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3383 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3385 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3387 /* masked drawing is needed for EMC style (double) movement graphics */
3388 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3389 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3393 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3394 /* ----------------------------------------------------------------------- */
3395 /* draw player himself */
3396 /* ----------------------------------------------------------------------- */
3398 graphic = getPlayerGraphic(player, move_dir);
3400 /* in the case of changed player action or direction, prevent the current
3401 animation frame from being restarted for identical animations */
3402 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3403 player->Frame = last_player_frame;
3405 frame = getGraphicAnimationFrame(graphic, player->Frame);
3409 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3410 sxx = player->GfxPos;
3412 syy = player->GfxPos;
3415 if (player_is_opaque)
3416 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3418 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3420 if (SHIELD_ON(player))
3422 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3423 IMG_SHIELD_NORMAL_ACTIVE);
3424 int frame = getGraphicAnimationFrame(graphic, -1);
3426 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3430 /* ----------------------------------------------------------------------- */
3431 /* draw things in front of player (active dynamite or dynabombs) */
3432 /* ----------------------------------------------------------------------- */
3434 if (IS_ACTIVE_BOMB(element))
3436 graphic = el2img(element);
3437 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3439 if (game.emulation == EMU_SUPAPLEX)
3440 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3442 DrawGraphicThruMask(sx, sy, graphic, frame);
3445 if (player_is_moving && last_element == EL_EXPLOSION)
3447 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3448 GfxElement[last_jx][last_jy] : EL_EMPTY);
3449 int graphic = el_act2img(element, ACTION_EXPLODING);
3450 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3451 int phase = ExplodePhase[last_jx][last_jy] - 1;
3452 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3455 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3458 /* ----------------------------------------------------------------------- */
3459 /* draw elements the player is just walking/passing through/under */
3460 /* ----------------------------------------------------------------------- */
3462 if (player_is_moving)
3464 /* handle the field the player is leaving ... */
3465 if (IS_ACCESSIBLE_INSIDE(last_element))
3466 DrawLevelField(last_jx, last_jy);
3467 else if (IS_ACCESSIBLE_UNDER(last_element))
3468 DrawLevelFieldThruMask(last_jx, last_jy);
3471 /* do not redraw accessible elements if the player is just pushing them */
3472 if (!player_is_moving || !player->is_pushing)
3474 /* ... and the field the player is entering */
3475 if (IS_ACCESSIBLE_INSIDE(element))
3476 DrawLevelField(jx, jy);
3477 else if (IS_ACCESSIBLE_UNDER(element))
3478 DrawLevelFieldThruMask(jx, jy);
3481 MarkTileDirty(sx, sy);
3484 /* ------------------------------------------------------------------------- */
3486 void WaitForEventToContinue()
3488 boolean still_wait = TRUE;
3490 /* simulate releasing mouse button over last gadget, if still pressed */
3492 HandleGadgets(-1, -1, 0);
3494 button_status = MB_RELEASED;
3508 case EVENT_BUTTONPRESS:
3509 case EVENT_KEYPRESS:
3513 case EVENT_KEYRELEASE:
3514 ClearPlayerAction();
3518 HandleOtherEvents(&event);
3522 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3531 #define MAX_REQUEST_LINES 13
3532 #define MAX_REQUEST_LINE_FONT1_LEN 7
3533 #define MAX_REQUEST_LINE_FONT2_LEN 10
3535 static int RequestHandleEvents(unsigned int req_state)
3537 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3538 local_player->LevelSolved_GameEnd);
3539 int width = request.width;
3540 int height = request.height;
3544 setRequestPosition(&sx, &sy, FALSE);
3546 button_status = MB_RELEASED;
3548 request_gadget_id = -1;
3555 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3557 HandleGameActions();
3559 SetDrawtoField(DRAW_TO_BACKBUFFER);
3561 if (global.use_envelope_request)
3563 /* copy current state of request area to middle of playfield area */
3564 BlitBitmap(bitmap_db_cross, drawto, sx, sy, width, height, sx, sy);
3572 while (NextValidEvent(&event))
3576 case EVENT_BUTTONPRESS:
3577 case EVENT_BUTTONRELEASE:
3578 case EVENT_MOTIONNOTIFY:
3582 if (event.type == EVENT_MOTIONNOTIFY)
3587 motion_status = TRUE;
3588 mx = ((MotionEvent *) &event)->x;
3589 my = ((MotionEvent *) &event)->y;
3593 motion_status = FALSE;
3594 mx = ((ButtonEvent *) &event)->x;
3595 my = ((ButtonEvent *) &event)->y;
3596 if (event.type == EVENT_BUTTONPRESS)
3597 button_status = ((ButtonEvent *) &event)->button;
3599 button_status = MB_RELEASED;
3602 /* this sets 'request_gadget_id' */
3603 HandleGadgets(mx, my, button_status);
3605 switch (request_gadget_id)
3607 case TOOL_CTRL_ID_YES:
3610 case TOOL_CTRL_ID_NO:
3613 case TOOL_CTRL_ID_CONFIRM:
3614 result = TRUE | FALSE;
3617 case TOOL_CTRL_ID_PLAYER_1:
3620 case TOOL_CTRL_ID_PLAYER_2:
3623 case TOOL_CTRL_ID_PLAYER_3:
3626 case TOOL_CTRL_ID_PLAYER_4:
3637 case EVENT_KEYPRESS:
3638 switch (GetEventKey((KeyEvent *)&event, TRUE))
3641 if (req_state & REQ_CONFIRM)
3646 #if defined(TARGET_SDL2)
3653 #if defined(TARGET_SDL2)
3663 if (req_state & REQ_PLAYER)
3667 case EVENT_KEYRELEASE:
3668 ClearPlayerAction();
3672 HandleOtherEvents(&event);
3677 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3679 int joy = AnyJoystick();
3681 if (joy & JOY_BUTTON_1)
3683 else if (joy & JOY_BUTTON_2)
3689 if (global.use_envelope_request)
3691 /* copy back current state of pressed buttons inside request area */
3692 BlitBitmap(drawto, bitmap_db_cross, sx, sy, width, height, sx, sy);
3702 static boolean RequestDoor(char *text, unsigned int req_state)
3704 unsigned int old_door_state;
3705 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3706 int font_nr = FONT_TEXT_2;
3711 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3713 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3714 font_nr = FONT_TEXT_1;
3717 if (game_status == GAME_MODE_PLAYING)
3718 BlitScreenToBitmap(backbuffer);
3720 /* disable deactivated drawing when quick-loading level tape recording */
3721 if (tape.playing && tape.deactivate_display)
3722 TapeDeactivateDisplayOff(TRUE);
3724 SetMouseCursor(CURSOR_DEFAULT);
3726 #if defined(NETWORK_AVALIABLE)
3727 /* pause network game while waiting for request to answer */
3728 if (options.network &&
3729 game_status == GAME_MODE_PLAYING &&
3730 req_state & REQUEST_WAIT_FOR_INPUT)
3731 SendToServer_PausePlaying();
3734 old_door_state = GetDoorState();
3736 /* simulate releasing mouse button over last gadget, if still pressed */
3738 HandleGadgets(-1, -1, 0);
3742 /* draw released gadget before proceeding */
3745 if (old_door_state & DOOR_OPEN_1)
3747 CloseDoor(DOOR_CLOSE_1);
3749 /* save old door content */
3750 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3751 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3754 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3755 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3757 /* clear door drawing field */
3758 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3760 /* force DOOR font inside door area */
3761 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3763 /* write text for request */
3764 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3766 char text_line[max_request_line_len + 1];
3772 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3774 tc = *(text_ptr + tx);
3775 // if (!tc || tc == ' ')
3776 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3780 if ((tc == '?' || tc == '!') && tl == 0)
3790 strncpy(text_line, text_ptr, tl);
3793 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3794 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3795 text_line, font_nr);
3797 text_ptr += tl + (tc == ' ' ? 1 : 0);
3798 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3803 if (req_state & REQ_ASK)
3805 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3806 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3808 else if (req_state & REQ_CONFIRM)
3810 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3812 else if (req_state & REQ_PLAYER)
3814 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3815 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3816 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3817 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3820 /* copy request gadgets to door backbuffer */
3821 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3823 OpenDoor(DOOR_OPEN_1);
3825 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3827 if (game_status == GAME_MODE_PLAYING)
3829 SetPanelBackground();
3830 SetDrawBackgroundMask(REDRAW_DOOR_1);
3834 SetDrawBackgroundMask(REDRAW_FIELD);
3840 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3842 // ---------- handle request buttons ----------
3843 result = RequestHandleEvents(req_state);
3847 if (!(req_state & REQ_STAY_OPEN))
3849 CloseDoor(DOOR_CLOSE_1);
3851 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3852 (req_state & REQ_REOPEN))
3853 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3858 if (game_status == GAME_MODE_PLAYING)
3860 SetPanelBackground();
3861 SetDrawBackgroundMask(REDRAW_DOOR_1);
3865 SetDrawBackgroundMask(REDRAW_FIELD);
3868 #if defined(NETWORK_AVALIABLE)
3869 /* continue network game after request */
3870 if (options.network &&
3871 game_status == GAME_MODE_PLAYING &&
3872 req_state & REQUEST_WAIT_FOR_INPUT)
3873 SendToServer_ContinuePlaying();
3876 /* restore deactivated drawing when quick-loading level tape recording */
3877 if (tape.playing && tape.deactivate_display)
3878 TapeDeactivateDisplayOn();
3883 static boolean RequestEnvelope(char *text, unsigned int req_state)
3887 if (game_status == GAME_MODE_PLAYING)
3888 BlitScreenToBitmap(backbuffer);
3890 /* disable deactivated drawing when quick-loading level tape recording */
3891 if (tape.playing && tape.deactivate_display)
3892 TapeDeactivateDisplayOff(TRUE);
3894 SetMouseCursor(CURSOR_DEFAULT);
3896 #if defined(NETWORK_AVALIABLE)
3897 /* pause network game while waiting for request to answer */
3898 if (options.network &&
3899 game_status == GAME_MODE_PLAYING &&
3900 req_state & REQUEST_WAIT_FOR_INPUT)
3901 SendToServer_PausePlaying();
3904 /* simulate releasing mouse button over last gadget, if still pressed */
3906 HandleGadgets(-1, -1, 0);
3910 // (replace with setting corresponding request background)
3911 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3912 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3914 /* clear door drawing field */
3915 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3917 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3919 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3921 if (game_status == GAME_MODE_PLAYING)
3923 SetPanelBackground();
3924 SetDrawBackgroundMask(REDRAW_DOOR_1);
3928 SetDrawBackgroundMask(REDRAW_FIELD);
3934 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3936 // ---------- handle request buttons ----------
3937 result = RequestHandleEvents(req_state);
3941 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3945 if (game_status == GAME_MODE_PLAYING)
3947 SetPanelBackground();
3948 SetDrawBackgroundMask(REDRAW_DOOR_1);
3952 SetDrawBackgroundMask(REDRAW_FIELD);
3955 #if defined(NETWORK_AVALIABLE)
3956 /* continue network game after request */
3957 if (options.network &&
3958 game_status == GAME_MODE_PLAYING &&
3959 req_state & REQUEST_WAIT_FOR_INPUT)
3960 SendToServer_ContinuePlaying();
3963 /* restore deactivated drawing when quick-loading level tape recording */
3964 if (tape.playing && tape.deactivate_display)
3965 TapeDeactivateDisplayOn();
3970 boolean Request(char *text, unsigned int req_state)
3972 if (global.use_envelope_request)
3973 return RequestEnvelope(text, req_state);
3975 return RequestDoor(text, req_state);
3978 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3980 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3981 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3984 if (dpo1->sort_priority != dpo2->sort_priority)
3985 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3987 compare_result = dpo1->nr - dpo2->nr;
3989 return compare_result;
3992 void InitGraphicCompatibilityInfo_Doors()
3998 struct DoorInfo *door;
4002 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4003 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4005 { -1, -1, -1, NULL }
4007 struct Rect door_rect_list[] =
4009 { DX, DY, DXSIZE, DYSIZE },
4010 { VX, VY, VXSIZE, VYSIZE }
4014 for (i = 0; doors[i].door_token != -1; i++)
4016 int door_token = doors[i].door_token;
4017 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4018 int part_1 = doors[i].part_1;
4019 int part_8 = doors[i].part_8;
4020 int part_2 = part_1 + 1;
4021 int part_3 = part_1 + 2;
4022 struct DoorInfo *door = doors[i].door;
4023 struct Rect *door_rect = &door_rect_list[door_index];
4024 boolean door_gfx_redefined = FALSE;
4026 /* check if any door part graphic definitions have been redefined */
4028 for (j = 0; door_part_controls[j].door_token != -1; j++)
4030 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4031 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4033 if (dpc->door_token == door_token && fi->redefined)
4034 door_gfx_redefined = TRUE;
4037 /* check for old-style door graphic/animation modifications */
4039 if (!door_gfx_redefined)
4041 if (door->anim_mode & ANIM_STATIC_PANEL)
4043 door->panel.step_xoffset = 0;
4044 door->panel.step_yoffset = 0;
4047 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4049 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4050 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4051 int num_door_steps, num_panel_steps;
4053 /* remove door part graphics other than the two default wings */
4055 for (j = 0; door_part_controls[j].door_token != -1; j++)
4057 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4058 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4060 if (dpc->graphic >= part_3 &&
4061 dpc->graphic <= part_8)
4065 /* set graphics and screen positions of the default wings */
4067 g_part_1->width = door_rect->width;
4068 g_part_1->height = door_rect->height;
4069 g_part_2->width = door_rect->width;
4070 g_part_2->height = door_rect->height;
4071 g_part_2->src_x = door_rect->width;
4072 g_part_2->src_y = g_part_1->src_y;
4074 door->part_2.x = door->part_1.x;
4075 door->part_2.y = door->part_1.y;
4077 if (door->width != -1)
4079 g_part_1->width = door->width;
4080 g_part_2->width = door->width;
4082 // special treatment for graphics and screen position of right wing
4083 g_part_2->src_x += door_rect->width - door->width;
4084 door->part_2.x += door_rect->width - door->width;
4087 if (door->height != -1)
4089 g_part_1->height = door->height;
4090 g_part_2->height = door->height;
4092 // special treatment for graphics and screen position of bottom wing
4093 g_part_2->src_y += door_rect->height - door->height;
4094 door->part_2.y += door_rect->height - door->height;
4097 /* set animation delays for the default wings and panels */
4099 door->part_1.step_delay = door->step_delay;
4100 door->part_2.step_delay = door->step_delay;
4101 door->panel.step_delay = door->step_delay;
4103 /* set animation draw order for the default wings */
4105 door->part_1.sort_priority = 2; /* draw left wing over ... */
4106 door->part_2.sort_priority = 1; /* ... right wing */
4108 /* set animation draw offset for the default wings */
4110 if (door->anim_mode & ANIM_HORIZONTAL)
4112 door->part_1.step_xoffset = door->step_offset;
4113 door->part_1.step_yoffset = 0;
4114 door->part_2.step_xoffset = door->step_offset * -1;
4115 door->part_2.step_yoffset = 0;
4117 num_door_steps = g_part_1->width / door->step_offset;
4119 else // ANIM_VERTICAL
4121 door->part_1.step_xoffset = 0;
4122 door->part_1.step_yoffset = door->step_offset;
4123 door->part_2.step_xoffset = 0;
4124 door->part_2.step_yoffset = door->step_offset * -1;
4126 num_door_steps = g_part_1->height / door->step_offset;
4129 /* set animation draw offset for the default panels */
4131 if (door->step_offset > 1)
4133 num_panel_steps = 2 * door_rect->height / door->step_offset;
4134 door->panel.start_step = num_panel_steps - num_door_steps;
4135 door->panel.start_step_closing = door->panel.start_step;
4139 num_panel_steps = door_rect->height / door->step_offset;
4140 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4141 door->panel.start_step_closing = door->panel.start_step;
4142 door->panel.step_delay *= 2;
4153 for (i = 0; door_part_controls[i].door_token != -1; i++)
4155 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4156 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4158 /* initialize "start_step_opening" and "start_step_closing", if needed */
4159 if (dpc->pos->start_step_opening == 0 &&
4160 dpc->pos->start_step_closing == 0)
4162 // dpc->pos->start_step_opening = dpc->pos->start_step;
4163 dpc->pos->start_step_closing = dpc->pos->start_step;
4166 /* fill structure for door part draw order (sorted below) */
4168 dpo->sort_priority = dpc->pos->sort_priority;
4171 /* sort door part controls according to sort_priority and graphic number */
4172 qsort(door_part_order, MAX_DOOR_PARTS,
4173 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4176 unsigned int OpenDoor(unsigned int door_state)
4178 if (door_state & DOOR_COPY_BACK)
4180 if (door_state & DOOR_OPEN_1)
4181 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4182 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4184 if (door_state & DOOR_OPEN_2)
4185 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4186 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4188 door_state &= ~DOOR_COPY_BACK;
4191 return MoveDoor(door_state);
4194 unsigned int CloseDoor(unsigned int door_state)
4196 unsigned int old_door_state = GetDoorState();
4198 if (!(door_state & DOOR_NO_COPY_BACK))
4200 if (old_door_state & DOOR_OPEN_1)
4201 BlitBitmap(backbuffer, bitmap_db_door_1,
4202 DX, DY, DXSIZE, DYSIZE, 0, 0);
4204 if (old_door_state & DOOR_OPEN_2)
4205 BlitBitmap(backbuffer, bitmap_db_door_2,
4206 VX, VY, VXSIZE, VYSIZE, 0, 0);
4208 door_state &= ~DOOR_NO_COPY_BACK;
4211 return MoveDoor(door_state);
4214 unsigned int GetDoorState()
4216 return MoveDoor(DOOR_GET_STATE);
4219 unsigned int SetDoorState(unsigned int door_state)
4221 return MoveDoor(door_state | DOOR_SET_STATE);
4224 int euclid(int a, int b)
4226 return (b ? euclid(b, a % b) : a);
4229 unsigned int MoveDoor(unsigned int door_state)
4231 struct Rect door_rect_list[] =
4233 { DX, DY, DXSIZE, DYSIZE },
4234 { VX, VY, VXSIZE, VYSIZE }
4236 static int door1 = DOOR_CLOSE_1;
4237 static int door2 = DOOR_CLOSE_2;
4238 unsigned int door_delay = 0;
4239 unsigned int door_delay_value;
4242 if (door_state == DOOR_GET_STATE)
4243 return (door1 | door2);
4245 if (door_state & DOOR_SET_STATE)
4247 if (door_state & DOOR_ACTION_1)
4248 door1 = door_state & DOOR_ACTION_1;
4249 if (door_state & DOOR_ACTION_2)
4250 door2 = door_state & DOOR_ACTION_2;
4252 return (door1 | door2);
4255 if (!(door_state & DOOR_FORCE_REDRAW))
4257 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4258 door_state &= ~DOOR_OPEN_1;
4259 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4260 door_state &= ~DOOR_CLOSE_1;
4261 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4262 door_state &= ~DOOR_OPEN_2;
4263 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4264 door_state &= ~DOOR_CLOSE_2;
4267 if (global.autoplay_leveldir)
4269 door_state |= DOOR_NO_DELAY;
4270 door_state &= ~DOOR_CLOSE_ALL;
4273 if (game_status == GAME_MODE_EDITOR)
4274 door_state |= DOOR_NO_DELAY;
4276 if (door_state & DOOR_ACTION)
4278 boolean door_panel_drawn[NUM_DOORS];
4279 boolean panel_has_doors[NUM_DOORS];
4280 boolean door_part_skip[MAX_DOOR_PARTS];
4281 boolean door_part_done[MAX_DOOR_PARTS];
4282 boolean door_part_done_all;
4283 int num_steps[MAX_DOOR_PARTS];
4284 int max_move_delay = 0; // delay for complete animations of all doors
4285 int max_step_delay = 0; // delay (ms) between two animation frames
4286 int num_move_steps = 0; // number of animation steps for all doors
4287 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4288 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4289 int current_move_delay = 0;
4293 for (i = 0; i < NUM_DOORS; i++)
4294 panel_has_doors[i] = FALSE;
4296 for (i = 0; i < MAX_DOOR_PARTS; i++)
4298 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4299 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4300 int door_token = dpc->door_token;
4302 door_part_done[i] = FALSE;
4303 door_part_skip[i] = (!(door_state & door_token) ||
4307 for (i = 0; i < MAX_DOOR_PARTS; i++)
4309 int nr = door_part_order[i].nr;
4310 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4311 struct DoorPartPosInfo *pos = dpc->pos;
4312 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4313 int door_token = dpc->door_token;
4314 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4315 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4316 int step_xoffset = ABS(pos->step_xoffset);
4317 int step_yoffset = ABS(pos->step_yoffset);
4318 int step_delay = pos->step_delay;
4319 int current_door_state = door_state & door_token;
4320 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4321 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4322 boolean part_opening = (is_panel ? door_closing : door_opening);
4323 int start_step = (part_opening ? pos->start_step_opening :
4324 pos->start_step_closing);
4325 float move_xsize = (step_xoffset ? g->width : 0);
4326 float move_ysize = (step_yoffset ? g->height : 0);
4327 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4328 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4329 int move_steps = (move_xsteps && move_ysteps ?
4330 MIN(move_xsteps, move_ysteps) :
4331 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4332 int move_delay = move_steps * step_delay;
4334 if (door_part_skip[nr])
4337 max_move_delay = MAX(max_move_delay, move_delay);
4338 max_step_delay = (max_step_delay == 0 ? step_delay :
4339 euclid(max_step_delay, step_delay));
4340 num_steps[nr] = move_steps;
4344 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4346 panel_has_doors[door_index] = TRUE;
4350 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4352 num_move_steps = max_move_delay / max_step_delay;
4353 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4355 door_delay_value = max_step_delay;
4357 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4359 start = num_move_steps - 1;
4363 /* opening door sound has priority over simultaneously closing door */
4364 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4365 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4366 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4367 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4370 for (k = start; k < num_move_steps; k++)
4372 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4374 door_part_done_all = TRUE;
4376 for (i = 0; i < NUM_DOORS; i++)
4377 door_panel_drawn[i] = FALSE;
4379 for (i = 0; i < MAX_DOOR_PARTS; i++)
4381 int nr = door_part_order[i].nr;
4382 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4383 struct DoorPartPosInfo *pos = dpc->pos;
4384 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4385 int door_token = dpc->door_token;
4386 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4387 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4388 boolean is_panel_and_door_has_closed = FALSE;
4389 struct Rect *door_rect = &door_rect_list[door_index];
4390 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4392 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4393 int current_door_state = door_state & door_token;
4394 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4395 boolean door_closing = !door_opening;
4396 boolean part_opening = (is_panel ? door_closing : door_opening);
4397 boolean part_closing = !part_opening;
4398 int start_step = (part_opening ? pos->start_step_opening :
4399 pos->start_step_closing);
4400 int step_delay = pos->step_delay;
4401 int step_factor = step_delay / max_step_delay;
4402 int k1 = (step_factor ? k / step_factor + 1 : k);
4403 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4404 int kk = MAX(0, k2);
4407 int src_x, src_y, src_xx, src_yy;
4408 int dst_x, dst_y, dst_xx, dst_yy;
4411 if (door_part_skip[nr])
4414 if (!(door_state & door_token))
4422 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4423 int kk_door = MAX(0, k2_door);
4424 int sync_frame = kk_door * door_delay_value;
4425 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4427 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4432 if (!door_panel_drawn[door_index])
4434 ClearRectangle(drawto, door_rect->x, door_rect->y,
4435 door_rect->width, door_rect->height);
4437 door_panel_drawn[door_index] = TRUE;
4440 // draw opening or closing door parts
4442 if (pos->step_xoffset < 0) // door part on right side
4445 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4448 if (dst_xx + width > door_rect->width)
4449 width = door_rect->width - dst_xx;
4451 else // door part on left side
4454 dst_xx = pos->x - kk * pos->step_xoffset;
4458 src_xx = ABS(dst_xx);
4462 width = g->width - src_xx;
4464 if (width > door_rect->width)
4465 width = door_rect->width;
4467 // printf("::: k == %d [%d] \n", k, start_step);
4470 if (pos->step_yoffset < 0) // door part on bottom side
4473 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4476 if (dst_yy + height > door_rect->height)
4477 height = door_rect->height - dst_yy;
4479 else // door part on top side
4482 dst_yy = pos->y - kk * pos->step_yoffset;
4486 src_yy = ABS(dst_yy);
4490 height = g->height - src_yy;
4493 src_x = g_src_x + src_xx;
4494 src_y = g_src_y + src_yy;
4496 dst_x = door_rect->x + dst_xx;
4497 dst_y = door_rect->y + dst_yy;
4499 is_panel_and_door_has_closed =
4502 panel_has_doors[door_index] &&
4503 k >= num_move_steps_doors_only - 1);
4505 if (width >= 0 && width <= g->width &&
4506 height >= 0 && height <= g->height &&
4507 !is_panel_and_door_has_closed)
4509 if (is_panel || !pos->draw_masked)
4510 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4513 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4517 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4519 if ((part_opening && (width < 0 || height < 0)) ||
4520 (part_closing && (width >= g->width && height >= g->height)))
4521 door_part_done[nr] = TRUE;
4523 // continue door part animations, but not panel after door has closed
4524 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4525 door_part_done_all = FALSE;
4528 if (!(door_state & DOOR_NO_DELAY))
4532 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4534 current_move_delay += max_step_delay;
4537 if (door_part_done_all)
4542 if (door_state & DOOR_ACTION_1)
4543 door1 = door_state & DOOR_ACTION_1;
4544 if (door_state & DOOR_ACTION_2)
4545 door2 = door_state & DOOR_ACTION_2;
4547 // draw masked border over door area
4548 DrawMaskedBorder(REDRAW_DOOR_1);
4549 DrawMaskedBorder(REDRAW_DOOR_2);
4551 return (door1 | door2);
4554 static boolean useSpecialEditorDoor()
4556 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4557 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4559 // do not draw special editor door if editor border defined or redefined
4560 if (graphic_info[graphic].bitmap != NULL || redefined)
4563 // do not draw special editor door if global border defined to be empty
4564 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4567 // do not draw special editor door if viewport definitions do not match
4571 EY + EYSIZE != VY + VYSIZE)
4577 void DrawSpecialEditorDoor()
4579 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4580 int top_border_width = gfx1->width;
4581 int top_border_height = gfx1->height;
4582 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4583 int ex = EX - outer_border;
4584 int ey = EY - outer_border;
4585 int vy = VY - outer_border;
4586 int exsize = EXSIZE + 2 * outer_border;
4588 if (!useSpecialEditorDoor())
4591 /* draw bigger level editor toolbox window */
4592 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4593 top_border_width, top_border_height, ex, ey - top_border_height);
4594 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4595 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4597 redraw_mask |= REDRAW_ALL;
4600 void UndrawSpecialEditorDoor()
4602 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4603 int top_border_width = gfx1->width;
4604 int top_border_height = gfx1->height;
4605 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4606 int ex = EX - outer_border;
4607 int ey = EY - outer_border;
4608 int ey_top = ey - top_border_height;
4609 int exsize = EXSIZE + 2 * outer_border;
4610 int eysize = EYSIZE + 2 * outer_border;
4612 if (!useSpecialEditorDoor())
4615 /* draw normal tape recorder window */
4616 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4618 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4619 ex, ey_top, top_border_width, top_border_height,
4621 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4622 ex, ey, exsize, eysize, ex, ey);
4626 // if screen background is set to "[NONE]", clear editor toolbox window
4627 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4628 ClearRectangle(drawto, ex, ey, exsize, eysize);
4631 redraw_mask |= REDRAW_ALL;
4635 /* ---------- new tool button stuff ---------------------------------------- */
4640 struct TextPosInfo *pos;
4643 } toolbutton_info[NUM_TOOL_BUTTONS] =
4646 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
4647 TOOL_CTRL_ID_YES, "yes"
4650 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
4651 TOOL_CTRL_ID_NO, "no"
4654 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
4655 TOOL_CTRL_ID_CONFIRM, "confirm"
4658 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
4659 TOOL_CTRL_ID_PLAYER_1, "player 1"
4662 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
4663 TOOL_CTRL_ID_PLAYER_2, "player 2"
4666 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
4667 TOOL_CTRL_ID_PLAYER_3, "player 3"
4670 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
4671 TOOL_CTRL_ID_PLAYER_4, "player 4"
4675 void CreateToolButtons()
4679 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4681 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4682 struct TextPosInfo *pos = toolbutton_info[i].pos;
4683 struct GadgetInfo *gi;
4684 Bitmap *deco_bitmap = None;
4685 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4686 unsigned int event_mask = GD_EVENT_RELEASED;
4689 int gd_x = gfx->src_x;
4690 int gd_y = gfx->src_y;
4691 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4692 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4695 if (global.use_envelope_request)
4696 setRequestPosition(&dx, &dy, TRUE);
4698 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4700 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4702 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4703 pos->size, &deco_bitmap, &deco_x, &deco_y);
4704 deco_xpos = (gfx->width - pos->size) / 2;
4705 deco_ypos = (gfx->height - pos->size) / 2;
4708 gi = CreateGadget(GDI_CUSTOM_ID, id,
4709 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4710 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4711 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4712 GDI_WIDTH, gfx->width,
4713 GDI_HEIGHT, gfx->height,
4714 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4715 GDI_STATE, GD_BUTTON_UNPRESSED,
4716 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4717 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4718 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4719 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4720 GDI_DECORATION_SIZE, pos->size, pos->size,
4721 GDI_DECORATION_SHIFTING, 1, 1,
4722 GDI_DIRECT_DRAW, FALSE,
4723 GDI_EVENT_MASK, event_mask,
4724 GDI_CALLBACK_ACTION, HandleToolButtons,
4728 Error(ERR_EXIT, "cannot create gadget");
4730 tool_gadget[id] = gi;
4734 void FreeToolButtons()
4738 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4739 FreeGadget(tool_gadget[i]);
4742 static void UnmapToolButtons()
4746 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4747 UnmapGadget(tool_gadget[i]);
4750 static void HandleToolButtons(struct GadgetInfo *gi)
4752 request_gadget_id = gi->custom_id;
4755 static struct Mapping_EM_to_RND_object
4758 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4759 boolean is_backside; /* backside of moving element */
4765 em_object_mapping_list[] =
4768 Xblank, TRUE, FALSE,
4772 Yacid_splash_eB, FALSE, FALSE,
4773 EL_ACID_SPLASH_RIGHT, -1, -1
4776 Yacid_splash_wB, FALSE, FALSE,
4777 EL_ACID_SPLASH_LEFT, -1, -1
4780 #ifdef EM_ENGINE_BAD_ROLL
4782 Xstone_force_e, FALSE, FALSE,
4783 EL_ROCK, -1, MV_BIT_RIGHT
4786 Xstone_force_w, FALSE, FALSE,
4787 EL_ROCK, -1, MV_BIT_LEFT
4790 Xnut_force_e, FALSE, FALSE,
4791 EL_NUT, -1, MV_BIT_RIGHT
4794 Xnut_force_w, FALSE, FALSE,
4795 EL_NUT, -1, MV_BIT_LEFT
4798 Xspring_force_e, FALSE, FALSE,
4799 EL_SPRING, -1, MV_BIT_RIGHT
4802 Xspring_force_w, FALSE, FALSE,
4803 EL_SPRING, -1, MV_BIT_LEFT
4806 Xemerald_force_e, FALSE, FALSE,
4807 EL_EMERALD, -1, MV_BIT_RIGHT
4810 Xemerald_force_w, FALSE, FALSE,
4811 EL_EMERALD, -1, MV_BIT_LEFT
4814 Xdiamond_force_e, FALSE, FALSE,
4815 EL_DIAMOND, -1, MV_BIT_RIGHT
4818 Xdiamond_force_w, FALSE, FALSE,
4819 EL_DIAMOND, -1, MV_BIT_LEFT
4822 Xbomb_force_e, FALSE, FALSE,
4823 EL_BOMB, -1, MV_BIT_RIGHT
4826 Xbomb_force_w, FALSE, FALSE,
4827 EL_BOMB, -1, MV_BIT_LEFT
4829 #endif /* EM_ENGINE_BAD_ROLL */
4832 Xstone, TRUE, FALSE,
4836 Xstone_pause, FALSE, FALSE,
4840 Xstone_fall, FALSE, FALSE,
4844 Ystone_s, FALSE, FALSE,
4845 EL_ROCK, ACTION_FALLING, -1
4848 Ystone_sB, FALSE, TRUE,
4849 EL_ROCK, ACTION_FALLING, -1
4852 Ystone_e, FALSE, FALSE,
4853 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4856 Ystone_eB, FALSE, TRUE,
4857 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4860 Ystone_w, FALSE, FALSE,
4861 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4864 Ystone_wB, FALSE, TRUE,
4865 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4872 Xnut_pause, FALSE, FALSE,
4876 Xnut_fall, FALSE, FALSE,
4880 Ynut_s, FALSE, FALSE,
4881 EL_NUT, ACTION_FALLING, -1
4884 Ynut_sB, FALSE, TRUE,
4885 EL_NUT, ACTION_FALLING, -1
4888 Ynut_e, FALSE, FALSE,
4889 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4892 Ynut_eB, FALSE, TRUE,
4893 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4896 Ynut_w, FALSE, FALSE,
4897 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4900 Ynut_wB, FALSE, TRUE,
4901 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4904 Xbug_n, TRUE, FALSE,
4908 Xbug_e, TRUE, FALSE,
4909 EL_BUG_RIGHT, -1, -1
4912 Xbug_s, TRUE, FALSE,
4916 Xbug_w, TRUE, FALSE,
4920 Xbug_gon, FALSE, FALSE,
4924 Xbug_goe, FALSE, FALSE,
4925 EL_BUG_RIGHT, -1, -1
4928 Xbug_gos, FALSE, FALSE,
4932 Xbug_gow, FALSE, FALSE,
4936 Ybug_n, FALSE, FALSE,
4937 EL_BUG, ACTION_MOVING, MV_BIT_UP
4940 Ybug_nB, FALSE, TRUE,
4941 EL_BUG, ACTION_MOVING, MV_BIT_UP
4944 Ybug_e, FALSE, FALSE,
4945 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4948 Ybug_eB, FALSE, TRUE,
4949 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4952 Ybug_s, FALSE, FALSE,
4953 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4956 Ybug_sB, FALSE, TRUE,
4957 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4960 Ybug_w, FALSE, FALSE,
4961 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4964 Ybug_wB, FALSE, TRUE,
4965 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4968 Ybug_w_n, FALSE, FALSE,
4969 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4972 Ybug_n_e, FALSE, FALSE,
4973 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4976 Ybug_e_s, FALSE, FALSE,
4977 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4980 Ybug_s_w, FALSE, FALSE,
4981 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4984 Ybug_e_n, FALSE, FALSE,
4985 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4988 Ybug_s_e, FALSE, FALSE,
4989 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4992 Ybug_w_s, FALSE, FALSE,
4993 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4996 Ybug_n_w, FALSE, FALSE,
4997 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5000 Ybug_stone, FALSE, FALSE,
5001 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5004 Ybug_spring, FALSE, FALSE,
5005 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5008 Xtank_n, TRUE, FALSE,
5009 EL_SPACESHIP_UP, -1, -1
5012 Xtank_e, TRUE, FALSE,
5013 EL_SPACESHIP_RIGHT, -1, -1
5016 Xtank_s, TRUE, FALSE,
5017 EL_SPACESHIP_DOWN, -1, -1
5020 Xtank_w, TRUE, FALSE,
5021 EL_SPACESHIP_LEFT, -1, -1
5024 Xtank_gon, FALSE, FALSE,
5025 EL_SPACESHIP_UP, -1, -1
5028 Xtank_goe, FALSE, FALSE,
5029 EL_SPACESHIP_RIGHT, -1, -1
5032 Xtank_gos, FALSE, FALSE,
5033 EL_SPACESHIP_DOWN, -1, -1
5036 Xtank_gow, FALSE, FALSE,
5037 EL_SPACESHIP_LEFT, -1, -1
5040 Ytank_n, FALSE, FALSE,
5041 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5044 Ytank_nB, FALSE, TRUE,
5045 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5048 Ytank_e, FALSE, FALSE,
5049 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5052 Ytank_eB, FALSE, TRUE,
5053 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5056 Ytank_s, FALSE, FALSE,
5057 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5060 Ytank_sB, FALSE, TRUE,
5061 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5064 Ytank_w, FALSE, FALSE,
5065 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5068 Ytank_wB, FALSE, TRUE,
5069 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5072 Ytank_w_n, FALSE, FALSE,
5073 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5076 Ytank_n_e, FALSE, FALSE,
5077 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5080 Ytank_e_s, FALSE, FALSE,
5081 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5084 Ytank_s_w, FALSE, FALSE,
5085 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5088 Ytank_e_n, FALSE, FALSE,
5089 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5092 Ytank_s_e, FALSE, FALSE,
5093 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5096 Ytank_w_s, FALSE, FALSE,
5097 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5100 Ytank_n_w, FALSE, FALSE,
5101 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5104 Ytank_stone, FALSE, FALSE,
5105 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5108 Ytank_spring, FALSE, FALSE,
5109 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5112 Xandroid, TRUE, FALSE,
5113 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5116 Xandroid_1_n, FALSE, FALSE,
5117 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5120 Xandroid_2_n, FALSE, FALSE,
5121 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5124 Xandroid_1_e, FALSE, FALSE,
5125 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5128 Xandroid_2_e, FALSE, FALSE,
5129 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5132 Xandroid_1_w, FALSE, FALSE,
5133 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5136 Xandroid_2_w, FALSE, FALSE,
5137 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5140 Xandroid_1_s, FALSE, FALSE,
5141 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5144 Xandroid_2_s, FALSE, FALSE,
5145 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5148 Yandroid_n, FALSE, FALSE,
5149 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5152 Yandroid_nB, FALSE, TRUE,
5153 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5156 Yandroid_ne, FALSE, FALSE,
5157 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5160 Yandroid_neB, FALSE, TRUE,
5161 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5164 Yandroid_e, FALSE, FALSE,
5165 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5168 Yandroid_eB, FALSE, TRUE,
5169 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5172 Yandroid_se, FALSE, FALSE,
5173 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5176 Yandroid_seB, FALSE, TRUE,
5177 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5180 Yandroid_s, FALSE, FALSE,
5181 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5184 Yandroid_sB, FALSE, TRUE,
5185 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5188 Yandroid_sw, FALSE, FALSE,
5189 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5192 Yandroid_swB, FALSE, TRUE,
5193 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5196 Yandroid_w, FALSE, FALSE,
5197 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5200 Yandroid_wB, FALSE, TRUE,
5201 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5204 Yandroid_nw, FALSE, FALSE,
5205 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5208 Yandroid_nwB, FALSE, TRUE,
5209 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5212 Xspring, TRUE, FALSE,
5216 Xspring_pause, FALSE, FALSE,
5220 Xspring_e, FALSE, FALSE,
5224 Xspring_w, FALSE, FALSE,
5228 Xspring_fall, FALSE, FALSE,
5232 Yspring_s, FALSE, FALSE,
5233 EL_SPRING, ACTION_FALLING, -1
5236 Yspring_sB, FALSE, TRUE,
5237 EL_SPRING, ACTION_FALLING, -1
5240 Yspring_e, FALSE, FALSE,
5241 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5244 Yspring_eB, FALSE, TRUE,
5245 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5248 Yspring_w, FALSE, FALSE,
5249 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5252 Yspring_wB, FALSE, TRUE,
5253 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5256 Yspring_kill_e, FALSE, FALSE,
5257 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5260 Yspring_kill_eB, FALSE, TRUE,
5261 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5264 Yspring_kill_w, FALSE, FALSE,
5265 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5268 Yspring_kill_wB, FALSE, TRUE,
5269 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5272 Xeater_n, TRUE, FALSE,
5273 EL_YAMYAM_UP, -1, -1
5276 Xeater_e, TRUE, FALSE,
5277 EL_YAMYAM_RIGHT, -1, -1
5280 Xeater_w, TRUE, FALSE,
5281 EL_YAMYAM_LEFT, -1, -1
5284 Xeater_s, TRUE, FALSE,
5285 EL_YAMYAM_DOWN, -1, -1
5288 Yeater_n, FALSE, FALSE,
5289 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5292 Yeater_nB, FALSE, TRUE,
5293 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5296 Yeater_e, FALSE, FALSE,
5297 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5300 Yeater_eB, FALSE, TRUE,
5301 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5304 Yeater_s, FALSE, FALSE,
5305 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5308 Yeater_sB, FALSE, TRUE,
5309 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5312 Yeater_w, FALSE, FALSE,
5313 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5316 Yeater_wB, FALSE, TRUE,
5317 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5320 Yeater_stone, FALSE, FALSE,
5321 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5324 Yeater_spring, FALSE, FALSE,
5325 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5328 Xalien, TRUE, FALSE,
5332 Xalien_pause, FALSE, FALSE,
5336 Yalien_n, FALSE, FALSE,
5337 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5340 Yalien_nB, FALSE, TRUE,
5341 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5344 Yalien_e, FALSE, FALSE,
5345 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5348 Yalien_eB, FALSE, TRUE,
5349 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5352 Yalien_s, FALSE, FALSE,
5353 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5356 Yalien_sB, FALSE, TRUE,
5357 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5360 Yalien_w, FALSE, FALSE,
5361 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5364 Yalien_wB, FALSE, TRUE,
5365 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5368 Yalien_stone, FALSE, FALSE,
5369 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5372 Yalien_spring, FALSE, FALSE,
5373 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5376 Xemerald, TRUE, FALSE,
5380 Xemerald_pause, FALSE, FALSE,
5384 Xemerald_fall, FALSE, FALSE,
5388 Xemerald_shine, FALSE, FALSE,
5389 EL_EMERALD, ACTION_TWINKLING, -1
5392 Yemerald_s, FALSE, FALSE,
5393 EL_EMERALD, ACTION_FALLING, -1
5396 Yemerald_sB, FALSE, TRUE,
5397 EL_EMERALD, ACTION_FALLING, -1
5400 Yemerald_e, FALSE, FALSE,
5401 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5404 Yemerald_eB, FALSE, TRUE,
5405 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5408 Yemerald_w, FALSE, FALSE,
5409 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5412 Yemerald_wB, FALSE, TRUE,
5413 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5416 Yemerald_eat, FALSE, FALSE,
5417 EL_EMERALD, ACTION_COLLECTING, -1
5420 Yemerald_stone, FALSE, FALSE,
5421 EL_NUT, ACTION_BREAKING, -1
5424 Xdiamond, TRUE, FALSE,
5428 Xdiamond_pause, FALSE, FALSE,
5432 Xdiamond_fall, FALSE, FALSE,
5436 Xdiamond_shine, FALSE, FALSE,
5437 EL_DIAMOND, ACTION_TWINKLING, -1
5440 Ydiamond_s, FALSE, FALSE,
5441 EL_DIAMOND, ACTION_FALLING, -1
5444 Ydiamond_sB, FALSE, TRUE,
5445 EL_DIAMOND, ACTION_FALLING, -1
5448 Ydiamond_e, FALSE, FALSE,
5449 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5452 Ydiamond_eB, FALSE, TRUE,
5453 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5456 Ydiamond_w, FALSE, FALSE,
5457 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5460 Ydiamond_wB, FALSE, TRUE,
5461 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5464 Ydiamond_eat, FALSE, FALSE,
5465 EL_DIAMOND, ACTION_COLLECTING, -1
5468 Ydiamond_stone, FALSE, FALSE,
5469 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5472 Xdrip_fall, TRUE, FALSE,
5473 EL_AMOEBA_DROP, -1, -1
5476 Xdrip_stretch, FALSE, FALSE,
5477 EL_AMOEBA_DROP, ACTION_FALLING, -1
5480 Xdrip_stretchB, FALSE, TRUE,
5481 EL_AMOEBA_DROP, ACTION_FALLING, -1
5484 Xdrip_eat, FALSE, FALSE,
5485 EL_AMOEBA_DROP, ACTION_GROWING, -1
5488 Ydrip_s1, FALSE, FALSE,
5489 EL_AMOEBA_DROP, ACTION_FALLING, -1
5492 Ydrip_s1B, FALSE, TRUE,
5493 EL_AMOEBA_DROP, ACTION_FALLING, -1
5496 Ydrip_s2, FALSE, FALSE,
5497 EL_AMOEBA_DROP, ACTION_FALLING, -1
5500 Ydrip_s2B, FALSE, TRUE,
5501 EL_AMOEBA_DROP, ACTION_FALLING, -1
5508 Xbomb_pause, FALSE, FALSE,
5512 Xbomb_fall, FALSE, FALSE,
5516 Ybomb_s, FALSE, FALSE,
5517 EL_BOMB, ACTION_FALLING, -1
5520 Ybomb_sB, FALSE, TRUE,
5521 EL_BOMB, ACTION_FALLING, -1
5524 Ybomb_e, FALSE, FALSE,
5525 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5528 Ybomb_eB, FALSE, TRUE,
5529 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5532 Ybomb_w, FALSE, FALSE,
5533 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5536 Ybomb_wB, FALSE, TRUE,
5537 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5540 Ybomb_eat, FALSE, FALSE,
5541 EL_BOMB, ACTION_ACTIVATING, -1
5544 Xballoon, TRUE, FALSE,
5548 Yballoon_n, FALSE, FALSE,
5549 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5552 Yballoon_nB, FALSE, TRUE,
5553 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5556 Yballoon_e, FALSE, FALSE,
5557 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5560 Yballoon_eB, FALSE, TRUE,
5561 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5564 Yballoon_s, FALSE, FALSE,
5565 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5568 Yballoon_sB, FALSE, TRUE,
5569 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5572 Yballoon_w, FALSE, FALSE,
5573 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5576 Yballoon_wB, FALSE, TRUE,
5577 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5580 Xgrass, TRUE, FALSE,
5581 EL_EMC_GRASS, -1, -1
5584 Ygrass_nB, FALSE, FALSE,
5585 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5588 Ygrass_eB, FALSE, FALSE,
5589 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5592 Ygrass_sB, FALSE, FALSE,
5593 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5596 Ygrass_wB, FALSE, FALSE,
5597 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5604 Ydirt_nB, FALSE, FALSE,
5605 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5608 Ydirt_eB, FALSE, FALSE,
5609 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5612 Ydirt_sB, FALSE, FALSE,
5613 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5616 Ydirt_wB, FALSE, FALSE,
5617 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5620 Xacid_ne, TRUE, FALSE,
5621 EL_ACID_POOL_TOPRIGHT, -1, -1
5624 Xacid_se, TRUE, FALSE,
5625 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5628 Xacid_s, TRUE, FALSE,
5629 EL_ACID_POOL_BOTTOM, -1, -1
5632 Xacid_sw, TRUE, FALSE,
5633 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5636 Xacid_nw, TRUE, FALSE,
5637 EL_ACID_POOL_TOPLEFT, -1, -1
5640 Xacid_1, TRUE, FALSE,
5644 Xacid_2, FALSE, FALSE,
5648 Xacid_3, FALSE, FALSE,
5652 Xacid_4, FALSE, FALSE,
5656 Xacid_5, FALSE, FALSE,
5660 Xacid_6, FALSE, FALSE,
5664 Xacid_7, FALSE, FALSE,
5668 Xacid_8, FALSE, FALSE,
5672 Xball_1, TRUE, FALSE,
5673 EL_EMC_MAGIC_BALL, -1, -1
5676 Xball_1B, FALSE, FALSE,
5677 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5680 Xball_2, FALSE, FALSE,
5681 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5684 Xball_2B, FALSE, FALSE,
5685 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5688 Yball_eat, FALSE, FALSE,
5689 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5692 Ykey_1_eat, FALSE, FALSE,
5693 EL_EM_KEY_1, ACTION_COLLECTING, -1
5696 Ykey_2_eat, FALSE, FALSE,
5697 EL_EM_KEY_2, ACTION_COLLECTING, -1
5700 Ykey_3_eat, FALSE, FALSE,
5701 EL_EM_KEY_3, ACTION_COLLECTING, -1
5704 Ykey_4_eat, FALSE, FALSE,
5705 EL_EM_KEY_4, ACTION_COLLECTING, -1
5708 Ykey_5_eat, FALSE, FALSE,
5709 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5712 Ykey_6_eat, FALSE, FALSE,
5713 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5716 Ykey_7_eat, FALSE, FALSE,
5717 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5720 Ykey_8_eat, FALSE, FALSE,
5721 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5724 Ylenses_eat, FALSE, FALSE,
5725 EL_EMC_LENSES, ACTION_COLLECTING, -1
5728 Ymagnify_eat, FALSE, FALSE,
5729 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5732 Ygrass_eat, FALSE, FALSE,
5733 EL_EMC_GRASS, ACTION_SNAPPING, -1
5736 Ydirt_eat, FALSE, FALSE,
5737 EL_SAND, ACTION_SNAPPING, -1
5740 Xgrow_ns, TRUE, FALSE,
5741 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5744 Ygrow_ns_eat, FALSE, FALSE,
5745 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5748 Xgrow_ew, TRUE, FALSE,
5749 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5752 Ygrow_ew_eat, FALSE, FALSE,
5753 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5756 Xwonderwall, TRUE, FALSE,
5757 EL_MAGIC_WALL, -1, -1
5760 XwonderwallB, FALSE, FALSE,
5761 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5764 Xamoeba_1, TRUE, FALSE,
5765 EL_AMOEBA_DRY, ACTION_OTHER, -1
5768 Xamoeba_2, FALSE, FALSE,
5769 EL_AMOEBA_DRY, ACTION_OTHER, -1
5772 Xamoeba_3, FALSE, FALSE,
5773 EL_AMOEBA_DRY, ACTION_OTHER, -1
5776 Xamoeba_4, FALSE, FALSE,
5777 EL_AMOEBA_DRY, ACTION_OTHER, -1
5780 Xamoeba_5, TRUE, FALSE,
5781 EL_AMOEBA_WET, ACTION_OTHER, -1
5784 Xamoeba_6, FALSE, FALSE,
5785 EL_AMOEBA_WET, ACTION_OTHER, -1
5788 Xamoeba_7, FALSE, FALSE,
5789 EL_AMOEBA_WET, ACTION_OTHER, -1
5792 Xamoeba_8, FALSE, FALSE,
5793 EL_AMOEBA_WET, ACTION_OTHER, -1
5796 Xdoor_1, TRUE, FALSE,
5797 EL_EM_GATE_1, -1, -1
5800 Xdoor_2, TRUE, FALSE,
5801 EL_EM_GATE_2, -1, -1
5804 Xdoor_3, TRUE, FALSE,
5805 EL_EM_GATE_3, -1, -1
5808 Xdoor_4, TRUE, FALSE,
5809 EL_EM_GATE_4, -1, -1
5812 Xdoor_5, TRUE, FALSE,
5813 EL_EMC_GATE_5, -1, -1
5816 Xdoor_6, TRUE, FALSE,
5817 EL_EMC_GATE_6, -1, -1
5820 Xdoor_7, TRUE, FALSE,
5821 EL_EMC_GATE_7, -1, -1
5824 Xdoor_8, TRUE, FALSE,
5825 EL_EMC_GATE_8, -1, -1
5828 Xkey_1, TRUE, FALSE,
5832 Xkey_2, TRUE, FALSE,
5836 Xkey_3, TRUE, FALSE,
5840 Xkey_4, TRUE, FALSE,
5844 Xkey_5, TRUE, FALSE,
5845 EL_EMC_KEY_5, -1, -1
5848 Xkey_6, TRUE, FALSE,
5849 EL_EMC_KEY_6, -1, -1
5852 Xkey_7, TRUE, FALSE,
5853 EL_EMC_KEY_7, -1, -1
5856 Xkey_8, TRUE, FALSE,
5857 EL_EMC_KEY_8, -1, -1
5860 Xwind_n, TRUE, FALSE,
5861 EL_BALLOON_SWITCH_UP, -1, -1
5864 Xwind_e, TRUE, FALSE,
5865 EL_BALLOON_SWITCH_RIGHT, -1, -1
5868 Xwind_s, TRUE, FALSE,
5869 EL_BALLOON_SWITCH_DOWN, -1, -1
5872 Xwind_w, TRUE, FALSE,
5873 EL_BALLOON_SWITCH_LEFT, -1, -1
5876 Xwind_nesw, TRUE, FALSE,
5877 EL_BALLOON_SWITCH_ANY, -1, -1
5880 Xwind_stop, TRUE, FALSE,
5881 EL_BALLOON_SWITCH_NONE, -1, -1
5885 EL_EM_EXIT_CLOSED, -1, -1
5888 Xexit_1, TRUE, FALSE,
5889 EL_EM_EXIT_OPEN, -1, -1
5892 Xexit_2, FALSE, FALSE,
5893 EL_EM_EXIT_OPEN, -1, -1
5896 Xexit_3, FALSE, FALSE,
5897 EL_EM_EXIT_OPEN, -1, -1
5900 Xdynamite, TRUE, FALSE,
5901 EL_EM_DYNAMITE, -1, -1
5904 Ydynamite_eat, FALSE, FALSE,
5905 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5908 Xdynamite_1, TRUE, FALSE,
5909 EL_EM_DYNAMITE_ACTIVE, -1, -1
5912 Xdynamite_2, FALSE, FALSE,
5913 EL_EM_DYNAMITE_ACTIVE, -1, -1
5916 Xdynamite_3, FALSE, FALSE,
5917 EL_EM_DYNAMITE_ACTIVE, -1, -1
5920 Xdynamite_4, FALSE, FALSE,
5921 EL_EM_DYNAMITE_ACTIVE, -1, -1
5924 Xbumper, TRUE, FALSE,
5925 EL_EMC_SPRING_BUMPER, -1, -1
5928 XbumperB, FALSE, FALSE,
5929 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5932 Xwheel, TRUE, FALSE,
5933 EL_ROBOT_WHEEL, -1, -1
5936 XwheelB, FALSE, FALSE,
5937 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5940 Xswitch, TRUE, FALSE,
5941 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5944 XswitchB, FALSE, FALSE,
5945 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5949 EL_QUICKSAND_EMPTY, -1, -1
5952 Xsand_stone, TRUE, FALSE,
5953 EL_QUICKSAND_FULL, -1, -1
5956 Xsand_stonein_1, FALSE, TRUE,
5957 EL_ROCK, ACTION_FILLING, -1
5960 Xsand_stonein_2, FALSE, TRUE,
5961 EL_ROCK, ACTION_FILLING, -1
5964 Xsand_stonein_3, FALSE, TRUE,
5965 EL_ROCK, ACTION_FILLING, -1
5968 Xsand_stonein_4, FALSE, TRUE,
5969 EL_ROCK, ACTION_FILLING, -1
5972 Xsand_stonesand_1, FALSE, FALSE,
5973 EL_QUICKSAND_EMPTYING, -1, -1
5976 Xsand_stonesand_2, FALSE, FALSE,
5977 EL_QUICKSAND_EMPTYING, -1, -1
5980 Xsand_stonesand_3, FALSE, FALSE,
5981 EL_QUICKSAND_EMPTYING, -1, -1
5984 Xsand_stonesand_4, FALSE, FALSE,
5985 EL_QUICKSAND_EMPTYING, -1, -1
5988 Xsand_stonesand_quickout_1, FALSE, FALSE,
5989 EL_QUICKSAND_EMPTYING, -1, -1
5992 Xsand_stonesand_quickout_2, FALSE, FALSE,
5993 EL_QUICKSAND_EMPTYING, -1, -1
5996 Xsand_stoneout_1, FALSE, FALSE,
5997 EL_ROCK, ACTION_EMPTYING, -1
6000 Xsand_stoneout_2, FALSE, FALSE,
6001 EL_ROCK, ACTION_EMPTYING, -1
6004 Xsand_sandstone_1, FALSE, FALSE,
6005 EL_QUICKSAND_FILLING, -1, -1
6008 Xsand_sandstone_2, FALSE, FALSE,
6009 EL_QUICKSAND_FILLING, -1, -1
6012 Xsand_sandstone_3, FALSE, FALSE,
6013 EL_QUICKSAND_FILLING, -1, -1
6016 Xsand_sandstone_4, FALSE, FALSE,
6017 EL_QUICKSAND_FILLING, -1, -1
6020 Xplant, TRUE, FALSE,
6021 EL_EMC_PLANT, -1, -1
6024 Yplant, FALSE, FALSE,
6025 EL_EMC_PLANT, -1, -1
6028 Xlenses, TRUE, FALSE,
6029 EL_EMC_LENSES, -1, -1
6032 Xmagnify, TRUE, FALSE,
6033 EL_EMC_MAGNIFIER, -1, -1
6036 Xdripper, TRUE, FALSE,
6037 EL_EMC_DRIPPER, -1, -1
6040 XdripperB, FALSE, FALSE,
6041 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6044 Xfake_blank, TRUE, FALSE,
6045 EL_INVISIBLE_WALL, -1, -1
6048 Xfake_blankB, FALSE, FALSE,
6049 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6052 Xfake_grass, TRUE, FALSE,
6053 EL_EMC_FAKE_GRASS, -1, -1
6056 Xfake_grassB, FALSE, FALSE,
6057 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6060 Xfake_door_1, TRUE, FALSE,
6061 EL_EM_GATE_1_GRAY, -1, -1
6064 Xfake_door_2, TRUE, FALSE,
6065 EL_EM_GATE_2_GRAY, -1, -1
6068 Xfake_door_3, TRUE, FALSE,
6069 EL_EM_GATE_3_GRAY, -1, -1
6072 Xfake_door_4, TRUE, FALSE,
6073 EL_EM_GATE_4_GRAY, -1, -1
6076 Xfake_door_5, TRUE, FALSE,
6077 EL_EMC_GATE_5_GRAY, -1, -1
6080 Xfake_door_6, TRUE, FALSE,
6081 EL_EMC_GATE_6_GRAY, -1, -1
6084 Xfake_door_7, TRUE, FALSE,
6085 EL_EMC_GATE_7_GRAY, -1, -1
6088 Xfake_door_8, TRUE, FALSE,
6089 EL_EMC_GATE_8_GRAY, -1, -1
6092 Xfake_acid_1, TRUE, FALSE,
6093 EL_EMC_FAKE_ACID, -1, -1
6096 Xfake_acid_2, FALSE, FALSE,
6097 EL_EMC_FAKE_ACID, -1, -1
6100 Xfake_acid_3, FALSE, FALSE,
6101 EL_EMC_FAKE_ACID, -1, -1
6104 Xfake_acid_4, FALSE, FALSE,
6105 EL_EMC_FAKE_ACID, -1, -1
6108 Xfake_acid_5, FALSE, FALSE,
6109 EL_EMC_FAKE_ACID, -1, -1
6112 Xfake_acid_6, FALSE, FALSE,
6113 EL_EMC_FAKE_ACID, -1, -1
6116 Xfake_acid_7, FALSE, FALSE,
6117 EL_EMC_FAKE_ACID, -1, -1
6120 Xfake_acid_8, FALSE, FALSE,
6121 EL_EMC_FAKE_ACID, -1, -1
6124 Xsteel_1, TRUE, FALSE,
6125 EL_STEELWALL, -1, -1
6128 Xsteel_2, TRUE, FALSE,
6129 EL_EMC_STEELWALL_2, -1, -1
6132 Xsteel_3, TRUE, FALSE,
6133 EL_EMC_STEELWALL_3, -1, -1
6136 Xsteel_4, TRUE, FALSE,
6137 EL_EMC_STEELWALL_4, -1, -1
6140 Xwall_1, TRUE, FALSE,
6144 Xwall_2, TRUE, FALSE,
6145 EL_EMC_WALL_14, -1, -1
6148 Xwall_3, TRUE, FALSE,
6149 EL_EMC_WALL_15, -1, -1
6152 Xwall_4, TRUE, FALSE,
6153 EL_EMC_WALL_16, -1, -1
6156 Xround_wall_1, TRUE, FALSE,
6157 EL_WALL_SLIPPERY, -1, -1
6160 Xround_wall_2, TRUE, FALSE,
6161 EL_EMC_WALL_SLIPPERY_2, -1, -1
6164 Xround_wall_3, TRUE, FALSE,
6165 EL_EMC_WALL_SLIPPERY_3, -1, -1
6168 Xround_wall_4, TRUE, FALSE,
6169 EL_EMC_WALL_SLIPPERY_4, -1, -1
6172 Xdecor_1, TRUE, FALSE,
6173 EL_EMC_WALL_8, -1, -1
6176 Xdecor_2, TRUE, FALSE,
6177 EL_EMC_WALL_6, -1, -1
6180 Xdecor_3, TRUE, FALSE,
6181 EL_EMC_WALL_4, -1, -1
6184 Xdecor_4, TRUE, FALSE,
6185 EL_EMC_WALL_7, -1, -1
6188 Xdecor_5, TRUE, FALSE,
6189 EL_EMC_WALL_5, -1, -1
6192 Xdecor_6, TRUE, FALSE,
6193 EL_EMC_WALL_9, -1, -1
6196 Xdecor_7, TRUE, FALSE,
6197 EL_EMC_WALL_10, -1, -1
6200 Xdecor_8, TRUE, FALSE,
6201 EL_EMC_WALL_1, -1, -1
6204 Xdecor_9, TRUE, FALSE,
6205 EL_EMC_WALL_2, -1, -1
6208 Xdecor_10, TRUE, FALSE,
6209 EL_EMC_WALL_3, -1, -1
6212 Xdecor_11, TRUE, FALSE,
6213 EL_EMC_WALL_11, -1, -1
6216 Xdecor_12, TRUE, FALSE,
6217 EL_EMC_WALL_12, -1, -1
6220 Xalpha_0, TRUE, FALSE,
6221 EL_CHAR('0'), -1, -1
6224 Xalpha_1, TRUE, FALSE,
6225 EL_CHAR('1'), -1, -1
6228 Xalpha_2, TRUE, FALSE,
6229 EL_CHAR('2'), -1, -1
6232 Xalpha_3, TRUE, FALSE,
6233 EL_CHAR('3'), -1, -1
6236 Xalpha_4, TRUE, FALSE,
6237 EL_CHAR('4'), -1, -1
6240 Xalpha_5, TRUE, FALSE,
6241 EL_CHAR('5'), -1, -1
6244 Xalpha_6, TRUE, FALSE,
6245 EL_CHAR('6'), -1, -1
6248 Xalpha_7, TRUE, FALSE,
6249 EL_CHAR('7'), -1, -1
6252 Xalpha_8, TRUE, FALSE,
6253 EL_CHAR('8'), -1, -1
6256 Xalpha_9, TRUE, FALSE,
6257 EL_CHAR('9'), -1, -1
6260 Xalpha_excla, TRUE, FALSE,
6261 EL_CHAR('!'), -1, -1
6264 Xalpha_quote, TRUE, FALSE,
6265 EL_CHAR('"'), -1, -1
6268 Xalpha_comma, TRUE, FALSE,
6269 EL_CHAR(','), -1, -1
6272 Xalpha_minus, TRUE, FALSE,
6273 EL_CHAR('-'), -1, -1
6276 Xalpha_perio, TRUE, FALSE,
6277 EL_CHAR('.'), -1, -1
6280 Xalpha_colon, TRUE, FALSE,
6281 EL_CHAR(':'), -1, -1
6284 Xalpha_quest, TRUE, FALSE,
6285 EL_CHAR('?'), -1, -1
6288 Xalpha_a, TRUE, FALSE,
6289 EL_CHAR('A'), -1, -1
6292 Xalpha_b, TRUE, FALSE,
6293 EL_CHAR('B'), -1, -1
6296 Xalpha_c, TRUE, FALSE,
6297 EL_CHAR('C'), -1, -1
6300 Xalpha_d, TRUE, FALSE,
6301 EL_CHAR('D'), -1, -1
6304 Xalpha_e, TRUE, FALSE,
6305 EL_CHAR('E'), -1, -1
6308 Xalpha_f, TRUE, FALSE,
6309 EL_CHAR('F'), -1, -1
6312 Xalpha_g, TRUE, FALSE,
6313 EL_CHAR('G'), -1, -1
6316 Xalpha_h, TRUE, FALSE,
6317 EL_CHAR('H'), -1, -1
6320 Xalpha_i, TRUE, FALSE,
6321 EL_CHAR('I'), -1, -1
6324 Xalpha_j, TRUE, FALSE,
6325 EL_CHAR('J'), -1, -1
6328 Xalpha_k, TRUE, FALSE,
6329 EL_CHAR('K'), -1, -1
6332 Xalpha_l, TRUE, FALSE,
6333 EL_CHAR('L'), -1, -1
6336 Xalpha_m, TRUE, FALSE,
6337 EL_CHAR('M'), -1, -1
6340 Xalpha_n, TRUE, FALSE,
6341 EL_CHAR('N'), -1, -1
6344 Xalpha_o, TRUE, FALSE,
6345 EL_CHAR('O'), -1, -1
6348 Xalpha_p, TRUE, FALSE,
6349 EL_CHAR('P'), -1, -1
6352 Xalpha_q, TRUE, FALSE,
6353 EL_CHAR('Q'), -1, -1
6356 Xalpha_r, TRUE, FALSE,
6357 EL_CHAR('R'), -1, -1
6360 Xalpha_s, TRUE, FALSE,
6361 EL_CHAR('S'), -1, -1
6364 Xalpha_t, TRUE, FALSE,
6365 EL_CHAR('T'), -1, -1
6368 Xalpha_u, TRUE, FALSE,
6369 EL_CHAR('U'), -1, -1
6372 Xalpha_v, TRUE, FALSE,
6373 EL_CHAR('V'), -1, -1
6376 Xalpha_w, TRUE, FALSE,
6377 EL_CHAR('W'), -1, -1
6380 Xalpha_x, TRUE, FALSE,
6381 EL_CHAR('X'), -1, -1
6384 Xalpha_y, TRUE, FALSE,
6385 EL_CHAR('Y'), -1, -1
6388 Xalpha_z, TRUE, FALSE,
6389 EL_CHAR('Z'), -1, -1
6392 Xalpha_arrow_e, TRUE, FALSE,
6393 EL_CHAR('>'), -1, -1
6396 Xalpha_arrow_w, TRUE, FALSE,
6397 EL_CHAR('<'), -1, -1
6400 Xalpha_copyr, TRUE, FALSE,
6401 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6405 Xboom_bug, FALSE, FALSE,
6406 EL_BUG, ACTION_EXPLODING, -1
6409 Xboom_bomb, FALSE, FALSE,
6410 EL_BOMB, ACTION_EXPLODING, -1
6413 Xboom_android, FALSE, FALSE,
6414 EL_EMC_ANDROID, ACTION_OTHER, -1
6417 Xboom_1, FALSE, FALSE,
6418 EL_DEFAULT, ACTION_EXPLODING, -1
6421 Xboom_2, FALSE, FALSE,
6422 EL_DEFAULT, ACTION_EXPLODING, -1
6425 Znormal, FALSE, FALSE,
6429 Zdynamite, FALSE, FALSE,
6433 Zplayer, FALSE, FALSE,
6437 ZBORDER, FALSE, FALSE,
6447 static struct Mapping_EM_to_RND_player
6456 em_player_mapping_list[] =
6460 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6464 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6468 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6472 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6476 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6480 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6484 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6488 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6492 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6496 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6500 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6504 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6508 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6512 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6516 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6520 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6524 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6528 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6532 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6536 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6540 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6544 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6548 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6552 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6556 EL_PLAYER_1, ACTION_DEFAULT, -1,
6560 EL_PLAYER_2, ACTION_DEFAULT, -1,
6564 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6568 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6572 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6576 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6580 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6584 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6588 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6592 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6596 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6600 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6604 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6608 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6612 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6616 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6620 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6624 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6628 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6632 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6636 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6640 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6644 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6648 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6652 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6656 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6660 EL_PLAYER_3, ACTION_DEFAULT, -1,
6664 EL_PLAYER_4, ACTION_DEFAULT, -1,
6673 int map_element_RND_to_EM(int element_rnd)
6675 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6676 static boolean mapping_initialized = FALSE;
6678 if (!mapping_initialized)
6682 /* return "Xalpha_quest" for all undefined elements in mapping array */
6683 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6684 mapping_RND_to_EM[i] = Xalpha_quest;
6686 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6687 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6688 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6689 em_object_mapping_list[i].element_em;
6691 mapping_initialized = TRUE;
6694 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6695 return mapping_RND_to_EM[element_rnd];
6697 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6702 int map_element_EM_to_RND(int element_em)
6704 static unsigned short mapping_EM_to_RND[TILE_MAX];
6705 static boolean mapping_initialized = FALSE;
6707 if (!mapping_initialized)
6711 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6712 for (i = 0; i < TILE_MAX; i++)
6713 mapping_EM_to_RND[i] = EL_UNKNOWN;
6715 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6716 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6717 em_object_mapping_list[i].element_rnd;
6719 mapping_initialized = TRUE;
6722 if (element_em >= 0 && element_em < TILE_MAX)
6723 return mapping_EM_to_RND[element_em];
6725 Error(ERR_WARN, "invalid EM level element %d", element_em);
6730 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6732 struct LevelInfo_EM *level_em = level->native_em_level;
6733 struct LEVEL *lev = level_em->lev;
6736 for (i = 0; i < TILE_MAX; i++)
6737 lev->android_array[i] = Xblank;
6739 for (i = 0; i < level->num_android_clone_elements; i++)
6741 int element_rnd = level->android_clone_element[i];
6742 int element_em = map_element_RND_to_EM(element_rnd);
6744 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6745 if (em_object_mapping_list[j].element_rnd == element_rnd)
6746 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6750 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6752 struct LevelInfo_EM *level_em = level->native_em_level;
6753 struct LEVEL *lev = level_em->lev;
6756 level->num_android_clone_elements = 0;
6758 for (i = 0; i < TILE_MAX; i++)
6760 int element_em = lev->android_array[i];
6762 boolean element_found = FALSE;
6764 if (element_em == Xblank)
6767 element_rnd = map_element_EM_to_RND(element_em);
6769 for (j = 0; j < level->num_android_clone_elements; j++)
6770 if (level->android_clone_element[j] == element_rnd)
6771 element_found = TRUE;
6775 level->android_clone_element[level->num_android_clone_elements++] =
6778 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6783 if (level->num_android_clone_elements == 0)
6785 level->num_android_clone_elements = 1;
6786 level->android_clone_element[0] = EL_EMPTY;
6790 int map_direction_RND_to_EM(int direction)
6792 return (direction == MV_UP ? 0 :
6793 direction == MV_RIGHT ? 1 :
6794 direction == MV_DOWN ? 2 :
6795 direction == MV_LEFT ? 3 :
6799 int map_direction_EM_to_RND(int direction)
6801 return (direction == 0 ? MV_UP :
6802 direction == 1 ? MV_RIGHT :
6803 direction == 2 ? MV_DOWN :
6804 direction == 3 ? MV_LEFT :
6808 int map_element_RND_to_SP(int element_rnd)
6810 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6812 if (element_rnd >= EL_SP_START &&
6813 element_rnd <= EL_SP_END)
6814 element_sp = element_rnd - EL_SP_START;
6815 else if (element_rnd == EL_EMPTY_SPACE)
6817 else if (element_rnd == EL_INVISIBLE_WALL)
6823 int map_element_SP_to_RND(int element_sp)
6825 int element_rnd = EL_UNKNOWN;
6827 if (element_sp >= 0x00 &&
6829 element_rnd = EL_SP_START + element_sp;
6830 else if (element_sp == 0x28)
6831 element_rnd = EL_INVISIBLE_WALL;
6836 int map_action_SP_to_RND(int action_sp)
6840 case actActive: return ACTION_ACTIVE;
6841 case actImpact: return ACTION_IMPACT;
6842 case actExploding: return ACTION_EXPLODING;
6843 case actDigging: return ACTION_DIGGING;
6844 case actSnapping: return ACTION_SNAPPING;
6845 case actCollecting: return ACTION_COLLECTING;
6846 case actPassing: return ACTION_PASSING;
6847 case actPushing: return ACTION_PUSHING;
6848 case actDropping: return ACTION_DROPPING;
6850 default: return ACTION_DEFAULT;
6854 int get_next_element(int element)
6858 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6859 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6860 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6861 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6862 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6863 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6864 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6865 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6866 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6867 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6868 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6870 default: return element;
6874 int el_act_dir2img(int element, int action, int direction)
6876 element = GFX_ELEMENT(element);
6877 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6879 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6880 return element_info[element].direction_graphic[action][direction];
6883 static int el_act_dir2crm(int element, int action, int direction)
6885 element = GFX_ELEMENT(element);
6886 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6888 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6889 return element_info[element].direction_crumbled[action][direction];
6892 int el_act2img(int element, int action)
6894 element = GFX_ELEMENT(element);
6896 return element_info[element].graphic[action];
6899 int el_act2crm(int element, int action)
6901 element = GFX_ELEMENT(element);
6903 return element_info[element].crumbled[action];
6906 int el_dir2img(int element, int direction)
6908 element = GFX_ELEMENT(element);
6910 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6913 int el2baseimg(int element)
6915 return element_info[element].graphic[ACTION_DEFAULT];
6918 int el2img(int element)
6920 element = GFX_ELEMENT(element);
6922 return element_info[element].graphic[ACTION_DEFAULT];
6925 int el2edimg(int element)
6927 element = GFX_ELEMENT(element);
6929 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6932 int el2preimg(int element)
6934 element = GFX_ELEMENT(element);
6936 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6939 int el2panelimg(int element)
6941 element = GFX_ELEMENT(element);
6943 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6946 int font2baseimg(int font_nr)
6948 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6951 int getBeltNrFromBeltElement(int element)
6953 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6954 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6955 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6958 int getBeltNrFromBeltActiveElement(int element)
6960 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6961 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6962 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6965 int getBeltNrFromBeltSwitchElement(int element)
6967 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6968 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6969 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6972 int getBeltDirNrFromBeltElement(int element)
6974 static int belt_base_element[4] =
6976 EL_CONVEYOR_BELT_1_LEFT,
6977 EL_CONVEYOR_BELT_2_LEFT,
6978 EL_CONVEYOR_BELT_3_LEFT,
6979 EL_CONVEYOR_BELT_4_LEFT
6982 int belt_nr = getBeltNrFromBeltElement(element);
6983 int belt_dir_nr = element - belt_base_element[belt_nr];
6985 return (belt_dir_nr % 3);
6988 int getBeltDirNrFromBeltSwitchElement(int element)
6990 static int belt_base_element[4] =
6992 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6993 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6994 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6995 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6998 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6999 int belt_dir_nr = element - belt_base_element[belt_nr];
7001 return (belt_dir_nr % 3);
7004 int getBeltDirFromBeltElement(int element)
7006 static int belt_move_dir[3] =
7013 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7015 return belt_move_dir[belt_dir_nr];
7018 int getBeltDirFromBeltSwitchElement(int element)
7020 static int belt_move_dir[3] =
7027 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7029 return belt_move_dir[belt_dir_nr];
7032 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7034 static int belt_base_element[4] =
7036 EL_CONVEYOR_BELT_1_LEFT,
7037 EL_CONVEYOR_BELT_2_LEFT,
7038 EL_CONVEYOR_BELT_3_LEFT,
7039 EL_CONVEYOR_BELT_4_LEFT
7042 return belt_base_element[belt_nr] + belt_dir_nr;
7045 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7047 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7049 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7052 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7054 static int belt_base_element[4] =
7056 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7057 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7058 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7059 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7062 return belt_base_element[belt_nr] + belt_dir_nr;
7065 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7067 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7069 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7072 boolean getTeamMode_EM()
7074 return game.team_mode;
7077 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7079 int game_frame_delay_value;
7081 game_frame_delay_value =
7082 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7083 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7086 if (tape.playing && tape.warp_forward && !tape.pausing)
7087 game_frame_delay_value = 0;
7089 return game_frame_delay_value;
7092 unsigned int InitRND(int seed)
7094 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7095 return InitEngineRandom_EM(seed);
7096 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7097 return InitEngineRandom_SP(seed);
7099 return InitEngineRandom_RND(seed);
7102 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7103 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7105 inline static int get_effective_element_EM(int tile, int frame_em)
7107 int element = object_mapping[tile].element_rnd;
7108 int action = object_mapping[tile].action;
7109 boolean is_backside = object_mapping[tile].is_backside;
7110 boolean action_removing = (action == ACTION_DIGGING ||
7111 action == ACTION_SNAPPING ||
7112 action == ACTION_COLLECTING);
7118 case Yacid_splash_eB:
7119 case Yacid_splash_wB:
7120 return (frame_em > 5 ? EL_EMPTY : element);
7126 else /* frame_em == 7 */
7130 case Yacid_splash_eB:
7131 case Yacid_splash_wB:
7134 case Yemerald_stone:
7137 case Ydiamond_stone:
7141 case Xdrip_stretchB:
7160 case Xsand_stonein_1:
7161 case Xsand_stonein_2:
7162 case Xsand_stonein_3:
7163 case Xsand_stonein_4:
7167 return (is_backside || action_removing ? EL_EMPTY : element);
7172 inline static boolean check_linear_animation_EM(int tile)
7176 case Xsand_stonesand_1:
7177 case Xsand_stonesand_quickout_1:
7178 case Xsand_sandstone_1:
7179 case Xsand_stonein_1:
7180 case Xsand_stoneout_1:
7199 case Yacid_splash_eB:
7200 case Yacid_splash_wB:
7201 case Yemerald_stone:
7208 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7209 boolean has_crumbled_graphics,
7210 int crumbled, int sync_frame)
7212 /* if element can be crumbled, but certain action graphics are just empty
7213 space (like instantly snapping sand to empty space in 1 frame), do not
7214 treat these empty space graphics as crumbled graphics in EMC engine */
7215 if (crumbled == IMG_EMPTY_SPACE)
7216 has_crumbled_graphics = FALSE;
7218 if (has_crumbled_graphics)
7220 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7221 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7222 g_crumbled->anim_delay,
7223 g_crumbled->anim_mode,
7224 g_crumbled->anim_start_frame,
7227 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7228 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7230 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7232 g_em->has_crumbled_graphics = TRUE;
7236 g_em->crumbled_bitmap = NULL;
7237 g_em->crumbled_src_x = 0;
7238 g_em->crumbled_src_y = 0;
7239 g_em->crumbled_border_size = 0;
7241 g_em->has_crumbled_graphics = FALSE;
7245 void ResetGfxAnimation_EM(int x, int y, int tile)
7250 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7251 int tile, int frame_em, int x, int y)
7253 int action = object_mapping[tile].action;
7254 int direction = object_mapping[tile].direction;
7255 int effective_element = get_effective_element_EM(tile, frame_em);
7256 int graphic = (direction == MV_NONE ?
7257 el_act2img(effective_element, action) :
7258 el_act_dir2img(effective_element, action, direction));
7259 struct GraphicInfo *g = &graphic_info[graphic];
7261 boolean action_removing = (action == ACTION_DIGGING ||
7262 action == ACTION_SNAPPING ||
7263 action == ACTION_COLLECTING);
7264 boolean action_moving = (action == ACTION_FALLING ||
7265 action == ACTION_MOVING ||
7266 action == ACTION_PUSHING ||
7267 action == ACTION_EATING ||
7268 action == ACTION_FILLING ||
7269 action == ACTION_EMPTYING);
7270 boolean action_falling = (action == ACTION_FALLING ||
7271 action == ACTION_FILLING ||
7272 action == ACTION_EMPTYING);
7274 /* special case: graphic uses "2nd movement tile" and has defined
7275 7 frames for movement animation (or less) => use default graphic
7276 for last (8th) frame which ends the movement animation */
7277 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7279 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7280 graphic = (direction == MV_NONE ?
7281 el_act2img(effective_element, action) :
7282 el_act_dir2img(effective_element, action, direction));
7284 g = &graphic_info[graphic];
7287 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7291 else if (action_moving)
7293 boolean is_backside = object_mapping[tile].is_backside;
7297 int direction = object_mapping[tile].direction;
7298 int move_dir = (action_falling ? MV_DOWN : direction);
7303 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7304 if (g->double_movement && frame_em == 0)
7308 if (move_dir == MV_LEFT)
7309 GfxFrame[x - 1][y] = GfxFrame[x][y];
7310 else if (move_dir == MV_RIGHT)
7311 GfxFrame[x + 1][y] = GfxFrame[x][y];
7312 else if (move_dir == MV_UP)
7313 GfxFrame[x][y - 1] = GfxFrame[x][y];
7314 else if (move_dir == MV_DOWN)
7315 GfxFrame[x][y + 1] = GfxFrame[x][y];
7322 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7323 if (tile == Xsand_stonesand_quickout_1 ||
7324 tile == Xsand_stonesand_quickout_2)
7328 if (graphic_info[graphic].anim_global_sync)
7329 sync_frame = FrameCounter;
7330 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7331 sync_frame = GfxFrame[x][y];
7333 sync_frame = 0; /* playfield border (pseudo steel) */
7335 SetRandomAnimationValue(x, y);
7337 int frame = getAnimationFrame(g->anim_frames,
7340 g->anim_start_frame,
7343 g_em->unique_identifier =
7344 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7347 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7348 int tile, int frame_em, int x, int y)
7350 int action = object_mapping[tile].action;
7351 int direction = object_mapping[tile].direction;
7352 boolean is_backside = object_mapping[tile].is_backside;
7353 int effective_element = get_effective_element_EM(tile, frame_em);
7354 int effective_action = action;
7355 int graphic = (direction == MV_NONE ?
7356 el_act2img(effective_element, effective_action) :
7357 el_act_dir2img(effective_element, effective_action,
7359 int crumbled = (direction == MV_NONE ?
7360 el_act2crm(effective_element, effective_action) :
7361 el_act_dir2crm(effective_element, effective_action,
7363 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7364 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7365 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7366 struct GraphicInfo *g = &graphic_info[graphic];
7369 /* special case: graphic uses "2nd movement tile" and has defined
7370 7 frames for movement animation (or less) => use default graphic
7371 for last (8th) frame which ends the movement animation */
7372 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7374 effective_action = ACTION_DEFAULT;
7375 graphic = (direction == MV_NONE ?
7376 el_act2img(effective_element, effective_action) :
7377 el_act_dir2img(effective_element, effective_action,
7379 crumbled = (direction == MV_NONE ?
7380 el_act2crm(effective_element, effective_action) :
7381 el_act_dir2crm(effective_element, effective_action,
7384 g = &graphic_info[graphic];
7387 if (graphic_info[graphic].anim_global_sync)
7388 sync_frame = FrameCounter;
7389 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7390 sync_frame = GfxFrame[x][y];
7392 sync_frame = 0; /* playfield border (pseudo steel) */
7394 SetRandomAnimationValue(x, y);
7396 int frame = getAnimationFrame(g->anim_frames,
7399 g->anim_start_frame,
7402 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7403 g->double_movement && is_backside);
7405 /* (updating the "crumbled" graphic definitions is probably not really needed,
7406 as animations for crumbled graphics can't be longer than one EMC cycle) */
7407 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7411 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7412 int player_nr, int anim, int frame_em)
7414 int element = player_mapping[player_nr][anim].element_rnd;
7415 int action = player_mapping[player_nr][anim].action;
7416 int direction = player_mapping[player_nr][anim].direction;
7417 int graphic = (direction == MV_NONE ?
7418 el_act2img(element, action) :
7419 el_act_dir2img(element, action, direction));
7420 struct GraphicInfo *g = &graphic_info[graphic];
7423 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7425 stored_player[player_nr].StepFrame = frame_em;
7427 sync_frame = stored_player[player_nr].Frame;
7429 int frame = getAnimationFrame(g->anim_frames,
7432 g->anim_start_frame,
7435 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7436 &g_em->src_x, &g_em->src_y, FALSE);
7439 void InitGraphicInfo_EM(void)
7444 int num_em_gfx_errors = 0;
7446 if (graphic_info_em_object[0][0].bitmap == NULL)
7448 /* EM graphics not yet initialized in em_open_all() */
7453 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7456 /* always start with reliable default values */
7457 for (i = 0; i < TILE_MAX; i++)
7459 object_mapping[i].element_rnd = EL_UNKNOWN;
7460 object_mapping[i].is_backside = FALSE;
7461 object_mapping[i].action = ACTION_DEFAULT;
7462 object_mapping[i].direction = MV_NONE;
7465 /* always start with reliable default values */
7466 for (p = 0; p < MAX_PLAYERS; p++)
7468 for (i = 0; i < SPR_MAX; i++)
7470 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7471 player_mapping[p][i].action = ACTION_DEFAULT;
7472 player_mapping[p][i].direction = MV_NONE;
7476 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7478 int e = em_object_mapping_list[i].element_em;
7480 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7481 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7483 if (em_object_mapping_list[i].action != -1)
7484 object_mapping[e].action = em_object_mapping_list[i].action;
7486 if (em_object_mapping_list[i].direction != -1)
7487 object_mapping[e].direction =
7488 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7491 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7493 int a = em_player_mapping_list[i].action_em;
7494 int p = em_player_mapping_list[i].player_nr;
7496 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7498 if (em_player_mapping_list[i].action != -1)
7499 player_mapping[p][a].action = em_player_mapping_list[i].action;
7501 if (em_player_mapping_list[i].direction != -1)
7502 player_mapping[p][a].direction =
7503 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7506 for (i = 0; i < TILE_MAX; i++)
7508 int element = object_mapping[i].element_rnd;
7509 int action = object_mapping[i].action;
7510 int direction = object_mapping[i].direction;
7511 boolean is_backside = object_mapping[i].is_backside;
7512 boolean action_exploding = ((action == ACTION_EXPLODING ||
7513 action == ACTION_SMASHED_BY_ROCK ||
7514 action == ACTION_SMASHED_BY_SPRING) &&
7515 element != EL_DIAMOND);
7516 boolean action_active = (action == ACTION_ACTIVE);
7517 boolean action_other = (action == ACTION_OTHER);
7519 for (j = 0; j < 8; j++)
7521 int effective_element = get_effective_element_EM(i, j);
7522 int effective_action = (j < 7 ? action :
7523 i == Xdrip_stretch ? action :
7524 i == Xdrip_stretchB ? action :
7525 i == Ydrip_s1 ? action :
7526 i == Ydrip_s1B ? action :
7527 i == Xball_1B ? action :
7528 i == Xball_2 ? action :
7529 i == Xball_2B ? action :
7530 i == Yball_eat ? action :
7531 i == Ykey_1_eat ? action :
7532 i == Ykey_2_eat ? action :
7533 i == Ykey_3_eat ? action :
7534 i == Ykey_4_eat ? action :
7535 i == Ykey_5_eat ? action :
7536 i == Ykey_6_eat ? action :
7537 i == Ykey_7_eat ? action :
7538 i == Ykey_8_eat ? action :
7539 i == Ylenses_eat ? action :
7540 i == Ymagnify_eat ? action :
7541 i == Ygrass_eat ? action :
7542 i == Ydirt_eat ? action :
7543 i == Xsand_stonein_1 ? action :
7544 i == Xsand_stonein_2 ? action :
7545 i == Xsand_stonein_3 ? action :
7546 i == Xsand_stonein_4 ? action :
7547 i == Xsand_stoneout_1 ? action :
7548 i == Xsand_stoneout_2 ? action :
7549 i == Xboom_android ? ACTION_EXPLODING :
7550 action_exploding ? ACTION_EXPLODING :
7551 action_active ? action :
7552 action_other ? action :
7554 int graphic = (el_act_dir2img(effective_element, effective_action,
7556 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7558 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7559 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7560 boolean has_action_graphics = (graphic != base_graphic);
7561 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7562 struct GraphicInfo *g = &graphic_info[graphic];
7563 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7566 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7567 boolean special_animation = (action != ACTION_DEFAULT &&
7568 g->anim_frames == 3 &&
7569 g->anim_delay == 2 &&
7570 g->anim_mode & ANIM_LINEAR);
7571 int sync_frame = (i == Xdrip_stretch ? 7 :
7572 i == Xdrip_stretchB ? 7 :
7573 i == Ydrip_s2 ? j + 8 :
7574 i == Ydrip_s2B ? j + 8 :
7583 i == Xfake_acid_1 ? 0 :
7584 i == Xfake_acid_2 ? 10 :
7585 i == Xfake_acid_3 ? 20 :
7586 i == Xfake_acid_4 ? 30 :
7587 i == Xfake_acid_5 ? 40 :
7588 i == Xfake_acid_6 ? 50 :
7589 i == Xfake_acid_7 ? 60 :
7590 i == Xfake_acid_8 ? 70 :
7592 i == Xball_2B ? j + 8 :
7593 i == Yball_eat ? j + 1 :
7594 i == Ykey_1_eat ? j + 1 :
7595 i == Ykey_2_eat ? j + 1 :
7596 i == Ykey_3_eat ? j + 1 :
7597 i == Ykey_4_eat ? j + 1 :
7598 i == Ykey_5_eat ? j + 1 :
7599 i == Ykey_6_eat ? j + 1 :
7600 i == Ykey_7_eat ? j + 1 :
7601 i == Ykey_8_eat ? j + 1 :
7602 i == Ylenses_eat ? j + 1 :
7603 i == Ymagnify_eat ? j + 1 :
7604 i == Ygrass_eat ? j + 1 :
7605 i == Ydirt_eat ? j + 1 :
7606 i == Xamoeba_1 ? 0 :
7607 i == Xamoeba_2 ? 1 :
7608 i == Xamoeba_3 ? 2 :
7609 i == Xamoeba_4 ? 3 :
7610 i == Xamoeba_5 ? 0 :
7611 i == Xamoeba_6 ? 1 :
7612 i == Xamoeba_7 ? 2 :
7613 i == Xamoeba_8 ? 3 :
7614 i == Xexit_2 ? j + 8 :
7615 i == Xexit_3 ? j + 16 :
7616 i == Xdynamite_1 ? 0 :
7617 i == Xdynamite_2 ? 8 :
7618 i == Xdynamite_3 ? 16 :
7619 i == Xdynamite_4 ? 24 :
7620 i == Xsand_stonein_1 ? j + 1 :
7621 i == Xsand_stonein_2 ? j + 9 :
7622 i == Xsand_stonein_3 ? j + 17 :
7623 i == Xsand_stonein_4 ? j + 25 :
7624 i == Xsand_stoneout_1 && j == 0 ? 0 :
7625 i == Xsand_stoneout_1 && j == 1 ? 0 :
7626 i == Xsand_stoneout_1 && j == 2 ? 1 :
7627 i == Xsand_stoneout_1 && j == 3 ? 2 :
7628 i == Xsand_stoneout_1 && j == 4 ? 2 :
7629 i == Xsand_stoneout_1 && j == 5 ? 3 :
7630 i == Xsand_stoneout_1 && j == 6 ? 4 :
7631 i == Xsand_stoneout_1 && j == 7 ? 4 :
7632 i == Xsand_stoneout_2 && j == 0 ? 5 :
7633 i == Xsand_stoneout_2 && j == 1 ? 6 :
7634 i == Xsand_stoneout_2 && j == 2 ? 7 :
7635 i == Xsand_stoneout_2 && j == 3 ? 8 :
7636 i == Xsand_stoneout_2 && j == 4 ? 9 :
7637 i == Xsand_stoneout_2 && j == 5 ? 11 :
7638 i == Xsand_stoneout_2 && j == 6 ? 13 :
7639 i == Xsand_stoneout_2 && j == 7 ? 15 :
7640 i == Xboom_bug && j == 1 ? 2 :
7641 i == Xboom_bug && j == 2 ? 2 :
7642 i == Xboom_bug && j == 3 ? 4 :
7643 i == Xboom_bug && j == 4 ? 4 :
7644 i == Xboom_bug && j == 5 ? 2 :
7645 i == Xboom_bug && j == 6 ? 2 :
7646 i == Xboom_bug && j == 7 ? 0 :
7647 i == Xboom_bomb && j == 1 ? 2 :
7648 i == Xboom_bomb && j == 2 ? 2 :
7649 i == Xboom_bomb && j == 3 ? 4 :
7650 i == Xboom_bomb && j == 4 ? 4 :
7651 i == Xboom_bomb && j == 5 ? 2 :
7652 i == Xboom_bomb && j == 6 ? 2 :
7653 i == Xboom_bomb && j == 7 ? 0 :
7654 i == Xboom_android && j == 7 ? 6 :
7655 i == Xboom_1 && j == 1 ? 2 :
7656 i == Xboom_1 && j == 2 ? 2 :
7657 i == Xboom_1 && j == 3 ? 4 :
7658 i == Xboom_1 && j == 4 ? 4 :
7659 i == Xboom_1 && j == 5 ? 6 :
7660 i == Xboom_1 && j == 6 ? 6 :
7661 i == Xboom_1 && j == 7 ? 8 :
7662 i == Xboom_2 && j == 0 ? 8 :
7663 i == Xboom_2 && j == 1 ? 8 :
7664 i == Xboom_2 && j == 2 ? 10 :
7665 i == Xboom_2 && j == 3 ? 10 :
7666 i == Xboom_2 && j == 4 ? 10 :
7667 i == Xboom_2 && j == 5 ? 12 :
7668 i == Xboom_2 && j == 6 ? 12 :
7669 i == Xboom_2 && j == 7 ? 12 :
7670 special_animation && j == 4 ? 3 :
7671 effective_action != action ? 0 :
7675 Bitmap *debug_bitmap = g_em->bitmap;
7676 int debug_src_x = g_em->src_x;
7677 int debug_src_y = g_em->src_y;
7680 int frame = getAnimationFrame(g->anim_frames,
7683 g->anim_start_frame,
7686 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7687 g->double_movement && is_backside);
7689 g_em->bitmap = src_bitmap;
7690 g_em->src_x = src_x;
7691 g_em->src_y = src_y;
7692 g_em->src_offset_x = 0;
7693 g_em->src_offset_y = 0;
7694 g_em->dst_offset_x = 0;
7695 g_em->dst_offset_y = 0;
7696 g_em->width = TILEX;
7697 g_em->height = TILEY;
7699 g_em->preserve_background = FALSE;
7701 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7704 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7705 effective_action == ACTION_MOVING ||
7706 effective_action == ACTION_PUSHING ||
7707 effective_action == ACTION_EATING)) ||
7708 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7709 effective_action == ACTION_EMPTYING)))
7712 (effective_action == ACTION_FALLING ||
7713 effective_action == ACTION_FILLING ||
7714 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7715 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7716 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7717 int num_steps = (i == Ydrip_s1 ? 16 :
7718 i == Ydrip_s1B ? 16 :
7719 i == Ydrip_s2 ? 16 :
7720 i == Ydrip_s2B ? 16 :
7721 i == Xsand_stonein_1 ? 32 :
7722 i == Xsand_stonein_2 ? 32 :
7723 i == Xsand_stonein_3 ? 32 :
7724 i == Xsand_stonein_4 ? 32 :
7725 i == Xsand_stoneout_1 ? 16 :
7726 i == Xsand_stoneout_2 ? 16 : 8);
7727 int cx = ABS(dx) * (TILEX / num_steps);
7728 int cy = ABS(dy) * (TILEY / num_steps);
7729 int step_frame = (i == Ydrip_s2 ? j + 8 :
7730 i == Ydrip_s2B ? j + 8 :
7731 i == Xsand_stonein_2 ? j + 8 :
7732 i == Xsand_stonein_3 ? j + 16 :
7733 i == Xsand_stonein_4 ? j + 24 :
7734 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7735 int step = (is_backside ? step_frame : num_steps - step_frame);
7737 if (is_backside) /* tile where movement starts */
7739 if (dx < 0 || dy < 0)
7741 g_em->src_offset_x = cx * step;
7742 g_em->src_offset_y = cy * step;
7746 g_em->dst_offset_x = cx * step;
7747 g_em->dst_offset_y = cy * step;
7750 else /* tile where movement ends */
7752 if (dx < 0 || dy < 0)
7754 g_em->dst_offset_x = cx * step;
7755 g_em->dst_offset_y = cy * step;
7759 g_em->src_offset_x = cx * step;
7760 g_em->src_offset_y = cy * step;
7764 g_em->width = TILEX - cx * step;
7765 g_em->height = TILEY - cy * step;
7768 /* create unique graphic identifier to decide if tile must be redrawn */
7769 /* bit 31 - 16 (16 bit): EM style graphic
7770 bit 15 - 12 ( 4 bit): EM style frame
7771 bit 11 - 6 ( 6 bit): graphic width
7772 bit 5 - 0 ( 6 bit): graphic height */
7773 g_em->unique_identifier =
7774 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7778 /* skip check for EMC elements not contained in original EMC artwork */
7779 if (element == EL_EMC_FAKE_ACID)
7782 if (g_em->bitmap != debug_bitmap ||
7783 g_em->src_x != debug_src_x ||
7784 g_em->src_y != debug_src_y ||
7785 g_em->src_offset_x != 0 ||
7786 g_em->src_offset_y != 0 ||
7787 g_em->dst_offset_x != 0 ||
7788 g_em->dst_offset_y != 0 ||
7789 g_em->width != TILEX ||
7790 g_em->height != TILEY)
7792 static int last_i = -1;
7800 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7801 i, element, element_info[element].token_name,
7802 element_action_info[effective_action].suffix, direction);
7804 if (element != effective_element)
7805 printf(" [%d ('%s')]",
7807 element_info[effective_element].token_name);
7811 if (g_em->bitmap != debug_bitmap)
7812 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7813 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7815 if (g_em->src_x != debug_src_x ||
7816 g_em->src_y != debug_src_y)
7817 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7818 j, (is_backside ? 'B' : 'F'),
7819 g_em->src_x, g_em->src_y,
7820 g_em->src_x / 32, g_em->src_y / 32,
7821 debug_src_x, debug_src_y,
7822 debug_src_x / 32, debug_src_y / 32);
7824 if (g_em->src_offset_x != 0 ||
7825 g_em->src_offset_y != 0 ||
7826 g_em->dst_offset_x != 0 ||
7827 g_em->dst_offset_y != 0)
7828 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7830 g_em->src_offset_x, g_em->src_offset_y,
7831 g_em->dst_offset_x, g_em->dst_offset_y);
7833 if (g_em->width != TILEX ||
7834 g_em->height != TILEY)
7835 printf(" %d (%d): size %d,%d should be %d,%d\n",
7837 g_em->width, g_em->height, TILEX, TILEY);
7839 num_em_gfx_errors++;
7846 for (i = 0; i < TILE_MAX; i++)
7848 for (j = 0; j < 8; j++)
7850 int element = object_mapping[i].element_rnd;
7851 int action = object_mapping[i].action;
7852 int direction = object_mapping[i].direction;
7853 boolean is_backside = object_mapping[i].is_backside;
7854 int graphic_action = el_act_dir2img(element, action, direction);
7855 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7857 if ((action == ACTION_SMASHED_BY_ROCK ||
7858 action == ACTION_SMASHED_BY_SPRING ||
7859 action == ACTION_EATING) &&
7860 graphic_action == graphic_default)
7862 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7863 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7864 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7865 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7868 /* no separate animation for "smashed by rock" -- use rock instead */
7869 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7870 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7872 g_em->bitmap = g_xx->bitmap;
7873 g_em->src_x = g_xx->src_x;
7874 g_em->src_y = g_xx->src_y;
7875 g_em->src_offset_x = g_xx->src_offset_x;
7876 g_em->src_offset_y = g_xx->src_offset_y;
7877 g_em->dst_offset_x = g_xx->dst_offset_x;
7878 g_em->dst_offset_y = g_xx->dst_offset_y;
7879 g_em->width = g_xx->width;
7880 g_em->height = g_xx->height;
7881 g_em->unique_identifier = g_xx->unique_identifier;
7884 g_em->preserve_background = TRUE;
7889 for (p = 0; p < MAX_PLAYERS; p++)
7891 for (i = 0; i < SPR_MAX; i++)
7893 int element = player_mapping[p][i].element_rnd;
7894 int action = player_mapping[p][i].action;
7895 int direction = player_mapping[p][i].direction;
7897 for (j = 0; j < 8; j++)
7899 int effective_element = element;
7900 int effective_action = action;
7901 int graphic = (direction == MV_NONE ?
7902 el_act2img(effective_element, effective_action) :
7903 el_act_dir2img(effective_element, effective_action,
7905 struct GraphicInfo *g = &graphic_info[graphic];
7906 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7912 Bitmap *debug_bitmap = g_em->bitmap;
7913 int debug_src_x = g_em->src_x;
7914 int debug_src_y = g_em->src_y;
7917 int frame = getAnimationFrame(g->anim_frames,
7920 g->anim_start_frame,
7923 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7925 g_em->bitmap = src_bitmap;
7926 g_em->src_x = src_x;
7927 g_em->src_y = src_y;
7928 g_em->src_offset_x = 0;
7929 g_em->src_offset_y = 0;
7930 g_em->dst_offset_x = 0;
7931 g_em->dst_offset_y = 0;
7932 g_em->width = TILEX;
7933 g_em->height = TILEY;
7937 /* skip check for EMC elements not contained in original EMC artwork */
7938 if (element == EL_PLAYER_3 ||
7939 element == EL_PLAYER_4)
7942 if (g_em->bitmap != debug_bitmap ||
7943 g_em->src_x != debug_src_x ||
7944 g_em->src_y != debug_src_y)
7946 static int last_i = -1;
7954 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7955 p, i, element, element_info[element].token_name,
7956 element_action_info[effective_action].suffix, direction);
7958 if (element != effective_element)
7959 printf(" [%d ('%s')]",
7961 element_info[effective_element].token_name);
7965 if (g_em->bitmap != debug_bitmap)
7966 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7967 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7969 if (g_em->src_x != debug_src_x ||
7970 g_em->src_y != debug_src_y)
7971 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7973 g_em->src_x, g_em->src_y,
7974 g_em->src_x / 32, g_em->src_y / 32,
7975 debug_src_x, debug_src_y,
7976 debug_src_x / 32, debug_src_y / 32);
7978 num_em_gfx_errors++;
7988 printf("::: [%d errors found]\n", num_em_gfx_errors);
7994 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
7995 boolean any_player_moving,
7996 boolean any_player_snapping,
7997 boolean any_player_dropping)
7999 static boolean player_was_waiting = TRUE;
8001 if (frame == 0 && !any_player_dropping)
8003 if (!player_was_waiting)
8005 if (!SaveEngineSnapshotToList())
8008 player_was_waiting = TRUE;
8011 else if (any_player_moving || any_player_snapping || any_player_dropping)
8013 player_was_waiting = FALSE;
8017 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8018 boolean murphy_is_dropping)
8020 static boolean player_was_waiting = TRUE;
8022 if (murphy_is_waiting)
8024 if (!player_was_waiting)
8026 if (!SaveEngineSnapshotToList())
8029 player_was_waiting = TRUE;
8034 player_was_waiting = FALSE;
8038 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8039 boolean any_player_moving,
8040 boolean any_player_snapping,
8041 boolean any_player_dropping)
8043 if (tape.single_step && tape.recording && !tape.pausing)
8044 if (frame == 0 && !any_player_dropping)
8045 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8047 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8048 any_player_snapping, any_player_dropping);
8051 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8052 boolean murphy_is_dropping)
8054 if (tape.single_step && tape.recording && !tape.pausing)
8055 if (murphy_is_waiting)
8056 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8058 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8061 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8062 int graphic, int sync_frame, int x, int y)
8064 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8066 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8069 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8071 return (IS_NEXT_FRAME(sync_frame, graphic));
8074 int getGraphicInfo_Delay(int graphic)
8076 return graphic_info[graphic].anim_delay;
8079 void PlayMenuSoundExt(int sound)
8081 if (sound == SND_UNDEFINED)
8084 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8085 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8088 if (IS_LOOP_SOUND(sound))
8089 PlaySoundLoop(sound);
8094 void PlayMenuSound()
8096 PlayMenuSoundExt(menu.sound[game_status]);
8099 void PlayMenuSoundStereo(int sound, int stereo_position)
8101 if (sound == SND_UNDEFINED)
8104 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8105 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8108 if (IS_LOOP_SOUND(sound))
8109 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8111 PlaySoundStereo(sound, stereo_position);
8114 void PlayMenuSoundIfLoopExt(int sound)
8116 if (sound == SND_UNDEFINED)
8119 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8120 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8123 if (IS_LOOP_SOUND(sound))
8124 PlaySoundLoop(sound);
8127 void PlayMenuSoundIfLoop()
8129 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8132 void PlayMenuMusicExt(int music)
8134 if (music == MUS_UNDEFINED)
8137 if (!setup.sound_music)
8143 void PlayMenuMusic()
8145 PlayMenuMusicExt(menu.music[game_status]);
8148 void PlaySoundActivating()
8151 PlaySound(SND_MENU_ITEM_ACTIVATING);
8155 void PlaySoundSelecting()
8158 PlaySound(SND_MENU_ITEM_SELECTING);
8162 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8164 boolean change_fullscreen = (setup.fullscreen !=
8165 video.fullscreen_enabled);
8166 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8167 setup.window_scaling_percent !=
8168 video.window_scaling_percent);
8170 if (change_window_scaling_percent && video.fullscreen_enabled)
8173 if (!change_window_scaling_percent && !video.fullscreen_available)
8176 #if defined(TARGET_SDL2)
8177 if (change_window_scaling_percent)
8179 SDLSetWindowScaling(setup.window_scaling_percent);
8183 else if (change_fullscreen)
8185 SDLSetWindowFullscreen(setup.fullscreen);
8187 /* set setup value according to successfully changed fullscreen mode */
8188 setup.fullscreen = video.fullscreen_enabled;
8194 if (change_fullscreen ||
8195 change_window_scaling_percent)
8197 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8199 /* save backbuffer content which gets lost when toggling fullscreen mode */
8200 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8202 if (change_window_scaling_percent)
8204 /* keep window mode, but change window scaling */
8205 video.fullscreen_enabled = TRUE; /* force new window scaling */
8208 /* toggle fullscreen */
8209 ChangeVideoModeIfNeeded(setup.fullscreen);
8211 /* set setup value according to successfully changed fullscreen mode */
8212 setup.fullscreen = video.fullscreen_enabled;
8214 /* restore backbuffer content from temporary backbuffer backup bitmap */
8215 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8217 FreeBitmap(tmp_backbuffer);
8219 /* update visible window/screen */
8220 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8224 void JoinRectangles(int *x, int *y, int *width, int *height,
8225 int x2, int y2, int width2, int height2)
8227 // do not join with "off-screen" rectangle
8228 if (x2 == -1 || y2 == -1)
8233 *width = MAX(*width, width2);
8234 *height = MAX(*height, height2);
8237 void SetAnimStatus(int anim_status_new)
8239 if (anim_status_new == GAME_MODE_MAIN)
8240 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
8242 global.anim_status_next = anim_status_new;
8244 // directly set screen modes that are entered without fading
8245 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
8246 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
8247 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
8248 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
8249 global.anim_status = global.anim_status_next;
8252 void SetGameStatus(int game_status_new)
8254 game_status = game_status_new;
8256 SetAnimStatus(game_status_new);
8259 void SetFontStatus(int game_status_new)
8261 static int last_game_status = -1;
8263 if (game_status_new != -1)
8265 // set game status for font use after storing last game status
8266 last_game_status = game_status;
8267 game_status = game_status_new;
8271 // reset game status after font use from last stored game status
8272 game_status = last_game_status;
8276 void ResetFontStatus()
8281 void ChangeViewportPropertiesIfNeeded()
8283 int gfx_game_mode = game_status;
8284 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8286 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
8287 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8288 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8289 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8290 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8291 int new_win_xsize = vp_window->width;
8292 int new_win_ysize = vp_window->height;
8293 int border_size = vp_playfield->border_size;
8294 int new_sx = vp_playfield->x + border_size;
8295 int new_sy = vp_playfield->y + border_size;
8296 int new_sxsize = vp_playfield->width - 2 * border_size;
8297 int new_sysize = vp_playfield->height - 2 * border_size;
8298 int new_real_sx = vp_playfield->x;
8299 int new_real_sy = vp_playfield->y;
8300 int new_full_sxsize = vp_playfield->width;
8301 int new_full_sysize = vp_playfield->height;
8302 int new_dx = vp_door_1->x;
8303 int new_dy = vp_door_1->y;
8304 int new_dxsize = vp_door_1->width;
8305 int new_dysize = vp_door_1->height;
8306 int new_vx = vp_door_2->x;
8307 int new_vy = vp_door_2->y;
8308 int new_vxsize = vp_door_2->width;
8309 int new_vysize = vp_door_2->height;
8310 int new_ex = vp_door_3->x;
8311 int new_ey = vp_door_3->y;
8312 int new_exsize = vp_door_3->width;
8313 int new_eysize = vp_door_3->height;
8314 int new_tilesize_var =
8315 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8317 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8318 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8319 int new_scr_fieldx = new_sxsize / tilesize;
8320 int new_scr_fieldy = new_sysize / tilesize;
8321 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8322 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8323 boolean init_gfx_buffers = FALSE;
8324 boolean init_video_buffer = FALSE;
8325 boolean init_gadgets_and_anims = FALSE;
8326 boolean init_em_graphics = FALSE;
8328 if (new_win_xsize != WIN_XSIZE ||
8329 new_win_ysize != WIN_YSIZE)
8331 WIN_XSIZE = new_win_xsize;
8332 WIN_YSIZE = new_win_ysize;
8334 init_video_buffer = TRUE;
8335 init_gfx_buffers = TRUE;
8336 init_gadgets_and_anims = TRUE;
8338 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8341 if (new_scr_fieldx != SCR_FIELDX ||
8342 new_scr_fieldy != SCR_FIELDY)
8344 /* this always toggles between MAIN and GAME when using small tile size */
8346 SCR_FIELDX = new_scr_fieldx;
8347 SCR_FIELDY = new_scr_fieldy;
8349 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8360 new_sxsize != SXSIZE ||
8361 new_sysize != SYSIZE ||
8362 new_dxsize != DXSIZE ||
8363 new_dysize != DYSIZE ||
8364 new_vxsize != VXSIZE ||
8365 new_vysize != VYSIZE ||
8366 new_exsize != EXSIZE ||
8367 new_eysize != EYSIZE ||
8368 new_real_sx != REAL_SX ||
8369 new_real_sy != REAL_SY ||
8370 new_full_sxsize != FULL_SXSIZE ||
8371 new_full_sysize != FULL_SYSIZE ||
8372 new_tilesize_var != TILESIZE_VAR
8375 // ------------------------------------------------------------------------
8376 // determine next fading area for changed viewport definitions
8377 // ------------------------------------------------------------------------
8379 // start with current playfield area (default fading area)
8382 FADE_SXSIZE = FULL_SXSIZE;
8383 FADE_SYSIZE = FULL_SYSIZE;
8385 // add new playfield area if position or size has changed
8386 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8387 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8389 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8390 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8393 // add current and new door 1 area if position or size has changed
8394 if (new_dx != DX || new_dy != DY ||
8395 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8397 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8398 DX, DY, DXSIZE, DYSIZE);
8399 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8400 new_dx, new_dy, new_dxsize, new_dysize);
8403 // add current and new door 2 area if position or size has changed
8404 if (new_dx != VX || new_dy != VY ||
8405 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8407 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8408 VX, VY, VXSIZE, VYSIZE);
8409 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8410 new_vx, new_vy, new_vxsize, new_vysize);
8413 // ------------------------------------------------------------------------
8414 // handle changed tile size
8415 // ------------------------------------------------------------------------
8417 if (new_tilesize_var != TILESIZE_VAR)
8419 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8421 // changing tile size invalidates scroll values of engine snapshots
8422 FreeEngineSnapshotSingle();
8424 // changing tile size requires update of graphic mapping for EM engine
8425 init_em_graphics = TRUE;
8436 SXSIZE = new_sxsize;
8437 SYSIZE = new_sysize;
8438 DXSIZE = new_dxsize;
8439 DYSIZE = new_dysize;
8440 VXSIZE = new_vxsize;
8441 VYSIZE = new_vysize;
8442 EXSIZE = new_exsize;
8443 EYSIZE = new_eysize;
8444 REAL_SX = new_real_sx;
8445 REAL_SY = new_real_sy;
8446 FULL_SXSIZE = new_full_sxsize;
8447 FULL_SYSIZE = new_full_sysize;
8448 TILESIZE_VAR = new_tilesize_var;
8450 init_gfx_buffers = TRUE;
8451 init_gadgets_and_anims = TRUE;
8453 // printf("::: viewports: init_gfx_buffers\n");
8454 // printf("::: viewports: init_gadgets_and_anims\n");
8457 if (init_gfx_buffers)
8459 // printf("::: init_gfx_buffers\n");
8461 SCR_FIELDX = new_scr_fieldx_buffers;
8462 SCR_FIELDY = new_scr_fieldy_buffers;
8466 SCR_FIELDX = new_scr_fieldx;
8467 SCR_FIELDY = new_scr_fieldy;
8469 SetDrawDeactivationMask(REDRAW_NONE);
8470 SetDrawBackgroundMask(REDRAW_FIELD);
8473 if (init_video_buffer)
8475 // printf("::: init_video_buffer\n");
8477 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8478 InitImageTextures();
8481 if (init_gadgets_and_anims)
8483 // printf("::: init_gadgets_and_anims\n");
8486 InitGlobalAnimations();
8489 if (init_em_graphics)
8491 InitGraphicInfo_EM();