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;
531 static int unifiedRedrawMask(int mask)
533 if (mask & REDRAW_ALL)
536 if (mask & REDRAW_FIELD && mask & REDRAW_DOORS)
542 static boolean equalRedrawMasks(int mask_1, int mask_2)
544 return unifiedRedrawMask(mask_1) == unifiedRedrawMask(mask_2);
549 static int last_redraw_mask = REDRAW_NONE;
551 // force screen redraw in every frame to continue drawing global animations
552 // (but always use the last redraw mask to prevent unwanted side effects)
553 if (redraw_mask == REDRAW_NONE)
554 redraw_mask = last_redraw_mask;
556 last_redraw_mask = redraw_mask;
559 // masked border now drawn immediately when blitting backbuffer to window
561 // draw masked border to all viewports, if defined
562 DrawMaskedBorder(redraw_mask);
565 // draw frames per second (only if debug mode is enabled)
566 if (redraw_mask & REDRAW_FPS)
567 DrawFramesPerSecond();
569 // remove playfield redraw before potentially merging with doors redraw
570 if (DrawingDeactivated(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE))
571 redraw_mask &= ~REDRAW_FIELD;
573 // redraw complete window if both playfield and (some) doors need redraw
574 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
575 redraw_mask = REDRAW_ALL;
577 /* although redrawing the whole window would be fine for normal gameplay,
578 being able to only redraw the playfield is required for deactivating
579 certain drawing areas (mainly playfield) to work, which is needed for
580 warp-forward to be fast enough (by skipping redraw of most frames) */
582 if (redraw_mask & REDRAW_ALL)
584 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
586 else if (redraw_mask & REDRAW_FIELD)
588 BlitBitmap(backbuffer, window,
589 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
591 else if (redraw_mask & REDRAW_DOORS)
593 // merge door areas to prevent calling screen redraw more than once
599 if (redraw_mask & REDRAW_DOOR_1)
603 x2 = MAX(x2, DX + DXSIZE);
604 y2 = MAX(y2, DY + DYSIZE);
607 if (redraw_mask & REDRAW_DOOR_2)
611 x2 = MAX(x2, VX + VXSIZE);
612 y2 = MAX(y2, VY + VYSIZE);
615 if (redraw_mask & REDRAW_DOOR_3)
619 x2 = MAX(x2, EX + EXSIZE);
620 y2 = MAX(y2, EY + EYSIZE);
623 BlitBitmap(backbuffer, window, x1, y1, x2 - x1, y2 - y1, x1, y1);
626 redraw_mask = REDRAW_NONE;
629 PrintFrameTimeDebugging();
633 void BackToFront_WithFrameDelay(unsigned int frame_delay_value)
635 unsigned int frame_delay_value_old = GetVideoFrameDelay();
637 SetVideoFrameDelay(frame_delay_value);
641 SetVideoFrameDelay(frame_delay_value_old);
644 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
646 static int fade_type_skip = FADE_TYPE_NONE;
647 void (*draw_border_function)(void) = NULL;
648 int x, y, width, height;
649 int fade_delay, post_delay;
651 if (fade_type == FADE_TYPE_FADE_OUT)
653 if (fade_type_skip != FADE_TYPE_NONE)
655 /* skip all fade operations until specified fade operation */
656 if (fade_type & fade_type_skip)
657 fade_type_skip = FADE_TYPE_NONE;
662 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
666 redraw_mask |= fade_mask;
668 if (fade_type == FADE_TYPE_SKIP)
670 fade_type_skip = fade_mode;
675 fade_delay = fading.fade_delay;
676 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
678 if (fade_type_skip != FADE_TYPE_NONE)
680 /* skip all fade operations until specified fade operation */
681 if (fade_type & fade_type_skip)
682 fade_type_skip = FADE_TYPE_NONE;
687 if (global.autoplay_leveldir)
692 if (fade_mask == REDRAW_FIELD)
697 height = FADE_SYSIZE;
699 if (border.draw_masked_when_fading)
700 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
702 DrawMaskedBorder_FIELD(); /* draw once */
704 else /* REDRAW_ALL */
712 if (!setup.fade_screens ||
714 fading.fade_mode == FADE_MODE_NONE)
716 if (fade_mode == FADE_MODE_FADE_OUT)
719 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
721 redraw_mask &= ~fade_mask;
726 FadeRectangle(x, y, width, height, fade_mode, fade_delay, post_delay,
727 draw_border_function);
729 redraw_mask &= ~fade_mask;
732 static void SetScreenStates_BeforeFadingIn()
734 // temporarily set screen mode for animations to screen after fading in
735 global.anim_status = global.anim_status_next;
737 // store backbuffer with all animations that will be started after fading in
738 PrepareFadeBitmap(DRAW_TO_FADE_TARGET);
740 // set screen mode for animations back to fading
741 global.anim_status = GAME_MODE_PSEUDO_FADING;
744 static void SetScreenStates_AfterFadingIn()
746 // store new source screen (to use correct masked border for fading)
747 gfx.fade_border_source_status = global.border_status;
749 global.anim_status = global.anim_status_next;
752 static void SetScreenStates_BeforeFadingOut()
754 // store new target screen (to use correct masked border for fading)
755 gfx.fade_border_target_status = game_status;
757 // set screen mode for animations to fading
758 global.anim_status = GAME_MODE_PSEUDO_FADING;
760 // store backbuffer with all animations that will be stopped for fading out
761 PrepareFadeBitmap(DRAW_TO_FADE_SOURCE);
764 static void SetScreenStates_AfterFadingOut()
766 global.border_status = game_status;
769 void FadeIn(int fade_mask)
771 SetScreenStates_BeforeFadingIn();
774 DrawMaskedBorder(REDRAW_ALL);
777 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
778 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
780 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
784 FADE_SXSIZE = FULL_SXSIZE;
785 FADE_SYSIZE = FULL_SYSIZE;
787 SetScreenStates_AfterFadingIn();
789 // force update of global animation status in case of rapid screen changes
790 redraw_mask = REDRAW_ALL;
794 void FadeOut(int fade_mask)
796 // update screen if areas covered by "fade_mask" and "redraw_mask" differ
797 if (!equalRedrawMasks(fade_mask, redraw_mask))
800 SetScreenStates_BeforeFadingOut();
803 DrawMaskedBorder(REDRAW_ALL);
806 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
807 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
809 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
811 SetScreenStates_AfterFadingOut();
814 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
816 static struct TitleFadingInfo fading_leave_stored;
819 fading_leave_stored = fading_leave;
821 fading = fading_leave_stored;
824 void FadeSetEnterMenu()
826 fading = menu.enter_menu;
828 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
831 void FadeSetLeaveMenu()
833 fading = menu.leave_menu;
835 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
838 void FadeSetEnterScreen()
840 fading = menu.enter_screen[game_status];
842 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
845 void FadeSetNextScreen()
847 fading = menu.next_screen[game_status];
849 // (do not overwrite fade mode set by FadeSetEnterScreen)
850 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
853 void FadeSetLeaveScreen()
855 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
858 void FadeSetFromType(int type)
860 if (type & TYPE_ENTER_SCREEN)
861 FadeSetEnterScreen();
862 else if (type & TYPE_ENTER)
864 else if (type & TYPE_LEAVE)
868 void FadeSetDisabled()
870 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
872 fading = fading_none;
875 void FadeSkipNextFadeIn()
877 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
880 void FadeSkipNextFadeOut()
882 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
885 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
887 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
889 return (graphic == IMG_UNDEFINED ? NULL :
890 graphic_info[graphic].bitmap != NULL || redefined ?
891 graphic_info[graphic].bitmap :
892 graphic_info[default_graphic].bitmap);
895 Bitmap *getBackgroundBitmap(int graphic)
897 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
900 Bitmap *getGlobalBorderBitmap(int graphic)
902 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
905 Bitmap *getGlobalBorderBitmapFromStatus(int status)
908 (status == GAME_MODE_MAIN ||
909 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
910 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
911 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
912 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
915 return getGlobalBorderBitmap(graphic);
918 void SetWindowBackgroundImageIfDefined(int graphic)
920 if (graphic_info[graphic].bitmap)
921 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
924 void SetMainBackgroundImageIfDefined(int graphic)
926 if (graphic_info[graphic].bitmap)
927 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
930 void SetDoorBackgroundImageIfDefined(int graphic)
932 if (graphic_info[graphic].bitmap)
933 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
936 void SetWindowBackgroundImage(int graphic)
938 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
941 void SetMainBackgroundImage(int graphic)
943 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
946 void SetDoorBackgroundImage(int graphic)
948 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
951 void SetPanelBackground()
953 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
955 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
956 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
958 SetDoorBackgroundBitmap(bitmap_db_panel);
961 void DrawBackground(int x, int y, int width, int height)
963 /* "drawto" might still point to playfield buffer here (hall of fame) */
964 ClearRectangleOnBackground(backbuffer, x, y, width, height);
966 if (IN_GFX_FIELD_FULL(x, y))
967 redraw_mask |= REDRAW_FIELD;
968 else if (IN_GFX_DOOR_1(x, y))
969 redraw_mask |= REDRAW_DOOR_1;
970 else if (IN_GFX_DOOR_2(x, y))
971 redraw_mask |= REDRAW_DOOR_2;
972 else if (IN_GFX_DOOR_3(x, y))
973 redraw_mask |= REDRAW_DOOR_3;
976 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
978 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
980 if (font->bitmap == NULL)
983 DrawBackground(x, y, width, height);
986 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
988 struct GraphicInfo *g = &graphic_info[graphic];
990 if (g->bitmap == NULL)
993 DrawBackground(x, y, width, height);
996 static int game_status_last = -1;
997 static Bitmap *global_border_bitmap_last = NULL;
998 static Bitmap *global_border_bitmap = NULL;
999 static int real_sx_last = -1, real_sy_last = -1;
1000 static int full_sxsize_last = -1, full_sysize_last = -1;
1001 static int dx_last = -1, dy_last = -1;
1002 static int dxsize_last = -1, dysize_last = -1;
1003 static int vx_last = -1, vy_last = -1;
1004 static int vxsize_last = -1, vysize_last = -1;
1006 boolean CheckIfGlobalBorderHasChanged()
1008 // if game status has not changed, global border has not changed either
1009 if (game_status == game_status_last)
1012 // determine and store new global border bitmap for current game status
1013 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
1015 return (global_border_bitmap_last != global_border_bitmap);
1018 boolean CheckIfGlobalBorderRedrawIsNeeded()
1020 // if game status has not changed, nothing has to be redrawn
1021 if (game_status == game_status_last)
1024 // redraw if last screen was title screen
1025 if (game_status_last == GAME_MODE_TITLE)
1028 // redraw if global screen border has changed
1029 if (CheckIfGlobalBorderHasChanged())
1032 // redraw if position or size of playfield area has changed
1033 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
1034 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
1037 // redraw if position or size of door area has changed
1038 if (dx_last != DX || dy_last != DY ||
1039 dxsize_last != DXSIZE || dysize_last != DYSIZE)
1042 // redraw if position or size of tape area has changed
1043 if (vx_last != VX || vy_last != VY ||
1044 vxsize_last != VXSIZE || vysize_last != VYSIZE)
1050 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
1053 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1055 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
1058 void RedrawGlobalBorder()
1060 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
1062 RedrawGlobalBorderFromBitmap(bitmap);
1064 redraw_mask = REDRAW_ALL;
1067 static void RedrawGlobalBorderIfNeeded()
1069 if (game_status == game_status_last)
1072 // copy current draw buffer to later copy back areas that have not changed
1073 if (game_status_last != GAME_MODE_TITLE)
1074 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1076 if (CheckIfGlobalBorderRedrawIsNeeded())
1078 // redraw global screen border (or clear, if defined to be empty)
1079 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1081 // copy previous playfield and door areas, if they are defined on both
1082 // previous and current screen and if they still have the same size
1084 if (real_sx_last != -1 && real_sy_last != -1 &&
1085 REAL_SX != -1 && REAL_SY != -1 &&
1086 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1087 BlitBitmap(bitmap_db_store_1, backbuffer,
1088 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1091 if (dx_last != -1 && dy_last != -1 &&
1092 DX != -1 && DY != -1 &&
1093 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1094 BlitBitmap(bitmap_db_store_1, backbuffer,
1095 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1097 if (vx_last != -1 && vy_last != -1 &&
1098 VX != -1 && VY != -1 &&
1099 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1100 BlitBitmap(bitmap_db_store_1, backbuffer,
1101 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1103 redraw_mask = REDRAW_ALL;
1106 game_status_last = game_status;
1108 global_border_bitmap_last = global_border_bitmap;
1110 real_sx_last = REAL_SX;
1111 real_sy_last = REAL_SY;
1112 full_sxsize_last = FULL_SXSIZE;
1113 full_sysize_last = FULL_SYSIZE;
1116 dxsize_last = DXSIZE;
1117 dysize_last = DYSIZE;
1120 vxsize_last = VXSIZE;
1121 vysize_last = VYSIZE;
1126 RedrawGlobalBorderIfNeeded();
1128 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1129 /* (when entering hall of fame after playing) */
1130 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1132 /* !!! maybe this should be done before clearing the background !!! */
1133 if (game_status == GAME_MODE_PLAYING)
1135 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1136 SetDrawtoField(DRAW_TO_FIELDBUFFER);
1140 SetDrawtoField(DRAW_TO_BACKBUFFER);
1144 void MarkTileDirty(int x, int y)
1146 redraw_mask |= REDRAW_FIELD;
1149 void SetBorderElement()
1153 BorderElement = EL_EMPTY;
1155 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1157 for (x = 0; x < lev_fieldx; x++)
1159 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1160 BorderElement = EL_STEELWALL;
1162 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1168 void FloodFillLevel(int from_x, int from_y, int fill_element,
1169 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1170 int max_fieldx, int max_fieldy)
1174 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1175 static int safety = 0;
1177 /* check if starting field still has the desired content */
1178 if (field[from_x][from_y] == fill_element)
1183 if (safety > max_fieldx * max_fieldy)
1184 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1186 old_element = field[from_x][from_y];
1187 field[from_x][from_y] = fill_element;
1189 for (i = 0; i < 4; i++)
1191 x = from_x + check[i][0];
1192 y = from_y + check[i][1];
1194 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1195 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1201 void SetRandomAnimationValue(int x, int y)
1203 gfx.anim_random_frame = GfxRandom[x][y];
1206 int getGraphicAnimationFrame(int graphic, int sync_frame)
1208 /* animation synchronized with global frame counter, not move position */
1209 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1210 sync_frame = FrameCounter;
1212 return getAnimationFrame(graphic_info[graphic].anim_frames,
1213 graphic_info[graphic].anim_delay,
1214 graphic_info[graphic].anim_mode,
1215 graphic_info[graphic].anim_start_frame,
1219 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1220 Bitmap **bitmap, int *x, int *y,
1221 boolean get_backside)
1223 struct GraphicInfo *g = &graphic_info[graphic];
1224 Bitmap *src_bitmap = g->bitmap;
1225 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1226 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1227 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1229 // if no in-game graphics defined, always use standard graphic size
1230 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1231 tilesize = TILESIZE;
1233 if (tilesize == gfx.standard_tile_size)
1234 src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1235 else if (tilesize == game.tile_size)
1236 src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
1238 src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1240 if (g->offset_y == 0) /* frames are ordered horizontally */
1242 int max_width = g->anim_frames_per_line * g->width;
1243 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1245 src_x = pos % max_width;
1246 src_y = src_y % g->height + pos / max_width * g->height;
1248 else if (g->offset_x == 0) /* frames are ordered vertically */
1250 int max_height = g->anim_frames_per_line * g->height;
1251 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1253 src_x = src_x % g->width + pos / max_height * g->width;
1254 src_y = pos % max_height;
1256 else /* frames are ordered diagonally */
1258 src_x = src_x + frame * g->offset_x;
1259 src_y = src_y + frame * g->offset_y;
1262 *bitmap = src_bitmap;
1263 *x = src_x * tilesize / g->tile_size;
1264 *y = src_y * tilesize / g->tile_size;
1267 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1268 int *x, int *y, boolean get_backside)
1270 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1274 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1275 Bitmap **bitmap, int *x, int *y)
1277 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1280 void getFixedGraphicSource(int graphic, int frame,
1281 Bitmap **bitmap, int *x, int *y)
1283 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1286 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1288 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1291 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1292 int *x, int *y, boolean get_backside)
1294 struct GraphicInfo *g = &graphic_info[graphic];
1295 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1296 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1298 if (TILESIZE_VAR != TILESIZE)
1299 return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1302 *bitmap = g->bitmap;
1304 if (g->offset_y == 0) /* frames are ordered horizontally */
1306 int max_width = g->anim_frames_per_line * g->width;
1307 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1309 *x = pos % max_width;
1310 *y = src_y % g->height + pos / max_width * g->height;
1312 else if (g->offset_x == 0) /* frames are ordered vertically */
1314 int max_height = g->anim_frames_per_line * g->height;
1315 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1317 *x = src_x % g->width + pos / max_height * g->width;
1318 *y = pos % max_height;
1320 else /* frames are ordered diagonally */
1322 *x = src_x + frame * g->offset_x;
1323 *y = src_y + frame * g->offset_y;
1326 *x = *x * TILESIZE_VAR / g->tile_size;
1327 *y = *y * TILESIZE_VAR / g->tile_size;
1330 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1332 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1335 void DrawGraphic(int x, int y, int graphic, int frame)
1338 if (!IN_SCR_FIELD(x, y))
1340 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1341 printf("DrawGraphic(): This should never happen!\n");
1346 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1349 MarkTileDirty(x, y);
1352 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1355 if (!IN_SCR_FIELD(x, y))
1357 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1358 printf("DrawGraphic(): This should never happen!\n");
1363 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1365 MarkTileDirty(x, y);
1368 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1374 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1376 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1379 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1385 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1386 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1389 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1392 if (!IN_SCR_FIELD(x, y))
1394 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1395 printf("DrawGraphicThruMask(): This should never happen!\n");
1400 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1403 MarkTileDirty(x, y);
1406 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1409 if (!IN_SCR_FIELD(x, y))
1411 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1412 printf("DrawGraphicThruMask(): This should never happen!\n");
1417 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1419 MarkTileDirty(x, y);
1422 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1428 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1430 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1434 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1435 int graphic, int frame)
1440 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1442 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1446 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1448 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1450 MarkTileDirty(x / tilesize, y / tilesize);
1453 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1459 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1460 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1463 void DrawMiniGraphic(int x, int y, int graphic)
1465 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1466 MarkTileDirty(x / 2, y / 2);
1469 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1474 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1475 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1478 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1479 int graphic, int frame,
1480 int cut_mode, int mask_mode)
1485 int width = TILEX, height = TILEY;
1488 if (dx || dy) /* shifted graphic */
1490 if (x < BX1) /* object enters playfield from the left */
1497 else if (x > BX2) /* object enters playfield from the right */
1503 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1509 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1511 else if (dx) /* general horizontal movement */
1512 MarkTileDirty(x + SIGN(dx), y);
1514 if (y < BY1) /* object enters playfield from the top */
1516 if (cut_mode == CUT_BELOW) /* object completely above top border */
1524 else if (y > BY2) /* object enters playfield from the bottom */
1530 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1536 else if (dy > 0 && cut_mode == CUT_ABOVE)
1538 if (y == BY2) /* object completely above bottom border */
1544 MarkTileDirty(x, y + 1);
1545 } /* object leaves playfield to the bottom */
1546 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1548 else if (dy) /* general vertical movement */
1549 MarkTileDirty(x, y + SIGN(dy));
1553 if (!IN_SCR_FIELD(x, y))
1555 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1556 printf("DrawGraphicShifted(): This should never happen!\n");
1561 width = width * TILESIZE_VAR / TILESIZE;
1562 height = height * TILESIZE_VAR / TILESIZE;
1563 cx = cx * TILESIZE_VAR / TILESIZE;
1564 cy = cy * TILESIZE_VAR / TILESIZE;
1565 dx = dx * TILESIZE_VAR / TILESIZE;
1566 dy = dy * TILESIZE_VAR / TILESIZE;
1568 if (width > 0 && height > 0)
1570 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1575 dst_x = FX + x * TILEX_VAR + dx;
1576 dst_y = FY + y * TILEY_VAR + dy;
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(x, y);
1589 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1590 int graphic, int frame,
1591 int cut_mode, int mask_mode)
1596 int width = TILEX_VAR, height = TILEY_VAR;
1599 int x2 = x + SIGN(dx);
1600 int y2 = y + SIGN(dy);
1602 /* movement with two-tile animations must be sync'ed with movement position,
1603 not with current GfxFrame (which can be higher when using slow movement) */
1604 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1605 int anim_frames = graphic_info[graphic].anim_frames;
1607 /* (we also need anim_delay here for movement animations with less frames) */
1608 int anim_delay = graphic_info[graphic].anim_delay;
1609 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1611 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1612 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1614 /* re-calculate animation frame for two-tile movement animation */
1615 frame = getGraphicAnimationFrame(graphic, sync_frame);
1617 /* check if movement start graphic inside screen area and should be drawn */
1618 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1620 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1622 dst_x = FX + x1 * TILEX_VAR;
1623 dst_y = FY + y1 * TILEY_VAR;
1625 if (mask_mode == USE_MASKING)
1626 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1629 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1632 MarkTileDirty(x1, y1);
1635 /* check if movement end graphic inside screen area and should be drawn */
1636 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1638 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1640 dst_x = FX + x2 * TILEX_VAR;
1641 dst_y = FY + y2 * TILEY_VAR;
1643 if (mask_mode == USE_MASKING)
1644 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1647 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1650 MarkTileDirty(x2, y2);
1654 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1655 int graphic, int frame,
1656 int cut_mode, int mask_mode)
1660 DrawGraphic(x, y, graphic, frame);
1665 if (graphic_info[graphic].double_movement) /* EM style movement images */
1666 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1668 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1671 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1672 int frame, int cut_mode)
1674 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1677 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1678 int cut_mode, int mask_mode)
1680 int lx = LEVELX(x), ly = LEVELY(y);
1684 if (IN_LEV_FIELD(lx, ly))
1686 SetRandomAnimationValue(lx, ly);
1688 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1689 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1691 /* do not use double (EM style) movement graphic when not moving */
1692 if (graphic_info[graphic].double_movement && !dx && !dy)
1694 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1695 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1698 else /* border element */
1700 graphic = el2img(element);
1701 frame = getGraphicAnimationFrame(graphic, -1);
1704 if (element == EL_EXPANDABLE_WALL)
1706 boolean left_stopped = FALSE, right_stopped = FALSE;
1708 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1709 left_stopped = TRUE;
1710 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1711 right_stopped = TRUE;
1713 if (left_stopped && right_stopped)
1715 else if (left_stopped)
1717 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1718 frame = graphic_info[graphic].anim_frames - 1;
1720 else if (right_stopped)
1722 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1723 frame = graphic_info[graphic].anim_frames - 1;
1728 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1729 else if (mask_mode == USE_MASKING)
1730 DrawGraphicThruMask(x, y, graphic, frame);
1732 DrawGraphic(x, y, graphic, frame);
1735 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1736 int cut_mode, int mask_mode)
1738 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1739 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1740 cut_mode, mask_mode);
1743 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1746 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1749 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1752 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1755 void DrawLevelElementThruMask(int x, int y, int element)
1757 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1760 void DrawLevelFieldThruMask(int x, int y)
1762 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1765 /* !!! implementation of quicksand is totally broken !!! */
1766 #define IS_CRUMBLED_TILE(x, y, e) \
1767 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1768 !IS_MOVING(x, y) || \
1769 (e) == EL_QUICKSAND_EMPTYING || \
1770 (e) == EL_QUICKSAND_FAST_EMPTYING))
1772 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1777 int width, height, cx, cy;
1778 int sx = SCREENX(x), sy = SCREENY(y);
1779 int crumbled_border_size = graphic_info[graphic].border_size;
1782 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1784 for (i = 1; i < 4; i++)
1786 int dxx = (i & 1 ? dx : 0);
1787 int dyy = (i & 2 ? dy : 0);
1790 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1793 /* check if neighbour field is of same crumble type */
1794 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1795 graphic_info[graphic].class ==
1796 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1798 /* return if check prevents inner corner */
1799 if (same == (dxx == dx && dyy == dy))
1803 /* if we reach this point, we have an inner corner */
1805 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1807 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1808 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1809 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1810 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1812 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1813 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1816 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1821 int width, height, bx, by, cx, cy;
1822 int sx = SCREENX(x), sy = SCREENY(y);
1823 int crumbled_border_size = graphic_info[graphic].border_size;
1824 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1825 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1828 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1830 /* draw simple, sloppy, non-corner-accurate crumbled border */
1832 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1833 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1834 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1835 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1837 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1838 FX + sx * TILEX_VAR + cx,
1839 FY + sy * TILEY_VAR + cy);
1841 /* (remaining middle border part must be at least as big as corner part) */
1842 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1843 crumbled_border_size >= TILESIZE / 3)
1846 /* correct corners of crumbled border, if needed */
1848 for (i = -1; i <= 1; i += 2)
1850 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1851 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1852 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1855 /* check if neighbour field is of same crumble type */
1856 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1857 graphic_info[graphic].class ==
1858 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1860 /* no crumbled corner, but continued crumbled border */
1862 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1863 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1864 int b1 = (i == 1 ? crumbled_border_size_var :
1865 TILESIZE_VAR - 2 * crumbled_border_size_var);
1867 width = crumbled_border_size_var;
1868 height = crumbled_border_size_var;
1870 if (dir == 1 || dir == 2)
1885 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1887 FX + sx * TILEX_VAR + cx,
1888 FY + sy * TILEY_VAR + cy);
1893 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1895 int sx = SCREENX(x), sy = SCREENY(y);
1898 static int xy[4][2] =
1906 if (!IN_LEV_FIELD(x, y))
1909 element = TILE_GFX_ELEMENT(x, y);
1911 /* crumble field itself */
1912 if (IS_CRUMBLED_TILE(x, y, element))
1914 if (!IN_SCR_FIELD(sx, sy))
1917 for (i = 0; i < 4; i++)
1919 int xx = x + xy[i][0];
1920 int yy = y + xy[i][1];
1922 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1925 /* check if neighbour field is of same crumble type */
1926 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1927 graphic_info[graphic].class ==
1928 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1931 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1934 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1935 graphic_info[graphic].anim_frames == 2)
1937 for (i = 0; i < 4; i++)
1939 int dx = (i & 1 ? +1 : -1);
1940 int dy = (i & 2 ? +1 : -1);
1942 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1946 MarkTileDirty(sx, sy);
1948 else /* center field not crumbled -- crumble neighbour fields */
1950 for (i = 0; i < 4; i++)
1952 int xx = x + xy[i][0];
1953 int yy = y + xy[i][1];
1954 int sxx = sx + xy[i][0];
1955 int syy = sy + xy[i][1];
1957 if (!IN_LEV_FIELD(xx, yy) ||
1958 !IN_SCR_FIELD(sxx, syy))
1961 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1964 element = TILE_GFX_ELEMENT(xx, yy);
1966 if (!IS_CRUMBLED_TILE(xx, yy, element))
1969 graphic = el_act2crm(element, ACTION_DEFAULT);
1971 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1973 MarkTileDirty(sxx, syy);
1978 void DrawLevelFieldCrumbled(int x, int y)
1982 if (!IN_LEV_FIELD(x, y))
1985 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1986 GfxElement[x][y] != EL_UNDEFINED &&
1987 GFX_CRUMBLED(GfxElement[x][y]))
1989 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1994 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1996 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1999 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
2002 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
2003 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
2004 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
2005 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
2006 int sx = SCREENX(x), sy = SCREENY(y);
2008 DrawGraphic(sx, sy, graphic1, frame1);
2009 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
2012 void DrawLevelFieldCrumbledNeighbours(int x, int y)
2014 int sx = SCREENX(x), sy = SCREENY(y);
2015 static int xy[4][2] =
2024 for (i = 0; i < 4; i++)
2026 int xx = x + xy[i][0];
2027 int yy = y + xy[i][1];
2028 int sxx = sx + xy[i][0];
2029 int syy = sy + xy[i][1];
2031 if (!IN_LEV_FIELD(xx, yy) ||
2032 !IN_SCR_FIELD(sxx, syy) ||
2033 !GFX_CRUMBLED(Feld[xx][yy]) ||
2037 DrawLevelField(xx, yy);
2041 static int getBorderElement(int x, int y)
2045 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
2046 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
2047 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
2048 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
2049 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
2050 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
2051 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
2053 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
2054 int steel_position = (x == -1 && y == -1 ? 0 :
2055 x == lev_fieldx && y == -1 ? 1 :
2056 x == -1 && y == lev_fieldy ? 2 :
2057 x == lev_fieldx && y == lev_fieldy ? 3 :
2058 x == -1 || x == lev_fieldx ? 4 :
2059 y == -1 || y == lev_fieldy ? 5 : 6);
2061 return border[steel_position][steel_type];
2064 void DrawScreenElement(int x, int y, int element)
2066 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
2067 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2070 void DrawLevelElement(int x, int y, int element)
2072 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2073 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2076 void DrawScreenField(int x, int y)
2078 int lx = LEVELX(x), ly = LEVELY(y);
2079 int element, content;
2081 if (!IN_LEV_FIELD(lx, ly))
2083 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2086 element = getBorderElement(lx, ly);
2088 DrawScreenElement(x, y, element);
2093 element = Feld[lx][ly];
2094 content = Store[lx][ly];
2096 if (IS_MOVING(lx, ly))
2098 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2099 boolean cut_mode = NO_CUTTING;
2101 if (element == EL_QUICKSAND_EMPTYING ||
2102 element == EL_QUICKSAND_FAST_EMPTYING ||
2103 element == EL_MAGIC_WALL_EMPTYING ||
2104 element == EL_BD_MAGIC_WALL_EMPTYING ||
2105 element == EL_DC_MAGIC_WALL_EMPTYING ||
2106 element == EL_AMOEBA_DROPPING)
2107 cut_mode = CUT_ABOVE;
2108 else if (element == EL_QUICKSAND_FILLING ||
2109 element == EL_QUICKSAND_FAST_FILLING ||
2110 element == EL_MAGIC_WALL_FILLING ||
2111 element == EL_BD_MAGIC_WALL_FILLING ||
2112 element == EL_DC_MAGIC_WALL_FILLING)
2113 cut_mode = CUT_BELOW;
2115 if (cut_mode == CUT_ABOVE)
2116 DrawScreenElement(x, y, element);
2118 DrawScreenElement(x, y, EL_EMPTY);
2121 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2122 else if (cut_mode == NO_CUTTING)
2123 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2126 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2128 if (cut_mode == CUT_BELOW &&
2129 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2130 DrawLevelElement(lx, ly + 1, element);
2133 if (content == EL_ACID)
2135 int dir = MovDir[lx][ly];
2136 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2137 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2139 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2142 else if (IS_BLOCKED(lx, ly))
2147 boolean cut_mode = NO_CUTTING;
2148 int element_old, content_old;
2150 Blocked2Moving(lx, ly, &oldx, &oldy);
2153 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2154 MovDir[oldx][oldy] == MV_RIGHT);
2156 element_old = Feld[oldx][oldy];
2157 content_old = Store[oldx][oldy];
2159 if (element_old == EL_QUICKSAND_EMPTYING ||
2160 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2161 element_old == EL_MAGIC_WALL_EMPTYING ||
2162 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2163 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2164 element_old == EL_AMOEBA_DROPPING)
2165 cut_mode = CUT_ABOVE;
2167 DrawScreenElement(x, y, EL_EMPTY);
2170 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2172 else if (cut_mode == NO_CUTTING)
2173 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2176 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2179 else if (IS_DRAWABLE(element))
2180 DrawScreenElement(x, y, element);
2182 DrawScreenElement(x, y, EL_EMPTY);
2185 void DrawLevelField(int x, int y)
2187 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2188 DrawScreenField(SCREENX(x), SCREENY(y));
2189 else if (IS_MOVING(x, y))
2193 Moving2Blocked(x, y, &newx, &newy);
2194 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2195 DrawScreenField(SCREENX(newx), SCREENY(newy));
2197 else if (IS_BLOCKED(x, y))
2201 Blocked2Moving(x, y, &oldx, &oldy);
2202 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2203 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2207 void DrawSizedElement(int x, int y, int element, int tilesize)
2211 graphic = el2edimg(element);
2212 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2215 void DrawMiniElement(int x, int y, int element)
2219 graphic = el2edimg(element);
2220 DrawMiniGraphic(x, y, graphic);
2223 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2226 int x = sx + scroll_x, y = sy + scroll_y;
2228 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2229 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2230 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2231 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2233 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2236 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2238 int x = sx + scroll_x, y = sy + scroll_y;
2240 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2241 DrawMiniElement(sx, sy, EL_EMPTY);
2242 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2243 DrawMiniElement(sx, sy, Feld[x][y]);
2245 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2248 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2249 int x, int y, int xsize, int ysize,
2250 int tile_width, int tile_height)
2254 int dst_x = startx + x * tile_width;
2255 int dst_y = starty + y * tile_height;
2256 int width = graphic_info[graphic].width;
2257 int height = graphic_info[graphic].height;
2258 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2259 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2260 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2261 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2262 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2263 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2264 boolean draw_masked = graphic_info[graphic].draw_masked;
2266 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2268 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2270 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2274 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2275 inner_sx + (x - 1) * tile_width % inner_width);
2276 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2277 inner_sy + (y - 1) * tile_height % inner_height);
2280 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2283 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2287 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2288 int x, int y, int xsize, int ysize, int font_nr)
2290 int font_width = getFontWidth(font_nr);
2291 int font_height = getFontHeight(font_nr);
2293 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2294 font_width, font_height);
2297 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2299 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2300 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2301 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2302 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2303 boolean no_delay = (tape.warp_forward);
2304 unsigned int anim_delay = 0;
2305 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2306 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2307 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2308 int font_width = getFontWidth(font_nr);
2309 int font_height = getFontHeight(font_nr);
2310 int max_xsize = level.envelope[envelope_nr].xsize;
2311 int max_ysize = level.envelope[envelope_nr].ysize;
2312 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2313 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2314 int xend = max_xsize;
2315 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2316 int xstep = (xstart < xend ? 1 : 0);
2317 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2319 int end = MAX(xend - xstart, yend - ystart);
2322 for (i = start; i <= end; i++)
2324 int last_frame = end; // last frame of this "for" loop
2325 int x = xstart + i * xstep;
2326 int y = ystart + i * ystep;
2327 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2328 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2329 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2330 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2333 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2335 BlitScreenToBitmap(backbuffer);
2337 SetDrawtoField(DRAW_TO_BACKBUFFER);
2339 for (yy = 0; yy < ysize; yy++)
2340 for (xx = 0; xx < xsize; xx++)
2341 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2343 DrawTextBuffer(sx + font_width, sy + font_height,
2344 level.envelope[envelope_nr].text, font_nr, max_xsize,
2345 xsize - 2, ysize - 2, 0, mask_mode,
2346 level.envelope[envelope_nr].autowrap,
2347 level.envelope[envelope_nr].centered, FALSE);
2349 redraw_mask |= REDRAW_FIELD;
2352 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2356 void ShowEnvelope(int envelope_nr)
2358 int element = EL_ENVELOPE_1 + envelope_nr;
2359 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2360 int sound_opening = element_info[element].sound[ACTION_OPENING];
2361 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2362 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2363 boolean no_delay = (tape.warp_forward);
2364 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2365 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2366 int anim_mode = graphic_info[graphic].anim_mode;
2367 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2368 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2370 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2372 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2374 if (anim_mode == ANIM_DEFAULT)
2375 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2377 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2380 Delay(wait_delay_value);
2382 WaitForEventToContinue();
2384 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2386 if (anim_mode != ANIM_NONE)
2387 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2389 if (anim_mode == ANIM_DEFAULT)
2390 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2392 game.envelope_active = FALSE;
2394 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2396 redraw_mask |= REDRAW_FIELD;
2400 static void setRequestBasePosition(int *x, int *y)
2402 int sx_base, sy_base;
2404 if (request.x != -1)
2405 sx_base = request.x;
2406 else if (request.align == ALIGN_LEFT)
2408 else if (request.align == ALIGN_RIGHT)
2409 sx_base = SX + SXSIZE;
2411 sx_base = SX + SXSIZE / 2;
2413 if (request.y != -1)
2414 sy_base = request.y;
2415 else if (request.valign == VALIGN_TOP)
2417 else if (request.valign == VALIGN_BOTTOM)
2418 sy_base = SY + SYSIZE;
2420 sy_base = SY + SYSIZE / 2;
2426 static void setRequestPositionExt(int *x, int *y, int width, int height,
2427 boolean add_border_size)
2429 int border_size = request.border_size;
2430 int sx_base, sy_base;
2433 setRequestBasePosition(&sx_base, &sy_base);
2435 if (request.align == ALIGN_LEFT)
2437 else if (request.align == ALIGN_RIGHT)
2438 sx = sx_base - width;
2440 sx = sx_base - width / 2;
2442 if (request.valign == VALIGN_TOP)
2444 else if (request.valign == VALIGN_BOTTOM)
2445 sy = sy_base - height;
2447 sy = sy_base - height / 2;
2449 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2450 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2452 if (add_border_size)
2462 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2464 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2467 void DrawEnvelopeRequest(char *text)
2469 char *text_final = text;
2470 char *text_door_style = NULL;
2471 int graphic = IMG_BACKGROUND_REQUEST;
2472 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2473 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2474 int font_nr = FONT_REQUEST;
2475 int font_width = getFontWidth(font_nr);
2476 int font_height = getFontHeight(font_nr);
2477 int border_size = request.border_size;
2478 int line_spacing = request.line_spacing;
2479 int line_height = font_height + line_spacing;
2480 int max_text_width = request.width - 2 * border_size;
2481 int max_text_height = request.height - 2 * border_size;
2482 int line_length = max_text_width / font_width;
2483 int max_lines = max_text_height / line_height;
2484 int text_width = line_length * font_width;
2485 int width = request.width;
2486 int height = request.height;
2487 int tile_size = MAX(request.step_offset, 1);
2488 int x_steps = width / tile_size;
2489 int y_steps = height / tile_size;
2490 int sx_offset = border_size;
2491 int sy_offset = border_size;
2495 if (request.centered)
2496 sx_offset = (request.width - text_width) / 2;
2498 if (request.wrap_single_words && !request.autowrap)
2500 char *src_text_ptr, *dst_text_ptr;
2502 text_door_style = checked_malloc(2 * strlen(text) + 1);
2504 src_text_ptr = text;
2505 dst_text_ptr = text_door_style;
2507 while (*src_text_ptr)
2509 if (*src_text_ptr == ' ' ||
2510 *src_text_ptr == '?' ||
2511 *src_text_ptr == '!')
2512 *dst_text_ptr++ = '\n';
2514 if (*src_text_ptr != ' ')
2515 *dst_text_ptr++ = *src_text_ptr;
2520 *dst_text_ptr = '\0';
2522 text_final = text_door_style;
2525 setRequestPosition(&sx, &sy, FALSE);
2527 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2529 for (y = 0; y < y_steps; y++)
2530 for (x = 0; x < x_steps; x++)
2531 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2532 x, y, x_steps, y_steps,
2533 tile_size, tile_size);
2535 /* force DOOR font inside door area */
2536 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
2538 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2539 line_length, -1, max_lines, line_spacing, mask_mode,
2540 request.autowrap, request.centered, FALSE);
2544 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2545 RedrawGadget(tool_gadget[i]);
2547 // store readily prepared envelope request for later use when animating
2548 BlitBitmap(backbuffer, bitmap_db_store_2, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2550 if (text_door_style)
2551 free(text_door_style);
2554 void AnimateEnvelopeRequest(int anim_mode, int action)
2556 int graphic = IMG_BACKGROUND_REQUEST;
2557 boolean draw_masked = graphic_info[graphic].draw_masked;
2558 int delay_value_normal = request.step_delay;
2559 int delay_value_fast = delay_value_normal / 2;
2560 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2561 boolean no_delay = (tape.warp_forward);
2562 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2563 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2564 unsigned int anim_delay = 0;
2566 int tile_size = MAX(request.step_offset, 1);
2567 int max_xsize = request.width / tile_size;
2568 int max_ysize = request.height / tile_size;
2569 int max_xsize_inner = max_xsize - 2;
2570 int max_ysize_inner = max_ysize - 2;
2572 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2573 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2574 int xend = max_xsize_inner;
2575 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2576 int xstep = (xstart < xend ? 1 : 0);
2577 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2579 int end = MAX(xend - xstart, yend - ystart);
2582 if (setup.quick_doors)
2589 for (i = start; i <= end; i++)
2591 int last_frame = end; // last frame of this "for" loop
2592 int x = xstart + i * xstep;
2593 int y = ystart + i * ystep;
2594 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2595 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2596 int xsize_size_left = (xsize - 1) * tile_size;
2597 int ysize_size_top = (ysize - 1) * tile_size;
2598 int max_xsize_pos = (max_xsize - 1) * tile_size;
2599 int max_ysize_pos = (max_ysize - 1) * tile_size;
2600 int width = xsize * tile_size;
2601 int height = ysize * tile_size;
2606 setRequestPosition(&src_x, &src_y, FALSE);
2607 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2609 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2611 for (yy = 0; yy < 2; yy++)
2613 for (xx = 0; xx < 2; xx++)
2615 int src_xx = src_x + xx * max_xsize_pos;
2616 int src_yy = src_y + yy * max_ysize_pos;
2617 int dst_xx = dst_x + xx * xsize_size_left;
2618 int dst_yy = dst_y + yy * ysize_size_top;
2619 int xx_size = (xx ? tile_size : xsize_size_left);
2620 int yy_size = (yy ? tile_size : ysize_size_top);
2623 BlitBitmapMasked(bitmap_db_store_2, backbuffer,
2624 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2626 BlitBitmap(bitmap_db_store_2, backbuffer,
2627 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2631 redraw_mask |= REDRAW_FIELD;
2635 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2639 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2641 int graphic = IMG_BACKGROUND_REQUEST;
2642 int sound_opening = SND_REQUEST_OPENING;
2643 int sound_closing = SND_REQUEST_CLOSING;
2644 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2645 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2646 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2647 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2648 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2650 if (game_status == GAME_MODE_PLAYING)
2651 BlitScreenToBitmap(backbuffer);
2653 SetDrawtoField(DRAW_TO_BACKBUFFER);
2655 // SetDrawBackgroundMask(REDRAW_NONE);
2657 if (action == ACTION_OPENING)
2659 BlitBitmap(backbuffer, bitmap_db_store_1, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2661 if (req_state & REQ_ASK)
2663 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2664 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2666 else if (req_state & REQ_CONFIRM)
2668 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2670 else if (req_state & REQ_PLAYER)
2672 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2673 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2674 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2675 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2678 DrawEnvelopeRequest(text);
2681 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2683 if (action == ACTION_OPENING)
2685 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2687 if (anim_mode == ANIM_DEFAULT)
2688 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2690 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2694 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2696 if (anim_mode != ANIM_NONE)
2697 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2699 if (anim_mode == ANIM_DEFAULT)
2700 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2703 game.envelope_active = FALSE;
2705 if (action == ACTION_CLOSING)
2706 BlitBitmap(bitmap_db_store_1, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2708 // SetDrawBackgroundMask(last_draw_background_mask);
2710 redraw_mask |= REDRAW_FIELD;
2714 if (action == ACTION_CLOSING &&
2715 game_status == GAME_MODE_PLAYING &&
2716 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2717 SetDrawtoField(DRAW_TO_FIELDBUFFER);
2720 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2724 int graphic = el2preimg(element);
2726 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2727 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2730 void DrawLevel(int draw_background_mask)
2734 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2735 SetDrawBackgroundMask(draw_background_mask);
2739 for (x = BX1; x <= BX2; x++)
2740 for (y = BY1; y <= BY2; y++)
2741 DrawScreenField(x, y);
2743 redraw_mask |= REDRAW_FIELD;
2746 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2751 for (x = 0; x < size_x; x++)
2752 for (y = 0; y < size_y; y++)
2753 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2755 redraw_mask |= REDRAW_FIELD;
2758 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2762 for (x = 0; x < size_x; x++)
2763 for (y = 0; y < size_y; y++)
2764 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2766 redraw_mask |= REDRAW_FIELD;
2769 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2771 boolean show_level_border = (BorderElement != EL_EMPTY);
2772 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2773 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2774 int tile_size = preview.tile_size;
2775 int preview_width = preview.xsize * tile_size;
2776 int preview_height = preview.ysize * tile_size;
2777 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2778 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2779 int real_preview_width = real_preview_xsize * tile_size;
2780 int real_preview_height = real_preview_ysize * tile_size;
2781 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2782 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2785 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2788 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2790 dst_x += (preview_width - real_preview_width) / 2;
2791 dst_y += (preview_height - real_preview_height) / 2;
2793 for (x = 0; x < real_preview_xsize; x++)
2795 for (y = 0; y < real_preview_ysize; y++)
2797 int lx = from_x + x + (show_level_border ? -1 : 0);
2798 int ly = from_y + y + (show_level_border ? -1 : 0);
2799 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2800 getBorderElement(lx, ly));
2802 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2803 element, tile_size);
2807 redraw_mask |= REDRAW_FIELD;
2810 #define MICROLABEL_EMPTY 0
2811 #define MICROLABEL_LEVEL_NAME 1
2812 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2813 #define MICROLABEL_LEVEL_AUTHOR 3
2814 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2815 #define MICROLABEL_IMPORTED_FROM 5
2816 #define MICROLABEL_IMPORTED_BY_HEAD 6
2817 #define MICROLABEL_IMPORTED_BY 7
2819 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2821 int max_text_width = SXSIZE;
2822 int font_width = getFontWidth(font_nr);
2824 if (pos->align == ALIGN_CENTER)
2825 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2826 else if (pos->align == ALIGN_RIGHT)
2827 max_text_width = pos->x;
2829 max_text_width = SXSIZE - pos->x;
2831 return max_text_width / font_width;
2834 static void DrawPreviewLevelLabelExt(int mode)
2836 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2837 char label_text[MAX_OUTPUT_LINESIZE + 1];
2838 int max_len_label_text;
2839 int font_nr = pos->font;
2842 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2845 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2846 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2847 mode == MICROLABEL_IMPORTED_BY_HEAD)
2848 font_nr = pos->font_alt;
2850 max_len_label_text = getMaxTextLength(pos, font_nr);
2852 if (pos->size != -1)
2853 max_len_label_text = pos->size;
2855 for (i = 0; i < max_len_label_text; i++)
2856 label_text[i] = ' ';
2857 label_text[max_len_label_text] = '\0';
2859 if (strlen(label_text) > 0)
2860 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2863 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2864 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2865 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2866 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2867 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2868 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2869 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2870 max_len_label_text);
2871 label_text[max_len_label_text] = '\0';
2873 if (strlen(label_text) > 0)
2874 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2876 redraw_mask |= REDRAW_FIELD;
2879 static void DrawPreviewLevelExt(boolean restart)
2881 static unsigned int scroll_delay = 0;
2882 static unsigned int label_delay = 0;
2883 static int from_x, from_y, scroll_direction;
2884 static int label_state, label_counter;
2885 unsigned int scroll_delay_value = preview.step_delay;
2886 boolean show_level_border = (BorderElement != EL_EMPTY);
2887 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2888 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2895 if (preview.anim_mode == ANIM_CENTERED)
2897 if (level_xsize > preview.xsize)
2898 from_x = (level_xsize - preview.xsize) / 2;
2899 if (level_ysize > preview.ysize)
2900 from_y = (level_ysize - preview.ysize) / 2;
2903 from_x += preview.xoffset;
2904 from_y += preview.yoffset;
2906 scroll_direction = MV_RIGHT;
2910 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2911 DrawPreviewLevelLabelExt(label_state);
2913 /* initialize delay counters */
2914 DelayReached(&scroll_delay, 0);
2915 DelayReached(&label_delay, 0);
2917 if (leveldir_current->name)
2919 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2920 char label_text[MAX_OUTPUT_LINESIZE + 1];
2921 int font_nr = pos->font;
2922 int max_len_label_text = getMaxTextLength(pos, font_nr);
2924 if (pos->size != -1)
2925 max_len_label_text = pos->size;
2927 strncpy(label_text, leveldir_current->name, max_len_label_text);
2928 label_text[max_len_label_text] = '\0';
2930 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2931 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2937 /* scroll preview level, if needed */
2938 if (preview.anim_mode != ANIM_NONE &&
2939 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2940 DelayReached(&scroll_delay, scroll_delay_value))
2942 switch (scroll_direction)
2947 from_x -= preview.step_offset;
2948 from_x = (from_x < 0 ? 0 : from_x);
2951 scroll_direction = MV_UP;
2955 if (from_x < level_xsize - preview.xsize)
2957 from_x += preview.step_offset;
2958 from_x = (from_x > level_xsize - preview.xsize ?
2959 level_xsize - preview.xsize : from_x);
2962 scroll_direction = MV_DOWN;
2968 from_y -= preview.step_offset;
2969 from_y = (from_y < 0 ? 0 : from_y);
2972 scroll_direction = MV_RIGHT;
2976 if (from_y < level_ysize - preview.ysize)
2978 from_y += preview.step_offset;
2979 from_y = (from_y > level_ysize - preview.ysize ?
2980 level_ysize - preview.ysize : from_y);
2983 scroll_direction = MV_LEFT;
2990 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2993 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2994 /* redraw micro level label, if needed */
2995 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2996 !strEqual(level.author, ANONYMOUS_NAME) &&
2997 !strEqual(level.author, leveldir_current->name) &&
2998 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
3000 int max_label_counter = 23;
3002 if (leveldir_current->imported_from != NULL &&
3003 strlen(leveldir_current->imported_from) > 0)
3004 max_label_counter += 14;
3005 if (leveldir_current->imported_by != NULL &&
3006 strlen(leveldir_current->imported_by) > 0)
3007 max_label_counter += 14;
3009 label_counter = (label_counter + 1) % max_label_counter;
3010 label_state = (label_counter >= 0 && label_counter <= 7 ?
3011 MICROLABEL_LEVEL_NAME :
3012 label_counter >= 9 && label_counter <= 12 ?
3013 MICROLABEL_LEVEL_AUTHOR_HEAD :
3014 label_counter >= 14 && label_counter <= 21 ?
3015 MICROLABEL_LEVEL_AUTHOR :
3016 label_counter >= 23 && label_counter <= 26 ?
3017 MICROLABEL_IMPORTED_FROM_HEAD :
3018 label_counter >= 28 && label_counter <= 35 ?
3019 MICROLABEL_IMPORTED_FROM :
3020 label_counter >= 37 && label_counter <= 40 ?
3021 MICROLABEL_IMPORTED_BY_HEAD :
3022 label_counter >= 42 && label_counter <= 49 ?
3023 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
3025 if (leveldir_current->imported_from == NULL &&
3026 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
3027 label_state == MICROLABEL_IMPORTED_FROM))
3028 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
3029 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
3031 DrawPreviewLevelLabelExt(label_state);
3035 void DrawPreviewLevelInitial()
3037 DrawPreviewLevelExt(TRUE);
3040 void DrawPreviewLevelAnimation()
3042 DrawPreviewLevelExt(FALSE);
3045 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3046 int graphic, int sync_frame,
3049 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3051 if (mask_mode == USE_MASKING)
3052 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3054 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3057 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3058 int graphic, int sync_frame, int mask_mode)
3060 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3062 if (mask_mode == USE_MASKING)
3063 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3065 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3068 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3070 int lx = LEVELX(x), ly = LEVELY(y);
3072 if (!IN_SCR_FIELD(x, y))
3075 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3076 graphic, GfxFrame[lx][ly], NO_MASKING);
3078 MarkTileDirty(x, y);
3081 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3083 int lx = LEVELX(x), ly = LEVELY(y);
3085 if (!IN_SCR_FIELD(x, y))
3088 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3089 graphic, GfxFrame[lx][ly], NO_MASKING);
3090 MarkTileDirty(x, y);
3093 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3095 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3098 void DrawLevelElementAnimation(int x, int y, int element)
3100 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3102 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3105 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3107 int sx = SCREENX(x), sy = SCREENY(y);
3109 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3112 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3115 DrawGraphicAnimation(sx, sy, graphic);
3118 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3119 DrawLevelFieldCrumbled(x, y);
3121 if (GFX_CRUMBLED(Feld[x][y]))
3122 DrawLevelFieldCrumbled(x, y);
3126 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3128 int sx = SCREENX(x), sy = SCREENY(y);
3131 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3134 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3136 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3139 DrawGraphicAnimation(sx, sy, graphic);
3141 if (GFX_CRUMBLED(element))
3142 DrawLevelFieldCrumbled(x, y);
3145 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3147 if (player->use_murphy)
3149 /* this works only because currently only one player can be "murphy" ... */
3150 static int last_horizontal_dir = MV_LEFT;
3151 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3153 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3154 last_horizontal_dir = move_dir;
3156 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3158 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3160 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3166 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3169 static boolean equalGraphics(int graphic1, int graphic2)
3171 struct GraphicInfo *g1 = &graphic_info[graphic1];
3172 struct GraphicInfo *g2 = &graphic_info[graphic2];
3174 return (g1->bitmap == g2->bitmap &&
3175 g1->src_x == g2->src_x &&
3176 g1->src_y == g2->src_y &&
3177 g1->anim_frames == g2->anim_frames &&
3178 g1->anim_delay == g2->anim_delay &&
3179 g1->anim_mode == g2->anim_mode);
3182 void DrawAllPlayers()
3186 for (i = 0; i < MAX_PLAYERS; i++)
3187 if (stored_player[i].active)
3188 DrawPlayer(&stored_player[i]);
3191 void DrawPlayerField(int x, int y)
3193 if (!IS_PLAYER(x, y))
3196 DrawPlayer(PLAYERINFO(x, y));
3199 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3201 void DrawPlayer(struct PlayerInfo *player)
3203 int jx = player->jx;
3204 int jy = player->jy;
3205 int move_dir = player->MovDir;
3206 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3207 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3208 int last_jx = (player->is_moving ? jx - dx : jx);
3209 int last_jy = (player->is_moving ? jy - dy : jy);
3210 int next_jx = jx + dx;
3211 int next_jy = jy + dy;
3212 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3213 boolean player_is_opaque = FALSE;
3214 int sx = SCREENX(jx), sy = SCREENY(jy);
3215 int sxx = 0, syy = 0;
3216 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3218 int action = ACTION_DEFAULT;
3219 int last_player_graphic = getPlayerGraphic(player, move_dir);
3220 int last_player_frame = player->Frame;
3223 /* GfxElement[][] is set to the element the player is digging or collecting;
3224 remove also for off-screen player if the player is not moving anymore */
3225 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3226 GfxElement[jx][jy] = EL_UNDEFINED;
3228 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3232 if (!IN_LEV_FIELD(jx, jy))
3234 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3235 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3236 printf("DrawPlayerField(): This should never happen!\n");
3241 if (element == EL_EXPLOSION)
3244 action = (player->is_pushing ? ACTION_PUSHING :
3245 player->is_digging ? ACTION_DIGGING :
3246 player->is_collecting ? ACTION_COLLECTING :
3247 player->is_moving ? ACTION_MOVING :
3248 player->is_snapping ? ACTION_SNAPPING :
3249 player->is_dropping ? ACTION_DROPPING :
3250 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3252 if (player->is_waiting)
3253 move_dir = player->dir_waiting;
3255 InitPlayerGfxAnimation(player, action, move_dir);
3257 /* ----------------------------------------------------------------------- */
3258 /* draw things in the field the player is leaving, if needed */
3259 /* ----------------------------------------------------------------------- */
3261 if (player->is_moving)
3263 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3265 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3267 if (last_element == EL_DYNAMITE_ACTIVE ||
3268 last_element == EL_EM_DYNAMITE_ACTIVE ||
3269 last_element == EL_SP_DISK_RED_ACTIVE)
3270 DrawDynamite(last_jx, last_jy);
3272 DrawLevelFieldThruMask(last_jx, last_jy);
3274 else if (last_element == EL_DYNAMITE_ACTIVE ||
3275 last_element == EL_EM_DYNAMITE_ACTIVE ||
3276 last_element == EL_SP_DISK_RED_ACTIVE)
3277 DrawDynamite(last_jx, last_jy);
3279 /* !!! this is not enough to prevent flickering of players which are
3280 moving next to each others without a free tile between them -- this
3281 can only be solved by drawing all players layer by layer (first the
3282 background, then the foreground etc.) !!! => TODO */
3283 else if (!IS_PLAYER(last_jx, last_jy))
3284 DrawLevelField(last_jx, last_jy);
3287 DrawLevelField(last_jx, last_jy);
3290 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3291 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3294 if (!IN_SCR_FIELD(sx, sy))
3297 /* ----------------------------------------------------------------------- */
3298 /* draw things behind the player, if needed */
3299 /* ----------------------------------------------------------------------- */
3302 DrawLevelElement(jx, jy, Back[jx][jy]);
3303 else if (IS_ACTIVE_BOMB(element))
3304 DrawLevelElement(jx, jy, EL_EMPTY);
3307 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3309 int old_element = GfxElement[jx][jy];
3310 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3311 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3313 if (GFX_CRUMBLED(old_element))
3314 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3316 DrawGraphic(sx, sy, old_graphic, frame);
3318 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3319 player_is_opaque = TRUE;
3323 GfxElement[jx][jy] = EL_UNDEFINED;
3325 /* make sure that pushed elements are drawn with correct frame rate */
3326 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3328 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3329 GfxFrame[jx][jy] = player->StepFrame;
3331 DrawLevelField(jx, jy);
3335 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3336 /* ----------------------------------------------------------------------- */
3337 /* draw player himself */
3338 /* ----------------------------------------------------------------------- */
3340 graphic = getPlayerGraphic(player, move_dir);
3342 /* in the case of changed player action or direction, prevent the current
3343 animation frame from being restarted for identical animations */
3344 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3345 player->Frame = last_player_frame;
3347 frame = getGraphicAnimationFrame(graphic, player->Frame);
3351 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3352 sxx = player->GfxPos;
3354 syy = player->GfxPos;
3357 if (player_is_opaque)
3358 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3360 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3362 if (SHIELD_ON(player))
3364 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3365 IMG_SHIELD_NORMAL_ACTIVE);
3366 int frame = getGraphicAnimationFrame(graphic, -1);
3368 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3372 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3375 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3376 sxx = player->GfxPos;
3378 syy = player->GfxPos;
3382 /* ----------------------------------------------------------------------- */
3383 /* draw things the player is pushing, if needed */
3384 /* ----------------------------------------------------------------------- */
3386 if (player->is_pushing && player->is_moving)
3388 int px = SCREENX(jx), py = SCREENY(jy);
3389 int pxx = (TILEX - ABS(sxx)) * dx;
3390 int pyy = (TILEY - ABS(syy)) * dy;
3391 int gfx_frame = GfxFrame[jx][jy];
3397 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3399 element = Feld[next_jx][next_jy];
3400 gfx_frame = GfxFrame[next_jx][next_jy];
3403 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3405 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3406 frame = getGraphicAnimationFrame(graphic, sync_frame);
3408 /* draw background element under pushed element (like the Sokoban field) */
3409 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3411 /* this allows transparent pushing animation over non-black background */
3414 DrawLevelElement(jx, jy, Back[jx][jy]);
3416 DrawLevelElement(jx, jy, EL_EMPTY);
3418 if (Back[next_jx][next_jy])
3419 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3421 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3423 else if (Back[next_jx][next_jy])
3424 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3427 /* do not draw (EM style) pushing animation when pushing is finished */
3428 /* (two-tile animations usually do not contain start and end frame) */
3429 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3430 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3432 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3434 /* masked drawing is needed for EMC style (double) movement graphics */
3435 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3436 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3440 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3441 /* ----------------------------------------------------------------------- */
3442 /* draw player himself */
3443 /* ----------------------------------------------------------------------- */
3445 graphic = getPlayerGraphic(player, move_dir);
3447 /* in the case of changed player action or direction, prevent the current
3448 animation frame from being restarted for identical animations */
3449 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3450 player->Frame = last_player_frame;
3452 frame = getGraphicAnimationFrame(graphic, player->Frame);
3456 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3457 sxx = player->GfxPos;
3459 syy = player->GfxPos;
3462 if (player_is_opaque)
3463 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3465 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3467 if (SHIELD_ON(player))
3469 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3470 IMG_SHIELD_NORMAL_ACTIVE);
3471 int frame = getGraphicAnimationFrame(graphic, -1);
3473 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3477 /* ----------------------------------------------------------------------- */
3478 /* draw things in front of player (active dynamite or dynabombs) */
3479 /* ----------------------------------------------------------------------- */
3481 if (IS_ACTIVE_BOMB(element))
3483 graphic = el2img(element);
3484 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3486 if (game.emulation == EMU_SUPAPLEX)
3487 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3489 DrawGraphicThruMask(sx, sy, graphic, frame);
3492 if (player_is_moving && last_element == EL_EXPLOSION)
3494 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3495 GfxElement[last_jx][last_jy] : EL_EMPTY);
3496 int graphic = el_act2img(element, ACTION_EXPLODING);
3497 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3498 int phase = ExplodePhase[last_jx][last_jy] - 1;
3499 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3502 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3505 /* ----------------------------------------------------------------------- */
3506 /* draw elements the player is just walking/passing through/under */
3507 /* ----------------------------------------------------------------------- */
3509 if (player_is_moving)
3511 /* handle the field the player is leaving ... */
3512 if (IS_ACCESSIBLE_INSIDE(last_element))
3513 DrawLevelField(last_jx, last_jy);
3514 else if (IS_ACCESSIBLE_UNDER(last_element))
3515 DrawLevelFieldThruMask(last_jx, last_jy);
3518 /* do not redraw accessible elements if the player is just pushing them */
3519 if (!player_is_moving || !player->is_pushing)
3521 /* ... and the field the player is entering */
3522 if (IS_ACCESSIBLE_INSIDE(element))
3523 DrawLevelField(jx, jy);
3524 else if (IS_ACCESSIBLE_UNDER(element))
3525 DrawLevelFieldThruMask(jx, jy);
3528 MarkTileDirty(sx, sy);
3531 /* ------------------------------------------------------------------------- */
3533 void WaitForEventToContinue()
3535 boolean still_wait = TRUE;
3537 /* simulate releasing mouse button over last gadget, if still pressed */
3539 HandleGadgets(-1, -1, 0);
3541 button_status = MB_RELEASED;
3555 case EVENT_BUTTONPRESS:
3556 case EVENT_KEYPRESS:
3560 case EVENT_KEYRELEASE:
3561 ClearPlayerAction();
3565 HandleOtherEvents(&event);
3569 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3578 #define MAX_REQUEST_LINES 13
3579 #define MAX_REQUEST_LINE_FONT1_LEN 7
3580 #define MAX_REQUEST_LINE_FONT2_LEN 10
3582 static int RequestHandleEvents(unsigned int req_state)
3584 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3585 local_player->LevelSolved_GameEnd);
3586 int width = request.width;
3587 int height = request.height;
3591 setRequestPosition(&sx, &sy, FALSE);
3593 button_status = MB_RELEASED;
3595 request_gadget_id = -1;
3602 SetDrawtoField(DRAW_TO_FIELDBUFFER);
3604 HandleGameActions();
3606 SetDrawtoField(DRAW_TO_BACKBUFFER);
3608 if (global.use_envelope_request)
3610 /* copy current state of request area to middle of playfield area */
3611 BlitBitmap(bitmap_db_store_2, drawto, sx, sy, width, height, sx, sy);
3619 while (NextValidEvent(&event))
3623 case EVENT_BUTTONPRESS:
3624 case EVENT_BUTTONRELEASE:
3625 case EVENT_MOTIONNOTIFY:
3629 if (event.type == EVENT_MOTIONNOTIFY)
3634 motion_status = TRUE;
3635 mx = ((MotionEvent *) &event)->x;
3636 my = ((MotionEvent *) &event)->y;
3640 motion_status = FALSE;
3641 mx = ((ButtonEvent *) &event)->x;
3642 my = ((ButtonEvent *) &event)->y;
3643 if (event.type == EVENT_BUTTONPRESS)
3644 button_status = ((ButtonEvent *) &event)->button;
3646 button_status = MB_RELEASED;
3649 /* this sets 'request_gadget_id' */
3650 HandleGadgets(mx, my, button_status);
3652 switch (request_gadget_id)
3654 case TOOL_CTRL_ID_YES:
3657 case TOOL_CTRL_ID_NO:
3660 case TOOL_CTRL_ID_CONFIRM:
3661 result = TRUE | FALSE;
3664 case TOOL_CTRL_ID_PLAYER_1:
3667 case TOOL_CTRL_ID_PLAYER_2:
3670 case TOOL_CTRL_ID_PLAYER_3:
3673 case TOOL_CTRL_ID_PLAYER_4:
3684 case EVENT_KEYPRESS:
3685 switch (GetEventKey((KeyEvent *)&event, TRUE))
3688 if (req_state & REQ_CONFIRM)
3693 #if defined(TARGET_SDL2)
3700 #if defined(TARGET_SDL2)
3710 if (req_state & REQ_PLAYER)
3714 case EVENT_KEYRELEASE:
3715 ClearPlayerAction();
3719 HandleOtherEvents(&event);
3724 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3726 int joy = AnyJoystick();
3728 if (joy & JOY_BUTTON_1)
3730 else if (joy & JOY_BUTTON_2)
3736 if (global.use_envelope_request)
3738 /* copy back current state of pressed buttons inside request area */
3739 BlitBitmap(drawto, bitmap_db_store_2, sx, sy, width, height, sx, sy);
3749 static boolean RequestDoor(char *text, unsigned int req_state)
3751 unsigned int old_door_state;
3752 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3753 int font_nr = FONT_TEXT_2;
3758 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3760 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3761 font_nr = FONT_TEXT_1;
3764 if (game_status == GAME_MODE_PLAYING)
3765 BlitScreenToBitmap(backbuffer);
3767 /* disable deactivated drawing when quick-loading level tape recording */
3768 if (tape.playing && tape.deactivate_display)
3769 TapeDeactivateDisplayOff(TRUE);
3771 SetMouseCursor(CURSOR_DEFAULT);
3773 #if defined(NETWORK_AVALIABLE)
3774 /* pause network game while waiting for request to answer */
3775 if (options.network &&
3776 game_status == GAME_MODE_PLAYING &&
3777 req_state & REQUEST_WAIT_FOR_INPUT)
3778 SendToServer_PausePlaying();
3781 old_door_state = GetDoorState();
3783 /* simulate releasing mouse button over last gadget, if still pressed */
3785 HandleGadgets(-1, -1, 0);
3789 /* draw released gadget before proceeding */
3792 if (old_door_state & DOOR_OPEN_1)
3794 CloseDoor(DOOR_CLOSE_1);
3796 /* save old door content */
3797 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3798 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3801 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3802 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3804 /* clear door drawing field */
3805 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3807 /* force DOOR font inside door area */
3808 SetFontStatus(GAME_MODE_PSEUDO_DOOR);
3810 /* write text for request */
3811 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3813 char text_line[max_request_line_len + 1];
3819 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3821 tc = *(text_ptr + tx);
3822 // if (!tc || tc == ' ')
3823 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3827 if ((tc == '?' || tc == '!') && tl == 0)
3837 strncpy(text_line, text_ptr, tl);
3840 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3841 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3842 text_line, font_nr);
3844 text_ptr += tl + (tc == ' ' ? 1 : 0);
3845 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3850 if (req_state & REQ_ASK)
3852 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3853 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3855 else if (req_state & REQ_CONFIRM)
3857 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3859 else if (req_state & REQ_PLAYER)
3861 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3862 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3863 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3864 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3867 /* copy request gadgets to door backbuffer */
3868 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3870 OpenDoor(DOOR_OPEN_1);
3872 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3874 if (game_status == GAME_MODE_PLAYING)
3876 SetPanelBackground();
3877 SetDrawBackgroundMask(REDRAW_DOOR_1);
3881 SetDrawBackgroundMask(REDRAW_FIELD);
3887 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3889 // ---------- handle request buttons ----------
3890 result = RequestHandleEvents(req_state);
3894 if (!(req_state & REQ_STAY_OPEN))
3896 CloseDoor(DOOR_CLOSE_1);
3898 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3899 (req_state & REQ_REOPEN))
3900 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3905 if (game_status == GAME_MODE_PLAYING)
3907 SetPanelBackground();
3908 SetDrawBackgroundMask(REDRAW_DOOR_1);
3912 SetDrawBackgroundMask(REDRAW_FIELD);
3915 #if defined(NETWORK_AVALIABLE)
3916 /* continue network game after request */
3917 if (options.network &&
3918 game_status == GAME_MODE_PLAYING &&
3919 req_state & REQUEST_WAIT_FOR_INPUT)
3920 SendToServer_ContinuePlaying();
3923 /* restore deactivated drawing when quick-loading level tape recording */
3924 if (tape.playing && tape.deactivate_display)
3925 TapeDeactivateDisplayOn();
3930 static boolean RequestEnvelope(char *text, unsigned int req_state)
3934 if (game_status == GAME_MODE_PLAYING)
3935 BlitScreenToBitmap(backbuffer);
3937 /* disable deactivated drawing when quick-loading level tape recording */
3938 if (tape.playing && tape.deactivate_display)
3939 TapeDeactivateDisplayOff(TRUE);
3941 SetMouseCursor(CURSOR_DEFAULT);
3943 #if defined(NETWORK_AVALIABLE)
3944 /* pause network game while waiting for request to answer */
3945 if (options.network &&
3946 game_status == GAME_MODE_PLAYING &&
3947 req_state & REQUEST_WAIT_FOR_INPUT)
3948 SendToServer_PausePlaying();
3951 /* simulate releasing mouse button over last gadget, if still pressed */
3953 HandleGadgets(-1, -1, 0);
3957 // (replace with setting corresponding request background)
3958 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3959 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3961 /* clear door drawing field */
3962 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3964 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3966 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3968 if (game_status == GAME_MODE_PLAYING)
3970 SetPanelBackground();
3971 SetDrawBackgroundMask(REDRAW_DOOR_1);
3975 SetDrawBackgroundMask(REDRAW_FIELD);
3981 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3983 // ---------- handle request buttons ----------
3984 result = RequestHandleEvents(req_state);
3988 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3992 if (game_status == GAME_MODE_PLAYING)
3994 SetPanelBackground();
3995 SetDrawBackgroundMask(REDRAW_DOOR_1);
3999 SetDrawBackgroundMask(REDRAW_FIELD);
4002 #if defined(NETWORK_AVALIABLE)
4003 /* continue network game after request */
4004 if (options.network &&
4005 game_status == GAME_MODE_PLAYING &&
4006 req_state & REQUEST_WAIT_FOR_INPUT)
4007 SendToServer_ContinuePlaying();
4010 /* restore deactivated drawing when quick-loading level tape recording */
4011 if (tape.playing && tape.deactivate_display)
4012 TapeDeactivateDisplayOn();
4017 boolean Request(char *text, unsigned int req_state)
4019 if (global.use_envelope_request)
4020 return RequestEnvelope(text, req_state);
4022 return RequestDoor(text, req_state);
4025 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
4027 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
4028 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
4031 if (dpo1->sort_priority != dpo2->sort_priority)
4032 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4034 compare_result = dpo1->nr - dpo2->nr;
4036 return compare_result;
4039 void InitGraphicCompatibilityInfo_Doors()
4045 struct DoorInfo *door;
4049 { DOOR_1, IMG_GFX_DOOR_1_PART_1, IMG_GFX_DOOR_1_PART_8, &door_1 },
4050 { DOOR_2, IMG_GFX_DOOR_2_PART_1, IMG_GFX_DOOR_2_PART_8, &door_2 },
4052 { -1, -1, -1, NULL }
4054 struct Rect door_rect_list[] =
4056 { DX, DY, DXSIZE, DYSIZE },
4057 { VX, VY, VXSIZE, VYSIZE }
4061 for (i = 0; doors[i].door_token != -1; i++)
4063 int door_token = doors[i].door_token;
4064 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4065 int part_1 = doors[i].part_1;
4066 int part_8 = doors[i].part_8;
4067 int part_2 = part_1 + 1;
4068 int part_3 = part_1 + 2;
4069 struct DoorInfo *door = doors[i].door;
4070 struct Rect *door_rect = &door_rect_list[door_index];
4071 boolean door_gfx_redefined = FALSE;
4073 /* check if any door part graphic definitions have been redefined */
4075 for (j = 0; door_part_controls[j].door_token != -1; j++)
4077 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4078 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4080 if (dpc->door_token == door_token && fi->redefined)
4081 door_gfx_redefined = TRUE;
4084 /* check for old-style door graphic/animation modifications */
4086 if (!door_gfx_redefined)
4088 if (door->anim_mode & ANIM_STATIC_PANEL)
4090 door->panel.step_xoffset = 0;
4091 door->panel.step_yoffset = 0;
4094 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4096 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4097 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4098 int num_door_steps, num_panel_steps;
4100 /* remove door part graphics other than the two default wings */
4102 for (j = 0; door_part_controls[j].door_token != -1; j++)
4104 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4105 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4107 if (dpc->graphic >= part_3 &&
4108 dpc->graphic <= part_8)
4112 /* set graphics and screen positions of the default wings */
4114 g_part_1->width = door_rect->width;
4115 g_part_1->height = door_rect->height;
4116 g_part_2->width = door_rect->width;
4117 g_part_2->height = door_rect->height;
4118 g_part_2->src_x = door_rect->width;
4119 g_part_2->src_y = g_part_1->src_y;
4121 door->part_2.x = door->part_1.x;
4122 door->part_2.y = door->part_1.y;
4124 if (door->width != -1)
4126 g_part_1->width = door->width;
4127 g_part_2->width = door->width;
4129 // special treatment for graphics and screen position of right wing
4130 g_part_2->src_x += door_rect->width - door->width;
4131 door->part_2.x += door_rect->width - door->width;
4134 if (door->height != -1)
4136 g_part_1->height = door->height;
4137 g_part_2->height = door->height;
4139 // special treatment for graphics and screen position of bottom wing
4140 g_part_2->src_y += door_rect->height - door->height;
4141 door->part_2.y += door_rect->height - door->height;
4144 /* set animation delays for the default wings and panels */
4146 door->part_1.step_delay = door->step_delay;
4147 door->part_2.step_delay = door->step_delay;
4148 door->panel.step_delay = door->step_delay;
4150 /* set animation draw order for the default wings */
4152 door->part_1.sort_priority = 2; /* draw left wing over ... */
4153 door->part_2.sort_priority = 1; /* ... right wing */
4155 /* set animation draw offset for the default wings */
4157 if (door->anim_mode & ANIM_HORIZONTAL)
4159 door->part_1.step_xoffset = door->step_offset;
4160 door->part_1.step_yoffset = 0;
4161 door->part_2.step_xoffset = door->step_offset * -1;
4162 door->part_2.step_yoffset = 0;
4164 num_door_steps = g_part_1->width / door->step_offset;
4166 else // ANIM_VERTICAL
4168 door->part_1.step_xoffset = 0;
4169 door->part_1.step_yoffset = door->step_offset;
4170 door->part_2.step_xoffset = 0;
4171 door->part_2.step_yoffset = door->step_offset * -1;
4173 num_door_steps = g_part_1->height / door->step_offset;
4176 /* set animation draw offset for the default panels */
4178 if (door->step_offset > 1)
4180 num_panel_steps = 2 * door_rect->height / door->step_offset;
4181 door->panel.start_step = num_panel_steps - num_door_steps;
4182 door->panel.start_step_closing = door->panel.start_step;
4186 num_panel_steps = door_rect->height / door->step_offset;
4187 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4188 door->panel.start_step_closing = door->panel.start_step;
4189 door->panel.step_delay *= 2;
4200 for (i = 0; door_part_controls[i].door_token != -1; i++)
4202 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4203 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4205 /* initialize "start_step_opening" and "start_step_closing", if needed */
4206 if (dpc->pos->start_step_opening == 0 &&
4207 dpc->pos->start_step_closing == 0)
4209 // dpc->pos->start_step_opening = dpc->pos->start_step;
4210 dpc->pos->start_step_closing = dpc->pos->start_step;
4213 /* fill structure for door part draw order (sorted below) */
4215 dpo->sort_priority = dpc->pos->sort_priority;
4218 /* sort door part controls according to sort_priority and graphic number */
4219 qsort(door_part_order, MAX_DOOR_PARTS,
4220 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4223 unsigned int OpenDoor(unsigned int door_state)
4225 if (door_state & DOOR_COPY_BACK)
4227 if (door_state & DOOR_OPEN_1)
4228 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4229 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4231 if (door_state & DOOR_OPEN_2)
4232 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4233 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4235 door_state &= ~DOOR_COPY_BACK;
4238 return MoveDoor(door_state);
4241 unsigned int CloseDoor(unsigned int door_state)
4243 unsigned int old_door_state = GetDoorState();
4245 if (!(door_state & DOOR_NO_COPY_BACK))
4247 if (old_door_state & DOOR_OPEN_1)
4248 BlitBitmap(backbuffer, bitmap_db_door_1,
4249 DX, DY, DXSIZE, DYSIZE, 0, 0);
4251 if (old_door_state & DOOR_OPEN_2)
4252 BlitBitmap(backbuffer, bitmap_db_door_2,
4253 VX, VY, VXSIZE, VYSIZE, 0, 0);
4255 door_state &= ~DOOR_NO_COPY_BACK;
4258 return MoveDoor(door_state);
4261 unsigned int GetDoorState()
4263 return MoveDoor(DOOR_GET_STATE);
4266 unsigned int SetDoorState(unsigned int door_state)
4268 return MoveDoor(door_state | DOOR_SET_STATE);
4271 int euclid(int a, int b)
4273 return (b ? euclid(b, a % b) : a);
4276 unsigned int MoveDoor(unsigned int door_state)
4278 struct Rect door_rect_list[] =
4280 { DX, DY, DXSIZE, DYSIZE },
4281 { VX, VY, VXSIZE, VYSIZE }
4283 static int door1 = DOOR_CLOSE_1;
4284 static int door2 = DOOR_CLOSE_2;
4285 unsigned int door_delay = 0;
4286 unsigned int door_delay_value;
4289 if (door_state == DOOR_GET_STATE)
4290 return (door1 | door2);
4292 if (door_state & DOOR_SET_STATE)
4294 if (door_state & DOOR_ACTION_1)
4295 door1 = door_state & DOOR_ACTION_1;
4296 if (door_state & DOOR_ACTION_2)
4297 door2 = door_state & DOOR_ACTION_2;
4299 return (door1 | door2);
4302 if (!(door_state & DOOR_FORCE_REDRAW))
4304 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4305 door_state &= ~DOOR_OPEN_1;
4306 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4307 door_state &= ~DOOR_CLOSE_1;
4308 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4309 door_state &= ~DOOR_OPEN_2;
4310 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4311 door_state &= ~DOOR_CLOSE_2;
4314 if (global.autoplay_leveldir)
4316 door_state |= DOOR_NO_DELAY;
4317 door_state &= ~DOOR_CLOSE_ALL;
4320 if (game_status == GAME_MODE_EDITOR)
4321 door_state |= DOOR_NO_DELAY;
4323 if (door_state & DOOR_ACTION)
4325 boolean door_panel_drawn[NUM_DOORS];
4326 boolean panel_has_doors[NUM_DOORS];
4327 boolean door_part_skip[MAX_DOOR_PARTS];
4328 boolean door_part_done[MAX_DOOR_PARTS];
4329 boolean door_part_done_all;
4330 int num_steps[MAX_DOOR_PARTS];
4331 int max_move_delay = 0; // delay for complete animations of all doors
4332 int max_step_delay = 0; // delay (ms) between two animation frames
4333 int num_move_steps = 0; // number of animation steps for all doors
4334 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4335 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4336 int current_move_delay = 0;
4340 for (i = 0; i < NUM_DOORS; i++)
4341 panel_has_doors[i] = FALSE;
4343 for (i = 0; i < MAX_DOOR_PARTS; i++)
4345 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4346 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4347 int door_token = dpc->door_token;
4349 door_part_done[i] = FALSE;
4350 door_part_skip[i] = (!(door_state & door_token) ||
4354 for (i = 0; i < MAX_DOOR_PARTS; i++)
4356 int nr = door_part_order[i].nr;
4357 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4358 struct DoorPartPosInfo *pos = dpc->pos;
4359 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4360 int door_token = dpc->door_token;
4361 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4362 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4363 int step_xoffset = ABS(pos->step_xoffset);
4364 int step_yoffset = ABS(pos->step_yoffset);
4365 int step_delay = pos->step_delay;
4366 int current_door_state = door_state & door_token;
4367 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4368 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4369 boolean part_opening = (is_panel ? door_closing : door_opening);
4370 int start_step = (part_opening ? pos->start_step_opening :
4371 pos->start_step_closing);
4372 float move_xsize = (step_xoffset ? g->width : 0);
4373 float move_ysize = (step_yoffset ? g->height : 0);
4374 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4375 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4376 int move_steps = (move_xsteps && move_ysteps ?
4377 MIN(move_xsteps, move_ysteps) :
4378 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4379 int move_delay = move_steps * step_delay;
4381 if (door_part_skip[nr])
4384 max_move_delay = MAX(max_move_delay, move_delay);
4385 max_step_delay = (max_step_delay == 0 ? step_delay :
4386 euclid(max_step_delay, step_delay));
4387 num_steps[nr] = move_steps;
4391 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4393 panel_has_doors[door_index] = TRUE;
4397 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4399 num_move_steps = max_move_delay / max_step_delay;
4400 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4402 door_delay_value = max_step_delay;
4404 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4406 start = num_move_steps - 1;
4410 /* opening door sound has priority over simultaneously closing door */
4411 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4412 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4413 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4414 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4417 for (k = start; k < num_move_steps; k++)
4419 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4421 door_part_done_all = TRUE;
4423 for (i = 0; i < NUM_DOORS; i++)
4424 door_panel_drawn[i] = FALSE;
4426 for (i = 0; i < MAX_DOOR_PARTS; i++)
4428 int nr = door_part_order[i].nr;
4429 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4430 struct DoorPartPosInfo *pos = dpc->pos;
4431 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4432 int door_token = dpc->door_token;
4433 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4434 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4435 boolean is_panel_and_door_has_closed = FALSE;
4436 struct Rect *door_rect = &door_rect_list[door_index];
4437 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4439 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4440 int current_door_state = door_state & door_token;
4441 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4442 boolean door_closing = !door_opening;
4443 boolean part_opening = (is_panel ? door_closing : door_opening);
4444 boolean part_closing = !part_opening;
4445 int start_step = (part_opening ? pos->start_step_opening :
4446 pos->start_step_closing);
4447 int step_delay = pos->step_delay;
4448 int step_factor = step_delay / max_step_delay;
4449 int k1 = (step_factor ? k / step_factor + 1 : k);
4450 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4451 int kk = MAX(0, k2);
4454 int src_x, src_y, src_xx, src_yy;
4455 int dst_x, dst_y, dst_xx, dst_yy;
4458 if (door_part_skip[nr])
4461 if (!(door_state & door_token))
4469 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4470 int kk_door = MAX(0, k2_door);
4471 int sync_frame = kk_door * door_delay_value;
4472 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4474 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4479 if (!door_panel_drawn[door_index])
4481 ClearRectangle(drawto, door_rect->x, door_rect->y,
4482 door_rect->width, door_rect->height);
4484 door_panel_drawn[door_index] = TRUE;
4487 // draw opening or closing door parts
4489 if (pos->step_xoffset < 0) // door part on right side
4492 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4495 if (dst_xx + width > door_rect->width)
4496 width = door_rect->width - dst_xx;
4498 else // door part on left side
4501 dst_xx = pos->x - kk * pos->step_xoffset;
4505 src_xx = ABS(dst_xx);
4509 width = g->width - src_xx;
4511 if (width > door_rect->width)
4512 width = door_rect->width;
4514 // printf("::: k == %d [%d] \n", k, start_step);
4517 if (pos->step_yoffset < 0) // door part on bottom side
4520 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4523 if (dst_yy + height > door_rect->height)
4524 height = door_rect->height - dst_yy;
4526 else // door part on top side
4529 dst_yy = pos->y - kk * pos->step_yoffset;
4533 src_yy = ABS(dst_yy);
4537 height = g->height - src_yy;
4540 src_x = g_src_x + src_xx;
4541 src_y = g_src_y + src_yy;
4543 dst_x = door_rect->x + dst_xx;
4544 dst_y = door_rect->y + dst_yy;
4546 is_panel_and_door_has_closed =
4549 panel_has_doors[door_index] &&
4550 k >= num_move_steps_doors_only - 1);
4552 if (width >= 0 && width <= g->width &&
4553 height >= 0 && height <= g->height &&
4554 !is_panel_and_door_has_closed)
4556 if (is_panel || !pos->draw_masked)
4557 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4560 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4564 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4566 if ((part_opening && (width < 0 || height < 0)) ||
4567 (part_closing && (width >= g->width && height >= g->height)))
4568 door_part_done[nr] = TRUE;
4570 // continue door part animations, but not panel after door has closed
4571 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4572 door_part_done_all = FALSE;
4575 if (!(door_state & DOOR_NO_DELAY))
4579 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4581 current_move_delay += max_step_delay;
4584 if (door_part_done_all)
4589 if (door_state & DOOR_ACTION_1)
4590 door1 = door_state & DOOR_ACTION_1;
4591 if (door_state & DOOR_ACTION_2)
4592 door2 = door_state & DOOR_ACTION_2;
4594 // draw masked border over door area
4595 DrawMaskedBorder(REDRAW_DOOR_1);
4596 DrawMaskedBorder(REDRAW_DOOR_2);
4598 return (door1 | door2);
4601 static boolean useSpecialEditorDoor()
4603 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4604 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4606 // do not draw special editor door if editor border defined or redefined
4607 if (graphic_info[graphic].bitmap != NULL || redefined)
4610 // do not draw special editor door if global border defined to be empty
4611 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4614 // do not draw special editor door if viewport definitions do not match
4618 EY + EYSIZE != VY + VYSIZE)
4624 void DrawSpecialEditorDoor()
4626 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4627 int top_border_width = gfx1->width;
4628 int top_border_height = gfx1->height;
4629 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4630 int ex = EX - outer_border;
4631 int ey = EY - outer_border;
4632 int vy = VY - outer_border;
4633 int exsize = EXSIZE + 2 * outer_border;
4635 if (!useSpecialEditorDoor())
4638 /* draw bigger level editor toolbox window */
4639 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4640 top_border_width, top_border_height, ex, ey - top_border_height);
4641 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4642 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4644 redraw_mask |= REDRAW_ALL;
4647 void UndrawSpecialEditorDoor()
4649 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4650 int top_border_width = gfx1->width;
4651 int top_border_height = gfx1->height;
4652 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4653 int ex = EX - outer_border;
4654 int ey = EY - outer_border;
4655 int ey_top = ey - top_border_height;
4656 int exsize = EXSIZE + 2 * outer_border;
4657 int eysize = EYSIZE + 2 * outer_border;
4659 if (!useSpecialEditorDoor())
4662 /* draw normal tape recorder window */
4663 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4665 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4666 ex, ey_top, top_border_width, top_border_height,
4668 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4669 ex, ey, exsize, eysize, ex, ey);
4673 // if screen background is set to "[NONE]", clear editor toolbox window
4674 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4675 ClearRectangle(drawto, ex, ey, exsize, eysize);
4678 redraw_mask |= REDRAW_ALL;
4682 /* ---------- new tool button stuff ---------------------------------------- */
4687 struct TextPosInfo *pos;
4690 } toolbutton_info[NUM_TOOL_BUTTONS] =
4693 IMG_GFX_REQUEST_BUTTON_YES, &request.button.yes,
4694 TOOL_CTRL_ID_YES, "yes"
4697 IMG_GFX_REQUEST_BUTTON_NO, &request.button.no,
4698 TOOL_CTRL_ID_NO, "no"
4701 IMG_GFX_REQUEST_BUTTON_CONFIRM, &request.button.confirm,
4702 TOOL_CTRL_ID_CONFIRM, "confirm"
4705 IMG_GFX_REQUEST_BUTTON_PLAYER_1, &request.button.player_1,
4706 TOOL_CTRL_ID_PLAYER_1, "player 1"
4709 IMG_GFX_REQUEST_BUTTON_PLAYER_2, &request.button.player_2,
4710 TOOL_CTRL_ID_PLAYER_2, "player 2"
4713 IMG_GFX_REQUEST_BUTTON_PLAYER_3, &request.button.player_3,
4714 TOOL_CTRL_ID_PLAYER_3, "player 3"
4717 IMG_GFX_REQUEST_BUTTON_PLAYER_4, &request.button.player_4,
4718 TOOL_CTRL_ID_PLAYER_4, "player 4"
4722 void CreateToolButtons()
4726 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4728 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4729 struct TextPosInfo *pos = toolbutton_info[i].pos;
4730 struct GadgetInfo *gi;
4731 Bitmap *deco_bitmap = None;
4732 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4733 unsigned int event_mask = GD_EVENT_RELEASED;
4736 int gd_x = gfx->src_x;
4737 int gd_y = gfx->src_y;
4738 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4739 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4742 if (global.use_envelope_request)
4743 setRequestPosition(&dx, &dy, TRUE);
4745 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4747 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4749 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4750 pos->size, &deco_bitmap, &deco_x, &deco_y);
4751 deco_xpos = (gfx->width - pos->size) / 2;
4752 deco_ypos = (gfx->height - pos->size) / 2;
4755 gi = CreateGadget(GDI_CUSTOM_ID, id,
4756 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4757 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4758 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4759 GDI_WIDTH, gfx->width,
4760 GDI_HEIGHT, gfx->height,
4761 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4762 GDI_STATE, GD_BUTTON_UNPRESSED,
4763 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4764 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4765 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4766 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4767 GDI_DECORATION_SIZE, pos->size, pos->size,
4768 GDI_DECORATION_SHIFTING, 1, 1,
4769 GDI_DIRECT_DRAW, FALSE,
4770 GDI_EVENT_MASK, event_mask,
4771 GDI_CALLBACK_ACTION, HandleToolButtons,
4775 Error(ERR_EXIT, "cannot create gadget");
4777 tool_gadget[id] = gi;
4781 void FreeToolButtons()
4785 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4786 FreeGadget(tool_gadget[i]);
4789 static void UnmapToolButtons()
4793 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4794 UnmapGadget(tool_gadget[i]);
4797 static void HandleToolButtons(struct GadgetInfo *gi)
4799 request_gadget_id = gi->custom_id;
4802 static struct Mapping_EM_to_RND_object
4805 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4806 boolean is_backside; /* backside of moving element */
4812 em_object_mapping_list[] =
4815 Xblank, TRUE, FALSE,
4819 Yacid_splash_eB, FALSE, FALSE,
4820 EL_ACID_SPLASH_RIGHT, -1, -1
4823 Yacid_splash_wB, FALSE, FALSE,
4824 EL_ACID_SPLASH_LEFT, -1, -1
4827 #ifdef EM_ENGINE_BAD_ROLL
4829 Xstone_force_e, FALSE, FALSE,
4830 EL_ROCK, -1, MV_BIT_RIGHT
4833 Xstone_force_w, FALSE, FALSE,
4834 EL_ROCK, -1, MV_BIT_LEFT
4837 Xnut_force_e, FALSE, FALSE,
4838 EL_NUT, -1, MV_BIT_RIGHT
4841 Xnut_force_w, FALSE, FALSE,
4842 EL_NUT, -1, MV_BIT_LEFT
4845 Xspring_force_e, FALSE, FALSE,
4846 EL_SPRING, -1, MV_BIT_RIGHT
4849 Xspring_force_w, FALSE, FALSE,
4850 EL_SPRING, -1, MV_BIT_LEFT
4853 Xemerald_force_e, FALSE, FALSE,
4854 EL_EMERALD, -1, MV_BIT_RIGHT
4857 Xemerald_force_w, FALSE, FALSE,
4858 EL_EMERALD, -1, MV_BIT_LEFT
4861 Xdiamond_force_e, FALSE, FALSE,
4862 EL_DIAMOND, -1, MV_BIT_RIGHT
4865 Xdiamond_force_w, FALSE, FALSE,
4866 EL_DIAMOND, -1, MV_BIT_LEFT
4869 Xbomb_force_e, FALSE, FALSE,
4870 EL_BOMB, -1, MV_BIT_RIGHT
4873 Xbomb_force_w, FALSE, FALSE,
4874 EL_BOMB, -1, MV_BIT_LEFT
4876 #endif /* EM_ENGINE_BAD_ROLL */
4879 Xstone, TRUE, FALSE,
4883 Xstone_pause, FALSE, FALSE,
4887 Xstone_fall, FALSE, FALSE,
4891 Ystone_s, FALSE, FALSE,
4892 EL_ROCK, ACTION_FALLING, -1
4895 Ystone_sB, FALSE, TRUE,
4896 EL_ROCK, ACTION_FALLING, -1
4899 Ystone_e, FALSE, FALSE,
4900 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4903 Ystone_eB, FALSE, TRUE,
4904 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4907 Ystone_w, FALSE, FALSE,
4908 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4911 Ystone_wB, FALSE, TRUE,
4912 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4919 Xnut_pause, FALSE, FALSE,
4923 Xnut_fall, FALSE, FALSE,
4927 Ynut_s, FALSE, FALSE,
4928 EL_NUT, ACTION_FALLING, -1
4931 Ynut_sB, FALSE, TRUE,
4932 EL_NUT, ACTION_FALLING, -1
4935 Ynut_e, FALSE, FALSE,
4936 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4939 Ynut_eB, FALSE, TRUE,
4940 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4943 Ynut_w, FALSE, FALSE,
4944 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4947 Ynut_wB, FALSE, TRUE,
4948 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4951 Xbug_n, TRUE, FALSE,
4955 Xbug_e, TRUE, FALSE,
4956 EL_BUG_RIGHT, -1, -1
4959 Xbug_s, TRUE, FALSE,
4963 Xbug_w, TRUE, FALSE,
4967 Xbug_gon, FALSE, FALSE,
4971 Xbug_goe, FALSE, FALSE,
4972 EL_BUG_RIGHT, -1, -1
4975 Xbug_gos, FALSE, FALSE,
4979 Xbug_gow, FALSE, FALSE,
4983 Ybug_n, FALSE, FALSE,
4984 EL_BUG, ACTION_MOVING, MV_BIT_UP
4987 Ybug_nB, FALSE, TRUE,
4988 EL_BUG, ACTION_MOVING, MV_BIT_UP
4991 Ybug_e, FALSE, FALSE,
4992 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4995 Ybug_eB, FALSE, TRUE,
4996 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4999 Ybug_s, FALSE, FALSE,
5000 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5003 Ybug_sB, FALSE, TRUE,
5004 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
5007 Ybug_w, FALSE, FALSE,
5008 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5011 Ybug_wB, FALSE, TRUE,
5012 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
5015 Ybug_w_n, FALSE, FALSE,
5016 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5019 Ybug_n_e, FALSE, FALSE,
5020 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5023 Ybug_e_s, FALSE, FALSE,
5024 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5027 Ybug_s_w, FALSE, FALSE,
5028 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5031 Ybug_e_n, FALSE, FALSE,
5032 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5035 Ybug_s_e, FALSE, FALSE,
5036 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5039 Ybug_w_s, FALSE, FALSE,
5040 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5043 Ybug_n_w, FALSE, FALSE,
5044 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5047 Ybug_stone, FALSE, FALSE,
5048 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5051 Ybug_spring, FALSE, FALSE,
5052 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5055 Xtank_n, TRUE, FALSE,
5056 EL_SPACESHIP_UP, -1, -1
5059 Xtank_e, TRUE, FALSE,
5060 EL_SPACESHIP_RIGHT, -1, -1
5063 Xtank_s, TRUE, FALSE,
5064 EL_SPACESHIP_DOWN, -1, -1
5067 Xtank_w, TRUE, FALSE,
5068 EL_SPACESHIP_LEFT, -1, -1
5071 Xtank_gon, FALSE, FALSE,
5072 EL_SPACESHIP_UP, -1, -1
5075 Xtank_goe, FALSE, FALSE,
5076 EL_SPACESHIP_RIGHT, -1, -1
5079 Xtank_gos, FALSE, FALSE,
5080 EL_SPACESHIP_DOWN, -1, -1
5083 Xtank_gow, FALSE, FALSE,
5084 EL_SPACESHIP_LEFT, -1, -1
5087 Ytank_n, FALSE, FALSE,
5088 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5091 Ytank_nB, FALSE, TRUE,
5092 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5095 Ytank_e, FALSE, FALSE,
5096 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5099 Ytank_eB, FALSE, TRUE,
5100 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5103 Ytank_s, FALSE, FALSE,
5104 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5107 Ytank_sB, FALSE, TRUE,
5108 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5111 Ytank_w, FALSE, FALSE,
5112 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5115 Ytank_wB, FALSE, TRUE,
5116 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5119 Ytank_w_n, FALSE, FALSE,
5120 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5123 Ytank_n_e, FALSE, FALSE,
5124 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5127 Ytank_e_s, FALSE, FALSE,
5128 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5131 Ytank_s_w, FALSE, FALSE,
5132 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5135 Ytank_e_n, FALSE, FALSE,
5136 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5139 Ytank_s_e, FALSE, FALSE,
5140 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5143 Ytank_w_s, FALSE, FALSE,
5144 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5147 Ytank_n_w, FALSE, FALSE,
5148 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5151 Ytank_stone, FALSE, FALSE,
5152 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5155 Ytank_spring, FALSE, FALSE,
5156 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5159 Xandroid, TRUE, FALSE,
5160 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5163 Xandroid_1_n, FALSE, FALSE,
5164 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5167 Xandroid_2_n, FALSE, FALSE,
5168 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5171 Xandroid_1_e, FALSE, FALSE,
5172 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5175 Xandroid_2_e, FALSE, FALSE,
5176 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5179 Xandroid_1_w, FALSE, FALSE,
5180 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5183 Xandroid_2_w, FALSE, FALSE,
5184 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5187 Xandroid_1_s, FALSE, FALSE,
5188 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5191 Xandroid_2_s, FALSE, FALSE,
5192 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5195 Yandroid_n, FALSE, FALSE,
5196 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5199 Yandroid_nB, FALSE, TRUE,
5200 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5203 Yandroid_ne, FALSE, FALSE,
5204 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5207 Yandroid_neB, FALSE, TRUE,
5208 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5211 Yandroid_e, FALSE, FALSE,
5212 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5215 Yandroid_eB, FALSE, TRUE,
5216 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5219 Yandroid_se, FALSE, FALSE,
5220 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5223 Yandroid_seB, FALSE, TRUE,
5224 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5227 Yandroid_s, FALSE, FALSE,
5228 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5231 Yandroid_sB, FALSE, TRUE,
5232 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5235 Yandroid_sw, FALSE, FALSE,
5236 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5239 Yandroid_swB, FALSE, TRUE,
5240 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5243 Yandroid_w, FALSE, FALSE,
5244 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5247 Yandroid_wB, FALSE, TRUE,
5248 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5251 Yandroid_nw, FALSE, FALSE,
5252 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5255 Yandroid_nwB, FALSE, TRUE,
5256 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5259 Xspring, TRUE, FALSE,
5263 Xspring_pause, FALSE, FALSE,
5267 Xspring_e, FALSE, FALSE,
5271 Xspring_w, FALSE, FALSE,
5275 Xspring_fall, FALSE, FALSE,
5279 Yspring_s, FALSE, FALSE,
5280 EL_SPRING, ACTION_FALLING, -1
5283 Yspring_sB, FALSE, TRUE,
5284 EL_SPRING, ACTION_FALLING, -1
5287 Yspring_e, FALSE, FALSE,
5288 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5291 Yspring_eB, FALSE, TRUE,
5292 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5295 Yspring_w, FALSE, FALSE,
5296 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5299 Yspring_wB, FALSE, TRUE,
5300 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5303 Yspring_kill_e, FALSE, FALSE,
5304 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5307 Yspring_kill_eB, FALSE, TRUE,
5308 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5311 Yspring_kill_w, FALSE, FALSE,
5312 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5315 Yspring_kill_wB, FALSE, TRUE,
5316 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5319 Xeater_n, TRUE, FALSE,
5320 EL_YAMYAM_UP, -1, -1
5323 Xeater_e, TRUE, FALSE,
5324 EL_YAMYAM_RIGHT, -1, -1
5327 Xeater_w, TRUE, FALSE,
5328 EL_YAMYAM_LEFT, -1, -1
5331 Xeater_s, TRUE, FALSE,
5332 EL_YAMYAM_DOWN, -1, -1
5335 Yeater_n, FALSE, FALSE,
5336 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5339 Yeater_nB, FALSE, TRUE,
5340 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5343 Yeater_e, FALSE, FALSE,
5344 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5347 Yeater_eB, FALSE, TRUE,
5348 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5351 Yeater_s, FALSE, FALSE,
5352 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5355 Yeater_sB, FALSE, TRUE,
5356 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5359 Yeater_w, FALSE, FALSE,
5360 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5363 Yeater_wB, FALSE, TRUE,
5364 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5367 Yeater_stone, FALSE, FALSE,
5368 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5371 Yeater_spring, FALSE, FALSE,
5372 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5375 Xalien, TRUE, FALSE,
5379 Xalien_pause, FALSE, FALSE,
5383 Yalien_n, FALSE, FALSE,
5384 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5387 Yalien_nB, FALSE, TRUE,
5388 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5391 Yalien_e, FALSE, FALSE,
5392 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5395 Yalien_eB, FALSE, TRUE,
5396 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5399 Yalien_s, FALSE, FALSE,
5400 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5403 Yalien_sB, FALSE, TRUE,
5404 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5407 Yalien_w, FALSE, FALSE,
5408 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5411 Yalien_wB, FALSE, TRUE,
5412 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5415 Yalien_stone, FALSE, FALSE,
5416 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5419 Yalien_spring, FALSE, FALSE,
5420 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5423 Xemerald, TRUE, FALSE,
5427 Xemerald_pause, FALSE, FALSE,
5431 Xemerald_fall, FALSE, FALSE,
5435 Xemerald_shine, FALSE, FALSE,
5436 EL_EMERALD, ACTION_TWINKLING, -1
5439 Yemerald_s, FALSE, FALSE,
5440 EL_EMERALD, ACTION_FALLING, -1
5443 Yemerald_sB, FALSE, TRUE,
5444 EL_EMERALD, ACTION_FALLING, -1
5447 Yemerald_e, FALSE, FALSE,
5448 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5451 Yemerald_eB, FALSE, TRUE,
5452 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5455 Yemerald_w, FALSE, FALSE,
5456 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5459 Yemerald_wB, FALSE, TRUE,
5460 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5463 Yemerald_eat, FALSE, FALSE,
5464 EL_EMERALD, ACTION_COLLECTING, -1
5467 Yemerald_stone, FALSE, FALSE,
5468 EL_NUT, ACTION_BREAKING, -1
5471 Xdiamond, TRUE, FALSE,
5475 Xdiamond_pause, FALSE, FALSE,
5479 Xdiamond_fall, FALSE, FALSE,
5483 Xdiamond_shine, FALSE, FALSE,
5484 EL_DIAMOND, ACTION_TWINKLING, -1
5487 Ydiamond_s, FALSE, FALSE,
5488 EL_DIAMOND, ACTION_FALLING, -1
5491 Ydiamond_sB, FALSE, TRUE,
5492 EL_DIAMOND, ACTION_FALLING, -1
5495 Ydiamond_e, FALSE, FALSE,
5496 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5499 Ydiamond_eB, FALSE, TRUE,
5500 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5503 Ydiamond_w, FALSE, FALSE,
5504 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5507 Ydiamond_wB, FALSE, TRUE,
5508 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5511 Ydiamond_eat, FALSE, FALSE,
5512 EL_DIAMOND, ACTION_COLLECTING, -1
5515 Ydiamond_stone, FALSE, FALSE,
5516 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5519 Xdrip_fall, TRUE, FALSE,
5520 EL_AMOEBA_DROP, -1, -1
5523 Xdrip_stretch, FALSE, FALSE,
5524 EL_AMOEBA_DROP, ACTION_FALLING, -1
5527 Xdrip_stretchB, FALSE, TRUE,
5528 EL_AMOEBA_DROP, ACTION_FALLING, -1
5531 Xdrip_eat, FALSE, FALSE,
5532 EL_AMOEBA_DROP, ACTION_GROWING, -1
5535 Ydrip_s1, FALSE, FALSE,
5536 EL_AMOEBA_DROP, ACTION_FALLING, -1
5539 Ydrip_s1B, FALSE, TRUE,
5540 EL_AMOEBA_DROP, ACTION_FALLING, -1
5543 Ydrip_s2, FALSE, FALSE,
5544 EL_AMOEBA_DROP, ACTION_FALLING, -1
5547 Ydrip_s2B, FALSE, TRUE,
5548 EL_AMOEBA_DROP, ACTION_FALLING, -1
5555 Xbomb_pause, FALSE, FALSE,
5559 Xbomb_fall, FALSE, FALSE,
5563 Ybomb_s, FALSE, FALSE,
5564 EL_BOMB, ACTION_FALLING, -1
5567 Ybomb_sB, FALSE, TRUE,
5568 EL_BOMB, ACTION_FALLING, -1
5571 Ybomb_e, FALSE, FALSE,
5572 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5575 Ybomb_eB, FALSE, TRUE,
5576 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5579 Ybomb_w, FALSE, FALSE,
5580 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5583 Ybomb_wB, FALSE, TRUE,
5584 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5587 Ybomb_eat, FALSE, FALSE,
5588 EL_BOMB, ACTION_ACTIVATING, -1
5591 Xballoon, TRUE, FALSE,
5595 Yballoon_n, FALSE, FALSE,
5596 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5599 Yballoon_nB, FALSE, TRUE,
5600 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5603 Yballoon_e, FALSE, FALSE,
5604 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5607 Yballoon_eB, FALSE, TRUE,
5608 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5611 Yballoon_s, FALSE, FALSE,
5612 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5615 Yballoon_sB, FALSE, TRUE,
5616 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5619 Yballoon_w, FALSE, FALSE,
5620 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5623 Yballoon_wB, FALSE, TRUE,
5624 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5627 Xgrass, TRUE, FALSE,
5628 EL_EMC_GRASS, -1, -1
5631 Ygrass_nB, FALSE, FALSE,
5632 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5635 Ygrass_eB, FALSE, FALSE,
5636 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5639 Ygrass_sB, FALSE, FALSE,
5640 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5643 Ygrass_wB, FALSE, FALSE,
5644 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5651 Ydirt_nB, FALSE, FALSE,
5652 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5655 Ydirt_eB, FALSE, FALSE,
5656 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5659 Ydirt_sB, FALSE, FALSE,
5660 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5663 Ydirt_wB, FALSE, FALSE,
5664 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5667 Xacid_ne, TRUE, FALSE,
5668 EL_ACID_POOL_TOPRIGHT, -1, -1
5671 Xacid_se, TRUE, FALSE,
5672 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5675 Xacid_s, TRUE, FALSE,
5676 EL_ACID_POOL_BOTTOM, -1, -1
5679 Xacid_sw, TRUE, FALSE,
5680 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5683 Xacid_nw, TRUE, FALSE,
5684 EL_ACID_POOL_TOPLEFT, -1, -1
5687 Xacid_1, TRUE, FALSE,
5691 Xacid_2, FALSE, FALSE,
5695 Xacid_3, FALSE, FALSE,
5699 Xacid_4, FALSE, FALSE,
5703 Xacid_5, FALSE, FALSE,
5707 Xacid_6, FALSE, FALSE,
5711 Xacid_7, FALSE, FALSE,
5715 Xacid_8, FALSE, FALSE,
5719 Xball_1, TRUE, FALSE,
5720 EL_EMC_MAGIC_BALL, -1, -1
5723 Xball_1B, FALSE, FALSE,
5724 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5727 Xball_2, FALSE, FALSE,
5728 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5731 Xball_2B, FALSE, FALSE,
5732 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5735 Yball_eat, FALSE, FALSE,
5736 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5739 Ykey_1_eat, FALSE, FALSE,
5740 EL_EM_KEY_1, ACTION_COLLECTING, -1
5743 Ykey_2_eat, FALSE, FALSE,
5744 EL_EM_KEY_2, ACTION_COLLECTING, -1
5747 Ykey_3_eat, FALSE, FALSE,
5748 EL_EM_KEY_3, ACTION_COLLECTING, -1
5751 Ykey_4_eat, FALSE, FALSE,
5752 EL_EM_KEY_4, ACTION_COLLECTING, -1
5755 Ykey_5_eat, FALSE, FALSE,
5756 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5759 Ykey_6_eat, FALSE, FALSE,
5760 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5763 Ykey_7_eat, FALSE, FALSE,
5764 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5767 Ykey_8_eat, FALSE, FALSE,
5768 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5771 Ylenses_eat, FALSE, FALSE,
5772 EL_EMC_LENSES, ACTION_COLLECTING, -1
5775 Ymagnify_eat, FALSE, FALSE,
5776 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5779 Ygrass_eat, FALSE, FALSE,
5780 EL_EMC_GRASS, ACTION_SNAPPING, -1
5783 Ydirt_eat, FALSE, FALSE,
5784 EL_SAND, ACTION_SNAPPING, -1
5787 Xgrow_ns, TRUE, FALSE,
5788 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5791 Ygrow_ns_eat, FALSE, FALSE,
5792 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5795 Xgrow_ew, TRUE, FALSE,
5796 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5799 Ygrow_ew_eat, FALSE, FALSE,
5800 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5803 Xwonderwall, TRUE, FALSE,
5804 EL_MAGIC_WALL, -1, -1
5807 XwonderwallB, FALSE, FALSE,
5808 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5811 Xamoeba_1, TRUE, FALSE,
5812 EL_AMOEBA_DRY, ACTION_OTHER, -1
5815 Xamoeba_2, FALSE, FALSE,
5816 EL_AMOEBA_DRY, ACTION_OTHER, -1
5819 Xamoeba_3, FALSE, FALSE,
5820 EL_AMOEBA_DRY, ACTION_OTHER, -1
5823 Xamoeba_4, FALSE, FALSE,
5824 EL_AMOEBA_DRY, ACTION_OTHER, -1
5827 Xamoeba_5, TRUE, FALSE,
5828 EL_AMOEBA_WET, ACTION_OTHER, -1
5831 Xamoeba_6, FALSE, FALSE,
5832 EL_AMOEBA_WET, ACTION_OTHER, -1
5835 Xamoeba_7, FALSE, FALSE,
5836 EL_AMOEBA_WET, ACTION_OTHER, -1
5839 Xamoeba_8, FALSE, FALSE,
5840 EL_AMOEBA_WET, ACTION_OTHER, -1
5843 Xdoor_1, TRUE, FALSE,
5844 EL_EM_GATE_1, -1, -1
5847 Xdoor_2, TRUE, FALSE,
5848 EL_EM_GATE_2, -1, -1
5851 Xdoor_3, TRUE, FALSE,
5852 EL_EM_GATE_3, -1, -1
5855 Xdoor_4, TRUE, FALSE,
5856 EL_EM_GATE_4, -1, -1
5859 Xdoor_5, TRUE, FALSE,
5860 EL_EMC_GATE_5, -1, -1
5863 Xdoor_6, TRUE, FALSE,
5864 EL_EMC_GATE_6, -1, -1
5867 Xdoor_7, TRUE, FALSE,
5868 EL_EMC_GATE_7, -1, -1
5871 Xdoor_8, TRUE, FALSE,
5872 EL_EMC_GATE_8, -1, -1
5875 Xkey_1, TRUE, FALSE,
5879 Xkey_2, TRUE, FALSE,
5883 Xkey_3, TRUE, FALSE,
5887 Xkey_4, TRUE, FALSE,
5891 Xkey_5, TRUE, FALSE,
5892 EL_EMC_KEY_5, -1, -1
5895 Xkey_6, TRUE, FALSE,
5896 EL_EMC_KEY_6, -1, -1
5899 Xkey_7, TRUE, FALSE,
5900 EL_EMC_KEY_7, -1, -1
5903 Xkey_8, TRUE, FALSE,
5904 EL_EMC_KEY_8, -1, -1
5907 Xwind_n, TRUE, FALSE,
5908 EL_BALLOON_SWITCH_UP, -1, -1
5911 Xwind_e, TRUE, FALSE,
5912 EL_BALLOON_SWITCH_RIGHT, -1, -1
5915 Xwind_s, TRUE, FALSE,
5916 EL_BALLOON_SWITCH_DOWN, -1, -1
5919 Xwind_w, TRUE, FALSE,
5920 EL_BALLOON_SWITCH_LEFT, -1, -1
5923 Xwind_nesw, TRUE, FALSE,
5924 EL_BALLOON_SWITCH_ANY, -1, -1
5927 Xwind_stop, TRUE, FALSE,
5928 EL_BALLOON_SWITCH_NONE, -1, -1
5932 EL_EM_EXIT_CLOSED, -1, -1
5935 Xexit_1, TRUE, FALSE,
5936 EL_EM_EXIT_OPEN, -1, -1
5939 Xexit_2, FALSE, FALSE,
5940 EL_EM_EXIT_OPEN, -1, -1
5943 Xexit_3, FALSE, FALSE,
5944 EL_EM_EXIT_OPEN, -1, -1
5947 Xdynamite, TRUE, FALSE,
5948 EL_EM_DYNAMITE, -1, -1
5951 Ydynamite_eat, FALSE, FALSE,
5952 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5955 Xdynamite_1, TRUE, FALSE,
5956 EL_EM_DYNAMITE_ACTIVE, -1, -1
5959 Xdynamite_2, FALSE, FALSE,
5960 EL_EM_DYNAMITE_ACTIVE, -1, -1
5963 Xdynamite_3, FALSE, FALSE,
5964 EL_EM_DYNAMITE_ACTIVE, -1, -1
5967 Xdynamite_4, FALSE, FALSE,
5968 EL_EM_DYNAMITE_ACTIVE, -1, -1
5971 Xbumper, TRUE, FALSE,
5972 EL_EMC_SPRING_BUMPER, -1, -1
5975 XbumperB, FALSE, FALSE,
5976 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5979 Xwheel, TRUE, FALSE,
5980 EL_ROBOT_WHEEL, -1, -1
5983 XwheelB, FALSE, FALSE,
5984 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5987 Xswitch, TRUE, FALSE,
5988 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5991 XswitchB, FALSE, FALSE,
5992 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5996 EL_QUICKSAND_EMPTY, -1, -1
5999 Xsand_stone, TRUE, FALSE,
6000 EL_QUICKSAND_FULL, -1, -1
6003 Xsand_stonein_1, FALSE, TRUE,
6004 EL_ROCK, ACTION_FILLING, -1
6007 Xsand_stonein_2, FALSE, TRUE,
6008 EL_ROCK, ACTION_FILLING, -1
6011 Xsand_stonein_3, FALSE, TRUE,
6012 EL_ROCK, ACTION_FILLING, -1
6015 Xsand_stonein_4, FALSE, TRUE,
6016 EL_ROCK, ACTION_FILLING, -1
6019 Xsand_stonesand_1, FALSE, FALSE,
6020 EL_QUICKSAND_EMPTYING, -1, -1
6023 Xsand_stonesand_2, FALSE, FALSE,
6024 EL_QUICKSAND_EMPTYING, -1, -1
6027 Xsand_stonesand_3, FALSE, FALSE,
6028 EL_QUICKSAND_EMPTYING, -1, -1
6031 Xsand_stonesand_4, FALSE, FALSE,
6032 EL_QUICKSAND_EMPTYING, -1, -1
6035 Xsand_stonesand_quickout_1, FALSE, FALSE,
6036 EL_QUICKSAND_EMPTYING, -1, -1
6039 Xsand_stonesand_quickout_2, FALSE, FALSE,
6040 EL_QUICKSAND_EMPTYING, -1, -1
6043 Xsand_stoneout_1, FALSE, FALSE,
6044 EL_ROCK, ACTION_EMPTYING, -1
6047 Xsand_stoneout_2, FALSE, FALSE,
6048 EL_ROCK, ACTION_EMPTYING, -1
6051 Xsand_sandstone_1, FALSE, FALSE,
6052 EL_QUICKSAND_FILLING, -1, -1
6055 Xsand_sandstone_2, FALSE, FALSE,
6056 EL_QUICKSAND_FILLING, -1, -1
6059 Xsand_sandstone_3, FALSE, FALSE,
6060 EL_QUICKSAND_FILLING, -1, -1
6063 Xsand_sandstone_4, FALSE, FALSE,
6064 EL_QUICKSAND_FILLING, -1, -1
6067 Xplant, TRUE, FALSE,
6068 EL_EMC_PLANT, -1, -1
6071 Yplant, FALSE, FALSE,
6072 EL_EMC_PLANT, -1, -1
6075 Xlenses, TRUE, FALSE,
6076 EL_EMC_LENSES, -1, -1
6079 Xmagnify, TRUE, FALSE,
6080 EL_EMC_MAGNIFIER, -1, -1
6083 Xdripper, TRUE, FALSE,
6084 EL_EMC_DRIPPER, -1, -1
6087 XdripperB, FALSE, FALSE,
6088 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6091 Xfake_blank, TRUE, FALSE,
6092 EL_INVISIBLE_WALL, -1, -1
6095 Xfake_blankB, FALSE, FALSE,
6096 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6099 Xfake_grass, TRUE, FALSE,
6100 EL_EMC_FAKE_GRASS, -1, -1
6103 Xfake_grassB, FALSE, FALSE,
6104 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6107 Xfake_door_1, TRUE, FALSE,
6108 EL_EM_GATE_1_GRAY, -1, -1
6111 Xfake_door_2, TRUE, FALSE,
6112 EL_EM_GATE_2_GRAY, -1, -1
6115 Xfake_door_3, TRUE, FALSE,
6116 EL_EM_GATE_3_GRAY, -1, -1
6119 Xfake_door_4, TRUE, FALSE,
6120 EL_EM_GATE_4_GRAY, -1, -1
6123 Xfake_door_5, TRUE, FALSE,
6124 EL_EMC_GATE_5_GRAY, -1, -1
6127 Xfake_door_6, TRUE, FALSE,
6128 EL_EMC_GATE_6_GRAY, -1, -1
6131 Xfake_door_7, TRUE, FALSE,
6132 EL_EMC_GATE_7_GRAY, -1, -1
6135 Xfake_door_8, TRUE, FALSE,
6136 EL_EMC_GATE_8_GRAY, -1, -1
6139 Xfake_acid_1, TRUE, FALSE,
6140 EL_EMC_FAKE_ACID, -1, -1
6143 Xfake_acid_2, FALSE, FALSE,
6144 EL_EMC_FAKE_ACID, -1, -1
6147 Xfake_acid_3, FALSE, FALSE,
6148 EL_EMC_FAKE_ACID, -1, -1
6151 Xfake_acid_4, FALSE, FALSE,
6152 EL_EMC_FAKE_ACID, -1, -1
6155 Xfake_acid_5, FALSE, FALSE,
6156 EL_EMC_FAKE_ACID, -1, -1
6159 Xfake_acid_6, FALSE, FALSE,
6160 EL_EMC_FAKE_ACID, -1, -1
6163 Xfake_acid_7, FALSE, FALSE,
6164 EL_EMC_FAKE_ACID, -1, -1
6167 Xfake_acid_8, FALSE, FALSE,
6168 EL_EMC_FAKE_ACID, -1, -1
6171 Xsteel_1, TRUE, FALSE,
6172 EL_STEELWALL, -1, -1
6175 Xsteel_2, TRUE, FALSE,
6176 EL_EMC_STEELWALL_2, -1, -1
6179 Xsteel_3, TRUE, FALSE,
6180 EL_EMC_STEELWALL_3, -1, -1
6183 Xsteel_4, TRUE, FALSE,
6184 EL_EMC_STEELWALL_4, -1, -1
6187 Xwall_1, TRUE, FALSE,
6191 Xwall_2, TRUE, FALSE,
6192 EL_EMC_WALL_14, -1, -1
6195 Xwall_3, TRUE, FALSE,
6196 EL_EMC_WALL_15, -1, -1
6199 Xwall_4, TRUE, FALSE,
6200 EL_EMC_WALL_16, -1, -1
6203 Xround_wall_1, TRUE, FALSE,
6204 EL_WALL_SLIPPERY, -1, -1
6207 Xround_wall_2, TRUE, FALSE,
6208 EL_EMC_WALL_SLIPPERY_2, -1, -1
6211 Xround_wall_3, TRUE, FALSE,
6212 EL_EMC_WALL_SLIPPERY_3, -1, -1
6215 Xround_wall_4, TRUE, FALSE,
6216 EL_EMC_WALL_SLIPPERY_4, -1, -1
6219 Xdecor_1, TRUE, FALSE,
6220 EL_EMC_WALL_8, -1, -1
6223 Xdecor_2, TRUE, FALSE,
6224 EL_EMC_WALL_6, -1, -1
6227 Xdecor_3, TRUE, FALSE,
6228 EL_EMC_WALL_4, -1, -1
6231 Xdecor_4, TRUE, FALSE,
6232 EL_EMC_WALL_7, -1, -1
6235 Xdecor_5, TRUE, FALSE,
6236 EL_EMC_WALL_5, -1, -1
6239 Xdecor_6, TRUE, FALSE,
6240 EL_EMC_WALL_9, -1, -1
6243 Xdecor_7, TRUE, FALSE,
6244 EL_EMC_WALL_10, -1, -1
6247 Xdecor_8, TRUE, FALSE,
6248 EL_EMC_WALL_1, -1, -1
6251 Xdecor_9, TRUE, FALSE,
6252 EL_EMC_WALL_2, -1, -1
6255 Xdecor_10, TRUE, FALSE,
6256 EL_EMC_WALL_3, -1, -1
6259 Xdecor_11, TRUE, FALSE,
6260 EL_EMC_WALL_11, -1, -1
6263 Xdecor_12, TRUE, FALSE,
6264 EL_EMC_WALL_12, -1, -1
6267 Xalpha_0, TRUE, FALSE,
6268 EL_CHAR('0'), -1, -1
6271 Xalpha_1, TRUE, FALSE,
6272 EL_CHAR('1'), -1, -1
6275 Xalpha_2, TRUE, FALSE,
6276 EL_CHAR('2'), -1, -1
6279 Xalpha_3, TRUE, FALSE,
6280 EL_CHAR('3'), -1, -1
6283 Xalpha_4, TRUE, FALSE,
6284 EL_CHAR('4'), -1, -1
6287 Xalpha_5, TRUE, FALSE,
6288 EL_CHAR('5'), -1, -1
6291 Xalpha_6, TRUE, FALSE,
6292 EL_CHAR('6'), -1, -1
6295 Xalpha_7, TRUE, FALSE,
6296 EL_CHAR('7'), -1, -1
6299 Xalpha_8, TRUE, FALSE,
6300 EL_CHAR('8'), -1, -1
6303 Xalpha_9, TRUE, FALSE,
6304 EL_CHAR('9'), -1, -1
6307 Xalpha_excla, TRUE, FALSE,
6308 EL_CHAR('!'), -1, -1
6311 Xalpha_quote, TRUE, FALSE,
6312 EL_CHAR('"'), -1, -1
6315 Xalpha_comma, TRUE, FALSE,
6316 EL_CHAR(','), -1, -1
6319 Xalpha_minus, TRUE, FALSE,
6320 EL_CHAR('-'), -1, -1
6323 Xalpha_perio, TRUE, FALSE,
6324 EL_CHAR('.'), -1, -1
6327 Xalpha_colon, TRUE, FALSE,
6328 EL_CHAR(':'), -1, -1
6331 Xalpha_quest, TRUE, FALSE,
6332 EL_CHAR('?'), -1, -1
6335 Xalpha_a, TRUE, FALSE,
6336 EL_CHAR('A'), -1, -1
6339 Xalpha_b, TRUE, FALSE,
6340 EL_CHAR('B'), -1, -1
6343 Xalpha_c, TRUE, FALSE,
6344 EL_CHAR('C'), -1, -1
6347 Xalpha_d, TRUE, FALSE,
6348 EL_CHAR('D'), -1, -1
6351 Xalpha_e, TRUE, FALSE,
6352 EL_CHAR('E'), -1, -1
6355 Xalpha_f, TRUE, FALSE,
6356 EL_CHAR('F'), -1, -1
6359 Xalpha_g, TRUE, FALSE,
6360 EL_CHAR('G'), -1, -1
6363 Xalpha_h, TRUE, FALSE,
6364 EL_CHAR('H'), -1, -1
6367 Xalpha_i, TRUE, FALSE,
6368 EL_CHAR('I'), -1, -1
6371 Xalpha_j, TRUE, FALSE,
6372 EL_CHAR('J'), -1, -1
6375 Xalpha_k, TRUE, FALSE,
6376 EL_CHAR('K'), -1, -1
6379 Xalpha_l, TRUE, FALSE,
6380 EL_CHAR('L'), -1, -1
6383 Xalpha_m, TRUE, FALSE,
6384 EL_CHAR('M'), -1, -1
6387 Xalpha_n, TRUE, FALSE,
6388 EL_CHAR('N'), -1, -1
6391 Xalpha_o, TRUE, FALSE,
6392 EL_CHAR('O'), -1, -1
6395 Xalpha_p, TRUE, FALSE,
6396 EL_CHAR('P'), -1, -1
6399 Xalpha_q, TRUE, FALSE,
6400 EL_CHAR('Q'), -1, -1
6403 Xalpha_r, TRUE, FALSE,
6404 EL_CHAR('R'), -1, -1
6407 Xalpha_s, TRUE, FALSE,
6408 EL_CHAR('S'), -1, -1
6411 Xalpha_t, TRUE, FALSE,
6412 EL_CHAR('T'), -1, -1
6415 Xalpha_u, TRUE, FALSE,
6416 EL_CHAR('U'), -1, -1
6419 Xalpha_v, TRUE, FALSE,
6420 EL_CHAR('V'), -1, -1
6423 Xalpha_w, TRUE, FALSE,
6424 EL_CHAR('W'), -1, -1
6427 Xalpha_x, TRUE, FALSE,
6428 EL_CHAR('X'), -1, -1
6431 Xalpha_y, TRUE, FALSE,
6432 EL_CHAR('Y'), -1, -1
6435 Xalpha_z, TRUE, FALSE,
6436 EL_CHAR('Z'), -1, -1
6439 Xalpha_arrow_e, TRUE, FALSE,
6440 EL_CHAR('>'), -1, -1
6443 Xalpha_arrow_w, TRUE, FALSE,
6444 EL_CHAR('<'), -1, -1
6447 Xalpha_copyr, TRUE, FALSE,
6448 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6452 Xboom_bug, FALSE, FALSE,
6453 EL_BUG, ACTION_EXPLODING, -1
6456 Xboom_bomb, FALSE, FALSE,
6457 EL_BOMB, ACTION_EXPLODING, -1
6460 Xboom_android, FALSE, FALSE,
6461 EL_EMC_ANDROID, ACTION_OTHER, -1
6464 Xboom_1, FALSE, FALSE,
6465 EL_DEFAULT, ACTION_EXPLODING, -1
6468 Xboom_2, FALSE, FALSE,
6469 EL_DEFAULT, ACTION_EXPLODING, -1
6472 Znormal, FALSE, FALSE,
6476 Zdynamite, FALSE, FALSE,
6480 Zplayer, FALSE, FALSE,
6484 ZBORDER, FALSE, FALSE,
6494 static struct Mapping_EM_to_RND_player
6503 em_player_mapping_list[] =
6507 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6511 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6515 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6519 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6523 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6527 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6531 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6535 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6539 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6543 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6547 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6551 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6555 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6559 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6563 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6567 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6571 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6575 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6579 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6583 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6587 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6591 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6595 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6599 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6603 EL_PLAYER_1, ACTION_DEFAULT, -1,
6607 EL_PLAYER_2, ACTION_DEFAULT, -1,
6611 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6615 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6619 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6623 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6627 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6631 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6635 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6639 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6643 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6647 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6651 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6655 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6659 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6663 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6667 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6671 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6675 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6679 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6683 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6687 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6691 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6695 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6699 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6703 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6707 EL_PLAYER_3, ACTION_DEFAULT, -1,
6711 EL_PLAYER_4, ACTION_DEFAULT, -1,
6720 int map_element_RND_to_EM(int element_rnd)
6722 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6723 static boolean mapping_initialized = FALSE;
6725 if (!mapping_initialized)
6729 /* return "Xalpha_quest" for all undefined elements in mapping array */
6730 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6731 mapping_RND_to_EM[i] = Xalpha_quest;
6733 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6734 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6735 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6736 em_object_mapping_list[i].element_em;
6738 mapping_initialized = TRUE;
6741 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6742 return mapping_RND_to_EM[element_rnd];
6744 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6749 int map_element_EM_to_RND(int element_em)
6751 static unsigned short mapping_EM_to_RND[TILE_MAX];
6752 static boolean mapping_initialized = FALSE;
6754 if (!mapping_initialized)
6758 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6759 for (i = 0; i < TILE_MAX; i++)
6760 mapping_EM_to_RND[i] = EL_UNKNOWN;
6762 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6763 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6764 em_object_mapping_list[i].element_rnd;
6766 mapping_initialized = TRUE;
6769 if (element_em >= 0 && element_em < TILE_MAX)
6770 return mapping_EM_to_RND[element_em];
6772 Error(ERR_WARN, "invalid EM level element %d", element_em);
6777 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6779 struct LevelInfo_EM *level_em = level->native_em_level;
6780 struct LEVEL *lev = level_em->lev;
6783 for (i = 0; i < TILE_MAX; i++)
6784 lev->android_array[i] = Xblank;
6786 for (i = 0; i < level->num_android_clone_elements; i++)
6788 int element_rnd = level->android_clone_element[i];
6789 int element_em = map_element_RND_to_EM(element_rnd);
6791 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6792 if (em_object_mapping_list[j].element_rnd == element_rnd)
6793 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6797 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6799 struct LevelInfo_EM *level_em = level->native_em_level;
6800 struct LEVEL *lev = level_em->lev;
6803 level->num_android_clone_elements = 0;
6805 for (i = 0; i < TILE_MAX; i++)
6807 int element_em = lev->android_array[i];
6809 boolean element_found = FALSE;
6811 if (element_em == Xblank)
6814 element_rnd = map_element_EM_to_RND(element_em);
6816 for (j = 0; j < level->num_android_clone_elements; j++)
6817 if (level->android_clone_element[j] == element_rnd)
6818 element_found = TRUE;
6822 level->android_clone_element[level->num_android_clone_elements++] =
6825 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6830 if (level->num_android_clone_elements == 0)
6832 level->num_android_clone_elements = 1;
6833 level->android_clone_element[0] = EL_EMPTY;
6837 int map_direction_RND_to_EM(int direction)
6839 return (direction == MV_UP ? 0 :
6840 direction == MV_RIGHT ? 1 :
6841 direction == MV_DOWN ? 2 :
6842 direction == MV_LEFT ? 3 :
6846 int map_direction_EM_to_RND(int direction)
6848 return (direction == 0 ? MV_UP :
6849 direction == 1 ? MV_RIGHT :
6850 direction == 2 ? MV_DOWN :
6851 direction == 3 ? MV_LEFT :
6855 int map_element_RND_to_SP(int element_rnd)
6857 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6859 if (element_rnd >= EL_SP_START &&
6860 element_rnd <= EL_SP_END)
6861 element_sp = element_rnd - EL_SP_START;
6862 else if (element_rnd == EL_EMPTY_SPACE)
6864 else if (element_rnd == EL_INVISIBLE_WALL)
6870 int map_element_SP_to_RND(int element_sp)
6872 int element_rnd = EL_UNKNOWN;
6874 if (element_sp >= 0x00 &&
6876 element_rnd = EL_SP_START + element_sp;
6877 else if (element_sp == 0x28)
6878 element_rnd = EL_INVISIBLE_WALL;
6883 int map_action_SP_to_RND(int action_sp)
6887 case actActive: return ACTION_ACTIVE;
6888 case actImpact: return ACTION_IMPACT;
6889 case actExploding: return ACTION_EXPLODING;
6890 case actDigging: return ACTION_DIGGING;
6891 case actSnapping: return ACTION_SNAPPING;
6892 case actCollecting: return ACTION_COLLECTING;
6893 case actPassing: return ACTION_PASSING;
6894 case actPushing: return ACTION_PUSHING;
6895 case actDropping: return ACTION_DROPPING;
6897 default: return ACTION_DEFAULT;
6901 int get_next_element(int element)
6905 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6906 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6907 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6908 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6909 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6910 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6911 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6912 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6913 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6914 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6915 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6917 default: return element;
6921 int el_act_dir2img(int element, int action, int direction)
6923 element = GFX_ELEMENT(element);
6924 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6926 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6927 return element_info[element].direction_graphic[action][direction];
6930 static int el_act_dir2crm(int element, int action, int direction)
6932 element = GFX_ELEMENT(element);
6933 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6935 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6936 return element_info[element].direction_crumbled[action][direction];
6939 int el_act2img(int element, int action)
6941 element = GFX_ELEMENT(element);
6943 return element_info[element].graphic[action];
6946 int el_act2crm(int element, int action)
6948 element = GFX_ELEMENT(element);
6950 return element_info[element].crumbled[action];
6953 int el_dir2img(int element, int direction)
6955 element = GFX_ELEMENT(element);
6957 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6960 int el2baseimg(int element)
6962 return element_info[element].graphic[ACTION_DEFAULT];
6965 int el2img(int element)
6967 element = GFX_ELEMENT(element);
6969 return element_info[element].graphic[ACTION_DEFAULT];
6972 int el2edimg(int element)
6974 element = GFX_ELEMENT(element);
6976 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6979 int el2preimg(int element)
6981 element = GFX_ELEMENT(element);
6983 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6986 int el2panelimg(int element)
6988 element = GFX_ELEMENT(element);
6990 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6993 int font2baseimg(int font_nr)
6995 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6998 int getBeltNrFromBeltElement(int element)
7000 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
7001 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
7002 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
7005 int getBeltNrFromBeltActiveElement(int element)
7007 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
7008 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
7009 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
7012 int getBeltNrFromBeltSwitchElement(int element)
7014 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
7015 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
7016 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
7019 int getBeltDirNrFromBeltElement(int element)
7021 static int belt_base_element[4] =
7023 EL_CONVEYOR_BELT_1_LEFT,
7024 EL_CONVEYOR_BELT_2_LEFT,
7025 EL_CONVEYOR_BELT_3_LEFT,
7026 EL_CONVEYOR_BELT_4_LEFT
7029 int belt_nr = getBeltNrFromBeltElement(element);
7030 int belt_dir_nr = element - belt_base_element[belt_nr];
7032 return (belt_dir_nr % 3);
7035 int getBeltDirNrFromBeltSwitchElement(int element)
7037 static int belt_base_element[4] =
7039 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7040 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7041 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7042 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7045 int belt_nr = getBeltNrFromBeltSwitchElement(element);
7046 int belt_dir_nr = element - belt_base_element[belt_nr];
7048 return (belt_dir_nr % 3);
7051 int getBeltDirFromBeltElement(int element)
7053 static int belt_move_dir[3] =
7060 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7062 return belt_move_dir[belt_dir_nr];
7065 int getBeltDirFromBeltSwitchElement(int element)
7067 static int belt_move_dir[3] =
7074 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7076 return belt_move_dir[belt_dir_nr];
7079 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7081 static int belt_base_element[4] =
7083 EL_CONVEYOR_BELT_1_LEFT,
7084 EL_CONVEYOR_BELT_2_LEFT,
7085 EL_CONVEYOR_BELT_3_LEFT,
7086 EL_CONVEYOR_BELT_4_LEFT
7089 return belt_base_element[belt_nr] + belt_dir_nr;
7092 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7094 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7096 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7099 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7101 static int belt_base_element[4] =
7103 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7104 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7105 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7106 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7109 return belt_base_element[belt_nr] + belt_dir_nr;
7112 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7114 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7116 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7119 boolean getTeamMode_EM()
7121 return game.team_mode;
7124 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7126 int game_frame_delay_value;
7128 game_frame_delay_value =
7129 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7130 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7133 if (tape.playing && tape.warp_forward && !tape.pausing)
7134 game_frame_delay_value = 0;
7136 return game_frame_delay_value;
7139 unsigned int InitRND(int seed)
7141 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7142 return InitEngineRandom_EM(seed);
7143 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7144 return InitEngineRandom_SP(seed);
7146 return InitEngineRandom_RND(seed);
7149 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7150 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7152 inline static int get_effective_element_EM(int tile, int frame_em)
7154 int element = object_mapping[tile].element_rnd;
7155 int action = object_mapping[tile].action;
7156 boolean is_backside = object_mapping[tile].is_backside;
7157 boolean action_removing = (action == ACTION_DIGGING ||
7158 action == ACTION_SNAPPING ||
7159 action == ACTION_COLLECTING);
7165 case Yacid_splash_eB:
7166 case Yacid_splash_wB:
7167 return (frame_em > 5 ? EL_EMPTY : element);
7173 else /* frame_em == 7 */
7177 case Yacid_splash_eB:
7178 case Yacid_splash_wB:
7181 case Yemerald_stone:
7184 case Ydiamond_stone:
7188 case Xdrip_stretchB:
7207 case Xsand_stonein_1:
7208 case Xsand_stonein_2:
7209 case Xsand_stonein_3:
7210 case Xsand_stonein_4:
7214 return (is_backside || action_removing ? EL_EMPTY : element);
7219 inline static boolean check_linear_animation_EM(int tile)
7223 case Xsand_stonesand_1:
7224 case Xsand_stonesand_quickout_1:
7225 case Xsand_sandstone_1:
7226 case Xsand_stonein_1:
7227 case Xsand_stoneout_1:
7246 case Yacid_splash_eB:
7247 case Yacid_splash_wB:
7248 case Yemerald_stone:
7255 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7256 boolean has_crumbled_graphics,
7257 int crumbled, int sync_frame)
7259 /* if element can be crumbled, but certain action graphics are just empty
7260 space (like instantly snapping sand to empty space in 1 frame), do not
7261 treat these empty space graphics as crumbled graphics in EMC engine */
7262 if (crumbled == IMG_EMPTY_SPACE)
7263 has_crumbled_graphics = FALSE;
7265 if (has_crumbled_graphics)
7267 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7268 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7269 g_crumbled->anim_delay,
7270 g_crumbled->anim_mode,
7271 g_crumbled->anim_start_frame,
7274 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7275 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7277 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7279 g_em->has_crumbled_graphics = TRUE;
7283 g_em->crumbled_bitmap = NULL;
7284 g_em->crumbled_src_x = 0;
7285 g_em->crumbled_src_y = 0;
7286 g_em->crumbled_border_size = 0;
7288 g_em->has_crumbled_graphics = FALSE;
7292 void ResetGfxAnimation_EM(int x, int y, int tile)
7297 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7298 int tile, int frame_em, int x, int y)
7300 int action = object_mapping[tile].action;
7301 int direction = object_mapping[tile].direction;
7302 int effective_element = get_effective_element_EM(tile, frame_em);
7303 int graphic = (direction == MV_NONE ?
7304 el_act2img(effective_element, action) :
7305 el_act_dir2img(effective_element, action, direction));
7306 struct GraphicInfo *g = &graphic_info[graphic];
7308 boolean action_removing = (action == ACTION_DIGGING ||
7309 action == ACTION_SNAPPING ||
7310 action == ACTION_COLLECTING);
7311 boolean action_moving = (action == ACTION_FALLING ||
7312 action == ACTION_MOVING ||
7313 action == ACTION_PUSHING ||
7314 action == ACTION_EATING ||
7315 action == ACTION_FILLING ||
7316 action == ACTION_EMPTYING);
7317 boolean action_falling = (action == ACTION_FALLING ||
7318 action == ACTION_FILLING ||
7319 action == ACTION_EMPTYING);
7321 /* special case: graphic uses "2nd movement tile" and has defined
7322 7 frames for movement animation (or less) => use default graphic
7323 for last (8th) frame which ends the movement animation */
7324 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7326 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7327 graphic = (direction == MV_NONE ?
7328 el_act2img(effective_element, action) :
7329 el_act_dir2img(effective_element, action, direction));
7331 g = &graphic_info[graphic];
7334 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7338 else if (action_moving)
7340 boolean is_backside = object_mapping[tile].is_backside;
7344 int direction = object_mapping[tile].direction;
7345 int move_dir = (action_falling ? MV_DOWN : direction);
7350 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7351 if (g->double_movement && frame_em == 0)
7355 if (move_dir == MV_LEFT)
7356 GfxFrame[x - 1][y] = GfxFrame[x][y];
7357 else if (move_dir == MV_RIGHT)
7358 GfxFrame[x + 1][y] = GfxFrame[x][y];
7359 else if (move_dir == MV_UP)
7360 GfxFrame[x][y - 1] = GfxFrame[x][y];
7361 else if (move_dir == MV_DOWN)
7362 GfxFrame[x][y + 1] = GfxFrame[x][y];
7369 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7370 if (tile == Xsand_stonesand_quickout_1 ||
7371 tile == Xsand_stonesand_quickout_2)
7375 if (graphic_info[graphic].anim_global_sync)
7376 sync_frame = FrameCounter;
7377 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7378 sync_frame = GfxFrame[x][y];
7380 sync_frame = 0; /* playfield border (pseudo steel) */
7382 SetRandomAnimationValue(x, y);
7384 int frame = getAnimationFrame(g->anim_frames,
7387 g->anim_start_frame,
7390 g_em->unique_identifier =
7391 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7394 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7395 int tile, int frame_em, int x, int y)
7397 int action = object_mapping[tile].action;
7398 int direction = object_mapping[tile].direction;
7399 boolean is_backside = object_mapping[tile].is_backside;
7400 int effective_element = get_effective_element_EM(tile, frame_em);
7401 int effective_action = action;
7402 int graphic = (direction == MV_NONE ?
7403 el_act2img(effective_element, effective_action) :
7404 el_act_dir2img(effective_element, effective_action,
7406 int crumbled = (direction == MV_NONE ?
7407 el_act2crm(effective_element, effective_action) :
7408 el_act_dir2crm(effective_element, effective_action,
7410 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7411 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7412 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7413 struct GraphicInfo *g = &graphic_info[graphic];
7416 /* special case: graphic uses "2nd movement tile" and has defined
7417 7 frames for movement animation (or less) => use default graphic
7418 for last (8th) frame which ends the movement animation */
7419 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7421 effective_action = ACTION_DEFAULT;
7422 graphic = (direction == MV_NONE ?
7423 el_act2img(effective_element, effective_action) :
7424 el_act_dir2img(effective_element, effective_action,
7426 crumbled = (direction == MV_NONE ?
7427 el_act2crm(effective_element, effective_action) :
7428 el_act_dir2crm(effective_element, effective_action,
7431 g = &graphic_info[graphic];
7434 if (graphic_info[graphic].anim_global_sync)
7435 sync_frame = FrameCounter;
7436 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7437 sync_frame = GfxFrame[x][y];
7439 sync_frame = 0; /* playfield border (pseudo steel) */
7441 SetRandomAnimationValue(x, y);
7443 int frame = getAnimationFrame(g->anim_frames,
7446 g->anim_start_frame,
7449 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7450 g->double_movement && is_backside);
7452 /* (updating the "crumbled" graphic definitions is probably not really needed,
7453 as animations for crumbled graphics can't be longer than one EMC cycle) */
7454 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7458 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7459 int player_nr, int anim, int frame_em)
7461 int element = player_mapping[player_nr][anim].element_rnd;
7462 int action = player_mapping[player_nr][anim].action;
7463 int direction = player_mapping[player_nr][anim].direction;
7464 int graphic = (direction == MV_NONE ?
7465 el_act2img(element, action) :
7466 el_act_dir2img(element, action, direction));
7467 struct GraphicInfo *g = &graphic_info[graphic];
7470 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7472 stored_player[player_nr].StepFrame = frame_em;
7474 sync_frame = stored_player[player_nr].Frame;
7476 int frame = getAnimationFrame(g->anim_frames,
7479 g->anim_start_frame,
7482 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7483 &g_em->src_x, &g_em->src_y, FALSE);
7486 void InitGraphicInfo_EM(void)
7491 int num_em_gfx_errors = 0;
7493 if (graphic_info_em_object[0][0].bitmap == NULL)
7495 /* EM graphics not yet initialized in em_open_all() */
7500 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7503 /* always start with reliable default values */
7504 for (i = 0; i < TILE_MAX; i++)
7506 object_mapping[i].element_rnd = EL_UNKNOWN;
7507 object_mapping[i].is_backside = FALSE;
7508 object_mapping[i].action = ACTION_DEFAULT;
7509 object_mapping[i].direction = MV_NONE;
7512 /* always start with reliable default values */
7513 for (p = 0; p < MAX_PLAYERS; p++)
7515 for (i = 0; i < SPR_MAX; i++)
7517 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7518 player_mapping[p][i].action = ACTION_DEFAULT;
7519 player_mapping[p][i].direction = MV_NONE;
7523 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7525 int e = em_object_mapping_list[i].element_em;
7527 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7528 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7530 if (em_object_mapping_list[i].action != -1)
7531 object_mapping[e].action = em_object_mapping_list[i].action;
7533 if (em_object_mapping_list[i].direction != -1)
7534 object_mapping[e].direction =
7535 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7538 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7540 int a = em_player_mapping_list[i].action_em;
7541 int p = em_player_mapping_list[i].player_nr;
7543 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7545 if (em_player_mapping_list[i].action != -1)
7546 player_mapping[p][a].action = em_player_mapping_list[i].action;
7548 if (em_player_mapping_list[i].direction != -1)
7549 player_mapping[p][a].direction =
7550 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7553 for (i = 0; i < TILE_MAX; i++)
7555 int element = object_mapping[i].element_rnd;
7556 int action = object_mapping[i].action;
7557 int direction = object_mapping[i].direction;
7558 boolean is_backside = object_mapping[i].is_backside;
7559 boolean action_exploding = ((action == ACTION_EXPLODING ||
7560 action == ACTION_SMASHED_BY_ROCK ||
7561 action == ACTION_SMASHED_BY_SPRING) &&
7562 element != EL_DIAMOND);
7563 boolean action_active = (action == ACTION_ACTIVE);
7564 boolean action_other = (action == ACTION_OTHER);
7566 for (j = 0; j < 8; j++)
7568 int effective_element = get_effective_element_EM(i, j);
7569 int effective_action = (j < 7 ? action :
7570 i == Xdrip_stretch ? action :
7571 i == Xdrip_stretchB ? action :
7572 i == Ydrip_s1 ? action :
7573 i == Ydrip_s1B ? action :
7574 i == Xball_1B ? action :
7575 i == Xball_2 ? action :
7576 i == Xball_2B ? action :
7577 i == Yball_eat ? action :
7578 i == Ykey_1_eat ? action :
7579 i == Ykey_2_eat ? action :
7580 i == Ykey_3_eat ? action :
7581 i == Ykey_4_eat ? action :
7582 i == Ykey_5_eat ? action :
7583 i == Ykey_6_eat ? action :
7584 i == Ykey_7_eat ? action :
7585 i == Ykey_8_eat ? action :
7586 i == Ylenses_eat ? action :
7587 i == Ymagnify_eat ? action :
7588 i == Ygrass_eat ? action :
7589 i == Ydirt_eat ? action :
7590 i == Xsand_stonein_1 ? action :
7591 i == Xsand_stonein_2 ? action :
7592 i == Xsand_stonein_3 ? action :
7593 i == Xsand_stonein_4 ? action :
7594 i == Xsand_stoneout_1 ? action :
7595 i == Xsand_stoneout_2 ? action :
7596 i == Xboom_android ? ACTION_EXPLODING :
7597 action_exploding ? ACTION_EXPLODING :
7598 action_active ? action :
7599 action_other ? action :
7601 int graphic = (el_act_dir2img(effective_element, effective_action,
7603 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7605 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7606 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7607 boolean has_action_graphics = (graphic != base_graphic);
7608 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7609 struct GraphicInfo *g = &graphic_info[graphic];
7610 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7613 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7614 boolean special_animation = (action != ACTION_DEFAULT &&
7615 g->anim_frames == 3 &&
7616 g->anim_delay == 2 &&
7617 g->anim_mode & ANIM_LINEAR);
7618 int sync_frame = (i == Xdrip_stretch ? 7 :
7619 i == Xdrip_stretchB ? 7 :
7620 i == Ydrip_s2 ? j + 8 :
7621 i == Ydrip_s2B ? j + 8 :
7630 i == Xfake_acid_1 ? 0 :
7631 i == Xfake_acid_2 ? 10 :
7632 i == Xfake_acid_3 ? 20 :
7633 i == Xfake_acid_4 ? 30 :
7634 i == Xfake_acid_5 ? 40 :
7635 i == Xfake_acid_6 ? 50 :
7636 i == Xfake_acid_7 ? 60 :
7637 i == Xfake_acid_8 ? 70 :
7639 i == Xball_2B ? j + 8 :
7640 i == Yball_eat ? j + 1 :
7641 i == Ykey_1_eat ? j + 1 :
7642 i == Ykey_2_eat ? j + 1 :
7643 i == Ykey_3_eat ? j + 1 :
7644 i == Ykey_4_eat ? j + 1 :
7645 i == Ykey_5_eat ? j + 1 :
7646 i == Ykey_6_eat ? j + 1 :
7647 i == Ykey_7_eat ? j + 1 :
7648 i == Ykey_8_eat ? j + 1 :
7649 i == Ylenses_eat ? j + 1 :
7650 i == Ymagnify_eat ? j + 1 :
7651 i == Ygrass_eat ? j + 1 :
7652 i == Ydirt_eat ? j + 1 :
7653 i == Xamoeba_1 ? 0 :
7654 i == Xamoeba_2 ? 1 :
7655 i == Xamoeba_3 ? 2 :
7656 i == Xamoeba_4 ? 3 :
7657 i == Xamoeba_5 ? 0 :
7658 i == Xamoeba_6 ? 1 :
7659 i == Xamoeba_7 ? 2 :
7660 i == Xamoeba_8 ? 3 :
7661 i == Xexit_2 ? j + 8 :
7662 i == Xexit_3 ? j + 16 :
7663 i == Xdynamite_1 ? 0 :
7664 i == Xdynamite_2 ? 8 :
7665 i == Xdynamite_3 ? 16 :
7666 i == Xdynamite_4 ? 24 :
7667 i == Xsand_stonein_1 ? j + 1 :
7668 i == Xsand_stonein_2 ? j + 9 :
7669 i == Xsand_stonein_3 ? j + 17 :
7670 i == Xsand_stonein_4 ? j + 25 :
7671 i == Xsand_stoneout_1 && j == 0 ? 0 :
7672 i == Xsand_stoneout_1 && j == 1 ? 0 :
7673 i == Xsand_stoneout_1 && j == 2 ? 1 :
7674 i == Xsand_stoneout_1 && j == 3 ? 2 :
7675 i == Xsand_stoneout_1 && j == 4 ? 2 :
7676 i == Xsand_stoneout_1 && j == 5 ? 3 :
7677 i == Xsand_stoneout_1 && j == 6 ? 4 :
7678 i == Xsand_stoneout_1 && j == 7 ? 4 :
7679 i == Xsand_stoneout_2 && j == 0 ? 5 :
7680 i == Xsand_stoneout_2 && j == 1 ? 6 :
7681 i == Xsand_stoneout_2 && j == 2 ? 7 :
7682 i == Xsand_stoneout_2 && j == 3 ? 8 :
7683 i == Xsand_stoneout_2 && j == 4 ? 9 :
7684 i == Xsand_stoneout_2 && j == 5 ? 11 :
7685 i == Xsand_stoneout_2 && j == 6 ? 13 :
7686 i == Xsand_stoneout_2 && j == 7 ? 15 :
7687 i == Xboom_bug && j == 1 ? 2 :
7688 i == Xboom_bug && j == 2 ? 2 :
7689 i == Xboom_bug && j == 3 ? 4 :
7690 i == Xboom_bug && j == 4 ? 4 :
7691 i == Xboom_bug && j == 5 ? 2 :
7692 i == Xboom_bug && j == 6 ? 2 :
7693 i == Xboom_bug && j == 7 ? 0 :
7694 i == Xboom_bomb && j == 1 ? 2 :
7695 i == Xboom_bomb && j == 2 ? 2 :
7696 i == Xboom_bomb && j == 3 ? 4 :
7697 i == Xboom_bomb && j == 4 ? 4 :
7698 i == Xboom_bomb && j == 5 ? 2 :
7699 i == Xboom_bomb && j == 6 ? 2 :
7700 i == Xboom_bomb && j == 7 ? 0 :
7701 i == Xboom_android && j == 7 ? 6 :
7702 i == Xboom_1 && j == 1 ? 2 :
7703 i == Xboom_1 && j == 2 ? 2 :
7704 i == Xboom_1 && j == 3 ? 4 :
7705 i == Xboom_1 && j == 4 ? 4 :
7706 i == Xboom_1 && j == 5 ? 6 :
7707 i == Xboom_1 && j == 6 ? 6 :
7708 i == Xboom_1 && j == 7 ? 8 :
7709 i == Xboom_2 && j == 0 ? 8 :
7710 i == Xboom_2 && j == 1 ? 8 :
7711 i == Xboom_2 && j == 2 ? 10 :
7712 i == Xboom_2 && j == 3 ? 10 :
7713 i == Xboom_2 && j == 4 ? 10 :
7714 i == Xboom_2 && j == 5 ? 12 :
7715 i == Xboom_2 && j == 6 ? 12 :
7716 i == Xboom_2 && j == 7 ? 12 :
7717 special_animation && j == 4 ? 3 :
7718 effective_action != action ? 0 :
7722 Bitmap *debug_bitmap = g_em->bitmap;
7723 int debug_src_x = g_em->src_x;
7724 int debug_src_y = g_em->src_y;
7727 int frame = getAnimationFrame(g->anim_frames,
7730 g->anim_start_frame,
7733 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7734 g->double_movement && is_backside);
7736 g_em->bitmap = src_bitmap;
7737 g_em->src_x = src_x;
7738 g_em->src_y = src_y;
7739 g_em->src_offset_x = 0;
7740 g_em->src_offset_y = 0;
7741 g_em->dst_offset_x = 0;
7742 g_em->dst_offset_y = 0;
7743 g_em->width = TILEX;
7744 g_em->height = TILEY;
7746 g_em->preserve_background = FALSE;
7748 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7751 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7752 effective_action == ACTION_MOVING ||
7753 effective_action == ACTION_PUSHING ||
7754 effective_action == ACTION_EATING)) ||
7755 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7756 effective_action == ACTION_EMPTYING)))
7759 (effective_action == ACTION_FALLING ||
7760 effective_action == ACTION_FILLING ||
7761 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7762 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7763 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7764 int num_steps = (i == Ydrip_s1 ? 16 :
7765 i == Ydrip_s1B ? 16 :
7766 i == Ydrip_s2 ? 16 :
7767 i == Ydrip_s2B ? 16 :
7768 i == Xsand_stonein_1 ? 32 :
7769 i == Xsand_stonein_2 ? 32 :
7770 i == Xsand_stonein_3 ? 32 :
7771 i == Xsand_stonein_4 ? 32 :
7772 i == Xsand_stoneout_1 ? 16 :
7773 i == Xsand_stoneout_2 ? 16 : 8);
7774 int cx = ABS(dx) * (TILEX / num_steps);
7775 int cy = ABS(dy) * (TILEY / num_steps);
7776 int step_frame = (i == Ydrip_s2 ? j + 8 :
7777 i == Ydrip_s2B ? j + 8 :
7778 i == Xsand_stonein_2 ? j + 8 :
7779 i == Xsand_stonein_3 ? j + 16 :
7780 i == Xsand_stonein_4 ? j + 24 :
7781 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7782 int step = (is_backside ? step_frame : num_steps - step_frame);
7784 if (is_backside) /* tile where movement starts */
7786 if (dx < 0 || dy < 0)
7788 g_em->src_offset_x = cx * step;
7789 g_em->src_offset_y = cy * step;
7793 g_em->dst_offset_x = cx * step;
7794 g_em->dst_offset_y = cy * step;
7797 else /* tile where movement ends */
7799 if (dx < 0 || dy < 0)
7801 g_em->dst_offset_x = cx * step;
7802 g_em->dst_offset_y = cy * step;
7806 g_em->src_offset_x = cx * step;
7807 g_em->src_offset_y = cy * step;
7811 g_em->width = TILEX - cx * step;
7812 g_em->height = TILEY - cy * step;
7815 /* create unique graphic identifier to decide if tile must be redrawn */
7816 /* bit 31 - 16 (16 bit): EM style graphic
7817 bit 15 - 12 ( 4 bit): EM style frame
7818 bit 11 - 6 ( 6 bit): graphic width
7819 bit 5 - 0 ( 6 bit): graphic height */
7820 g_em->unique_identifier =
7821 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7825 /* skip check for EMC elements not contained in original EMC artwork */
7826 if (element == EL_EMC_FAKE_ACID)
7829 if (g_em->bitmap != debug_bitmap ||
7830 g_em->src_x != debug_src_x ||
7831 g_em->src_y != debug_src_y ||
7832 g_em->src_offset_x != 0 ||
7833 g_em->src_offset_y != 0 ||
7834 g_em->dst_offset_x != 0 ||
7835 g_em->dst_offset_y != 0 ||
7836 g_em->width != TILEX ||
7837 g_em->height != TILEY)
7839 static int last_i = -1;
7847 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7848 i, element, element_info[element].token_name,
7849 element_action_info[effective_action].suffix, direction);
7851 if (element != effective_element)
7852 printf(" [%d ('%s')]",
7854 element_info[effective_element].token_name);
7858 if (g_em->bitmap != debug_bitmap)
7859 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7860 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7862 if (g_em->src_x != debug_src_x ||
7863 g_em->src_y != debug_src_y)
7864 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7865 j, (is_backside ? 'B' : 'F'),
7866 g_em->src_x, g_em->src_y,
7867 g_em->src_x / 32, g_em->src_y / 32,
7868 debug_src_x, debug_src_y,
7869 debug_src_x / 32, debug_src_y / 32);
7871 if (g_em->src_offset_x != 0 ||
7872 g_em->src_offset_y != 0 ||
7873 g_em->dst_offset_x != 0 ||
7874 g_em->dst_offset_y != 0)
7875 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7877 g_em->src_offset_x, g_em->src_offset_y,
7878 g_em->dst_offset_x, g_em->dst_offset_y);
7880 if (g_em->width != TILEX ||
7881 g_em->height != TILEY)
7882 printf(" %d (%d): size %d,%d should be %d,%d\n",
7884 g_em->width, g_em->height, TILEX, TILEY);
7886 num_em_gfx_errors++;
7893 for (i = 0; i < TILE_MAX; i++)
7895 for (j = 0; j < 8; j++)
7897 int element = object_mapping[i].element_rnd;
7898 int action = object_mapping[i].action;
7899 int direction = object_mapping[i].direction;
7900 boolean is_backside = object_mapping[i].is_backside;
7901 int graphic_action = el_act_dir2img(element, action, direction);
7902 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7904 if ((action == ACTION_SMASHED_BY_ROCK ||
7905 action == ACTION_SMASHED_BY_SPRING ||
7906 action == ACTION_EATING) &&
7907 graphic_action == graphic_default)
7909 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7910 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7911 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7912 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7915 /* no separate animation for "smashed by rock" -- use rock instead */
7916 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7917 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7919 g_em->bitmap = g_xx->bitmap;
7920 g_em->src_x = g_xx->src_x;
7921 g_em->src_y = g_xx->src_y;
7922 g_em->src_offset_x = g_xx->src_offset_x;
7923 g_em->src_offset_y = g_xx->src_offset_y;
7924 g_em->dst_offset_x = g_xx->dst_offset_x;
7925 g_em->dst_offset_y = g_xx->dst_offset_y;
7926 g_em->width = g_xx->width;
7927 g_em->height = g_xx->height;
7928 g_em->unique_identifier = g_xx->unique_identifier;
7931 g_em->preserve_background = TRUE;
7936 for (p = 0; p < MAX_PLAYERS; p++)
7938 for (i = 0; i < SPR_MAX; i++)
7940 int element = player_mapping[p][i].element_rnd;
7941 int action = player_mapping[p][i].action;
7942 int direction = player_mapping[p][i].direction;
7944 for (j = 0; j < 8; j++)
7946 int effective_element = element;
7947 int effective_action = action;
7948 int graphic = (direction == MV_NONE ?
7949 el_act2img(effective_element, effective_action) :
7950 el_act_dir2img(effective_element, effective_action,
7952 struct GraphicInfo *g = &graphic_info[graphic];
7953 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7959 Bitmap *debug_bitmap = g_em->bitmap;
7960 int debug_src_x = g_em->src_x;
7961 int debug_src_y = g_em->src_y;
7964 int frame = getAnimationFrame(g->anim_frames,
7967 g->anim_start_frame,
7970 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7972 g_em->bitmap = src_bitmap;
7973 g_em->src_x = src_x;
7974 g_em->src_y = src_y;
7975 g_em->src_offset_x = 0;
7976 g_em->src_offset_y = 0;
7977 g_em->dst_offset_x = 0;
7978 g_em->dst_offset_y = 0;
7979 g_em->width = TILEX;
7980 g_em->height = TILEY;
7984 /* skip check for EMC elements not contained in original EMC artwork */
7985 if (element == EL_PLAYER_3 ||
7986 element == EL_PLAYER_4)
7989 if (g_em->bitmap != debug_bitmap ||
7990 g_em->src_x != debug_src_x ||
7991 g_em->src_y != debug_src_y)
7993 static int last_i = -1;
8001 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
8002 p, i, element, element_info[element].token_name,
8003 element_action_info[effective_action].suffix, direction);
8005 if (element != effective_element)
8006 printf(" [%d ('%s')]",
8008 element_info[effective_element].token_name);
8012 if (g_em->bitmap != debug_bitmap)
8013 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
8014 j, (int)(g_em->bitmap), (int)(debug_bitmap));
8016 if (g_em->src_x != debug_src_x ||
8017 g_em->src_y != debug_src_y)
8018 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
8020 g_em->src_x, g_em->src_y,
8021 g_em->src_x / 32, g_em->src_y / 32,
8022 debug_src_x, debug_src_y,
8023 debug_src_x / 32, debug_src_y / 32);
8025 num_em_gfx_errors++;
8035 printf("::: [%d errors found]\n", num_em_gfx_errors);
8041 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
8042 boolean any_player_moving,
8043 boolean any_player_snapping,
8044 boolean any_player_dropping)
8046 static boolean player_was_waiting = TRUE;
8048 if (frame == 0 && !any_player_dropping)
8050 if (!player_was_waiting)
8052 if (!SaveEngineSnapshotToList())
8055 player_was_waiting = TRUE;
8058 else if (any_player_moving || any_player_snapping || any_player_dropping)
8060 player_was_waiting = FALSE;
8064 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8065 boolean murphy_is_dropping)
8067 static boolean player_was_waiting = TRUE;
8069 if (murphy_is_waiting)
8071 if (!player_was_waiting)
8073 if (!SaveEngineSnapshotToList())
8076 player_was_waiting = TRUE;
8081 player_was_waiting = FALSE;
8085 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8086 boolean any_player_moving,
8087 boolean any_player_snapping,
8088 boolean any_player_dropping)
8090 if (tape.single_step && tape.recording && !tape.pausing)
8091 if (frame == 0 && !any_player_dropping)
8092 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8094 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8095 any_player_snapping, any_player_dropping);
8098 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8099 boolean murphy_is_dropping)
8101 if (tape.single_step && tape.recording && !tape.pausing)
8102 if (murphy_is_waiting)
8103 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8105 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8108 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8109 int graphic, int sync_frame, int x, int y)
8111 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8113 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8116 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8118 return (IS_NEXT_FRAME(sync_frame, graphic));
8121 int getGraphicInfo_Delay(int graphic)
8123 return graphic_info[graphic].anim_delay;
8126 void PlayMenuSoundExt(int sound)
8128 if (sound == SND_UNDEFINED)
8131 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8132 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8135 if (IS_LOOP_SOUND(sound))
8136 PlaySoundLoop(sound);
8141 void PlayMenuSound()
8143 PlayMenuSoundExt(menu.sound[game_status]);
8146 void PlayMenuSoundStereo(int sound, int stereo_position)
8148 if (sound == SND_UNDEFINED)
8151 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8152 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8155 if (IS_LOOP_SOUND(sound))
8156 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8158 PlaySoundStereo(sound, stereo_position);
8161 void PlayMenuSoundIfLoopExt(int sound)
8163 if (sound == SND_UNDEFINED)
8166 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8167 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8170 if (IS_LOOP_SOUND(sound))
8171 PlaySoundLoop(sound);
8174 void PlayMenuSoundIfLoop()
8176 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8179 void PlayMenuMusicExt(int music)
8181 if (music == MUS_UNDEFINED)
8184 if (!setup.sound_music)
8190 void PlayMenuMusic()
8192 PlayMenuMusicExt(menu.music[game_status]);
8195 void PlaySoundActivating()
8198 PlaySound(SND_MENU_ITEM_ACTIVATING);
8202 void PlaySoundSelecting()
8205 PlaySound(SND_MENU_ITEM_SELECTING);
8209 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8211 boolean change_fullscreen = (setup.fullscreen !=
8212 video.fullscreen_enabled);
8213 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8214 setup.window_scaling_percent !=
8215 video.window_scaling_percent);
8217 if (change_window_scaling_percent && video.fullscreen_enabled)
8220 if (!change_window_scaling_percent && !video.fullscreen_available)
8223 #if defined(TARGET_SDL2)
8224 if (change_window_scaling_percent)
8226 SDLSetWindowScaling(setup.window_scaling_percent);
8230 else if (change_fullscreen)
8232 SDLSetWindowFullscreen(setup.fullscreen);
8234 /* set setup value according to successfully changed fullscreen mode */
8235 setup.fullscreen = video.fullscreen_enabled;
8241 if (change_fullscreen ||
8242 change_window_scaling_percent)
8244 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8246 /* save backbuffer content which gets lost when toggling fullscreen mode */
8247 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8249 if (change_window_scaling_percent)
8251 /* keep window mode, but change window scaling */
8252 video.fullscreen_enabled = TRUE; /* force new window scaling */
8255 /* toggle fullscreen */
8256 ChangeVideoModeIfNeeded(setup.fullscreen);
8258 /* set setup value according to successfully changed fullscreen mode */
8259 setup.fullscreen = video.fullscreen_enabled;
8261 /* restore backbuffer content from temporary backbuffer backup bitmap */
8262 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8264 FreeBitmap(tmp_backbuffer);
8266 /* update visible window/screen */
8267 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8271 void JoinRectangles(int *x, int *y, int *width, int *height,
8272 int x2, int y2, int width2, int height2)
8274 // do not join with "off-screen" rectangle
8275 if (x2 == -1 || y2 == -1)
8280 *width = MAX(*width, width2);
8281 *height = MAX(*height, height2);
8284 void SetAnimStatus(int anim_status_new)
8286 if (anim_status_new == GAME_MODE_MAIN)
8287 anim_status_new = GAME_MODE_PSEUDO_MAINONLY;
8289 global.anim_status_next = anim_status_new;
8291 // directly set screen modes that are entered without fading
8292 if ((global.anim_status == GAME_MODE_PSEUDO_MAINONLY &&
8293 global.anim_status_next == GAME_MODE_PSEUDO_TYPENAME) ||
8294 (global.anim_status == GAME_MODE_PSEUDO_TYPENAME &&
8295 global.anim_status_next == GAME_MODE_PSEUDO_MAINONLY))
8296 global.anim_status = global.anim_status_next;
8299 void SetGameStatus(int game_status_new)
8301 game_status = game_status_new;
8303 SetAnimStatus(game_status_new);
8306 void SetFontStatus(int game_status_new)
8308 static int last_game_status = -1;
8310 if (game_status_new != -1)
8312 // set game status for font use after storing last game status
8313 last_game_status = game_status;
8314 game_status = game_status_new;
8318 // reset game status after font use from last stored game status
8319 game_status = last_game_status;
8323 void ResetFontStatus()
8328 void ChangeViewportPropertiesIfNeeded()
8330 int gfx_game_mode = game_status;
8331 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8333 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
8334 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8335 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8336 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8337 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8338 int new_win_xsize = vp_window->width;
8339 int new_win_ysize = vp_window->height;
8340 int border_size = vp_playfield->border_size;
8341 int new_sx = vp_playfield->x + border_size;
8342 int new_sy = vp_playfield->y + border_size;
8343 int new_sxsize = vp_playfield->width - 2 * border_size;
8344 int new_sysize = vp_playfield->height - 2 * border_size;
8345 int new_real_sx = vp_playfield->x;
8346 int new_real_sy = vp_playfield->y;
8347 int new_full_sxsize = vp_playfield->width;
8348 int new_full_sysize = vp_playfield->height;
8349 int new_dx = vp_door_1->x;
8350 int new_dy = vp_door_1->y;
8351 int new_dxsize = vp_door_1->width;
8352 int new_dysize = vp_door_1->height;
8353 int new_vx = vp_door_2->x;
8354 int new_vy = vp_door_2->y;
8355 int new_vxsize = vp_door_2->width;
8356 int new_vysize = vp_door_2->height;
8357 int new_ex = vp_door_3->x;
8358 int new_ey = vp_door_3->y;
8359 int new_exsize = vp_door_3->width;
8360 int new_eysize = vp_door_3->height;
8361 int new_tilesize_var =
8362 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8364 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8365 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8366 int new_scr_fieldx = new_sxsize / tilesize;
8367 int new_scr_fieldy = new_sysize / tilesize;
8368 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8369 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8370 boolean init_gfx_buffers = FALSE;
8371 boolean init_video_buffer = FALSE;
8372 boolean init_gadgets_and_anims = FALSE;
8373 boolean init_em_graphics = FALSE;
8375 if (new_win_xsize != WIN_XSIZE ||
8376 new_win_ysize != WIN_YSIZE)
8378 WIN_XSIZE = new_win_xsize;
8379 WIN_YSIZE = new_win_ysize;
8381 init_video_buffer = TRUE;
8382 init_gfx_buffers = TRUE;
8383 init_gadgets_and_anims = TRUE;
8385 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8388 if (new_scr_fieldx != SCR_FIELDX ||
8389 new_scr_fieldy != SCR_FIELDY)
8391 /* this always toggles between MAIN and GAME when using small tile size */
8393 SCR_FIELDX = new_scr_fieldx;
8394 SCR_FIELDY = new_scr_fieldy;
8396 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8407 new_sxsize != SXSIZE ||
8408 new_sysize != SYSIZE ||
8409 new_dxsize != DXSIZE ||
8410 new_dysize != DYSIZE ||
8411 new_vxsize != VXSIZE ||
8412 new_vysize != VYSIZE ||
8413 new_exsize != EXSIZE ||
8414 new_eysize != EYSIZE ||
8415 new_real_sx != REAL_SX ||
8416 new_real_sy != REAL_SY ||
8417 new_full_sxsize != FULL_SXSIZE ||
8418 new_full_sysize != FULL_SYSIZE ||
8419 new_tilesize_var != TILESIZE_VAR
8422 // ------------------------------------------------------------------------
8423 // determine next fading area for changed viewport definitions
8424 // ------------------------------------------------------------------------
8426 // start with current playfield area (default fading area)
8429 FADE_SXSIZE = FULL_SXSIZE;
8430 FADE_SYSIZE = FULL_SYSIZE;
8432 // add new playfield area if position or size has changed
8433 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8434 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8436 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8437 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8440 // add current and new door 1 area if position or size has changed
8441 if (new_dx != DX || new_dy != DY ||
8442 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8444 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8445 DX, DY, DXSIZE, DYSIZE);
8446 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8447 new_dx, new_dy, new_dxsize, new_dysize);
8450 // add current and new door 2 area if position or size has changed
8451 if (new_dx != VX || new_dy != VY ||
8452 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8454 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8455 VX, VY, VXSIZE, VYSIZE);
8456 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8457 new_vx, new_vy, new_vxsize, new_vysize);
8460 // ------------------------------------------------------------------------
8461 // handle changed tile size
8462 // ------------------------------------------------------------------------
8464 if (new_tilesize_var != TILESIZE_VAR)
8466 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8468 // changing tile size invalidates scroll values of engine snapshots
8469 FreeEngineSnapshotSingle();
8471 // changing tile size requires update of graphic mapping for EM engine
8472 init_em_graphics = TRUE;
8483 SXSIZE = new_sxsize;
8484 SYSIZE = new_sysize;
8485 DXSIZE = new_dxsize;
8486 DYSIZE = new_dysize;
8487 VXSIZE = new_vxsize;
8488 VYSIZE = new_vysize;
8489 EXSIZE = new_exsize;
8490 EYSIZE = new_eysize;
8491 REAL_SX = new_real_sx;
8492 REAL_SY = new_real_sy;
8493 FULL_SXSIZE = new_full_sxsize;
8494 FULL_SYSIZE = new_full_sysize;
8495 TILESIZE_VAR = new_tilesize_var;
8497 init_gfx_buffers = TRUE;
8498 init_gadgets_and_anims = TRUE;
8500 // printf("::: viewports: init_gfx_buffers\n");
8501 // printf("::: viewports: init_gadgets_and_anims\n");
8504 if (init_gfx_buffers)
8506 // printf("::: init_gfx_buffers\n");
8508 SCR_FIELDX = new_scr_fieldx_buffers;
8509 SCR_FIELDY = new_scr_fieldy_buffers;
8513 SCR_FIELDX = new_scr_fieldx;
8514 SCR_FIELDY = new_scr_fieldy;
8516 SetDrawDeactivationMask(REDRAW_NONE);
8517 SetDrawBackgroundMask(REDRAW_FIELD);
8520 if (init_video_buffer)
8522 // printf("::: init_video_buffer\n");
8524 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8525 InitImageTextures();
8528 if (init_gadgets_and_anims)
8530 // printf("::: init_gadgets_and_anims\n");
8533 InitGlobalAnimations();
8536 if (init_em_graphics)
8538 InitGraphicInfo_EM();