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_DOOR_1_GFX_PART_1,
74 IMG_DOOR_1_GFX_PART_2,
79 IMG_DOOR_1_GFX_PART_3,
84 IMG_DOOR_1_GFX_PART_4,
89 IMG_DOOR_1_GFX_PART_5,
94 IMG_DOOR_1_GFX_PART_6,
99 IMG_DOOR_1_GFX_PART_7,
104 IMG_DOOR_1_GFX_PART_8,
110 IMG_DOOR_2_GFX_PART_1,
115 IMG_DOOR_2_GFX_PART_2,
120 IMG_DOOR_2_GFX_PART_3,
125 IMG_DOOR_2_GFX_PART_4,
130 IMG_DOOR_2_GFX_PART_5,
135 IMG_DOOR_2_GFX_PART_6,
140 IMG_DOOR_2_GFX_PART_7,
145 IMG_DOOR_2_GFX_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 unsigned int sync_frame_delay = 0;
178 static unsigned int sync_frame_delay_value = GAME_FRAME_DELAY;
180 static char *print_if_not_empty(int element)
182 static char *s = NULL;
183 char *token_name = element_info[element].token_name;
188 s = checked_malloc(strlen(token_name) + 10 + 1);
190 if (element != EL_EMPTY)
191 sprintf(s, "%d\t['%s']", element, token_name);
193 sprintf(s, "%d", element);
198 void DumpTile(int x, int y)
203 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
209 printf_line("-", 79);
210 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
211 printf_line("-", 79);
213 if (!IN_LEV_FIELD(x, y))
215 printf("(not in level field)\n");
221 printf(" Feld: %d\t['%s']\n", Feld[x][y],
222 element_info[Feld[x][y]].token_name);
223 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
224 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
225 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
226 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
227 printf(" MovPos: %d\n", MovPos[x][y]);
228 printf(" MovDir: %d\n", MovDir[x][y]);
229 printf(" MovDelay: %d\n", MovDelay[x][y]);
230 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
231 printf(" CustomValue: %d\n", CustomValue[x][y]);
232 printf(" GfxElement: %d\n", GfxElement[x][y]);
233 printf(" GfxAction: %d\n", GfxAction[x][y]);
234 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
238 void SetDrawtoField(int mode)
240 if (mode == DRAW_FIELDBUFFER)
246 BX2 = SCR_FIELDX + 1;
247 BY2 = SCR_FIELDY + 1;
249 drawto_field = fieldbuffer;
251 else /* DRAW_BACKBUFFER */
257 BX2 = SCR_FIELDX - 1;
258 BY2 = SCR_FIELDY - 1;
260 drawto_field = backbuffer;
264 static void RedrawPlayfield_RND()
266 if (game.envelope_active)
269 DrawLevel(REDRAW_ALL);
273 void RedrawPlayfield()
275 if (game_status != GAME_MODE_PLAYING)
278 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
279 RedrawPlayfield_EM(TRUE);
280 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
281 RedrawPlayfield_SP(TRUE);
282 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
283 RedrawPlayfield_RND();
285 BlitScreenToBitmap(backbuffer);
287 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
291 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
294 Bitmap *src_bitmap = getGlobalBorderBitmapFromStatus(global.border_status);
295 Bitmap *dst_bitmap = gfx.masked_border_bitmap_ptr;
297 if (x == -1 && y == -1)
300 if (draw_target == DRAW_BORDER_TO_SCREEN)
301 BlitToScreenMasked(src_bitmap, x, y, width, height, x, y);
303 BlitBitmapMasked(src_bitmap, dst_bitmap, x, y, width, height, x, y);
306 static void DrawMaskedBorderExt_FIELD(int draw_target)
308 if (global.border_status >= GAME_MODE_TITLE &&
309 global.border_status <= GAME_MODE_PLAYING &&
310 border.draw_masked[global.border_status])
311 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
315 static void DrawMaskedBorderExt_DOOR_1(int draw_target)
317 // when drawing to backbuffer, never draw border over open doors
318 if (draw_target == DRAW_BORDER_TO_BACKBUFFER &&
319 (GetDoorState() & DOOR_OPEN_1))
322 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
323 (global.border_status != GAME_MODE_EDITOR ||
324 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
325 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, draw_target);
328 static void DrawMaskedBorderExt_DOOR_2(int draw_target)
330 // when drawing to backbuffer, never draw border over open doors
331 if (draw_target == DRAW_BORDER_TO_BACKBUFFER &&
332 (GetDoorState() & DOOR_OPEN_2))
335 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
336 global.border_status != GAME_MODE_EDITOR)
337 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, draw_target);
340 static void DrawMaskedBorderExt_DOOR_3(int draw_target)
342 /* currently not available */
345 static void DrawMaskedBorderExt_ALL(int draw_target)
347 DrawMaskedBorderExt_FIELD(draw_target);
348 DrawMaskedBorderExt_DOOR_1(draw_target);
349 DrawMaskedBorderExt_DOOR_2(draw_target);
350 DrawMaskedBorderExt_DOOR_3(draw_target);
353 static void DrawMaskedBorderExt(int redraw_mask, int draw_target)
355 /* never draw masked screen borders on borderless screens */
356 if (game_status == GAME_MODE_LOADING ||
357 game_status == GAME_MODE_TITLE)
360 if (redraw_mask & REDRAW_ALL)
361 DrawMaskedBorderExt_ALL(draw_target);
364 if (redraw_mask & REDRAW_FIELD)
365 DrawMaskedBorderExt_FIELD(draw_target);
366 if (redraw_mask & REDRAW_DOOR_1)
367 DrawMaskedBorderExt_DOOR_1(draw_target);
368 if (redraw_mask & REDRAW_DOOR_2)
369 DrawMaskedBorderExt_DOOR_2(draw_target);
370 if (redraw_mask & REDRAW_DOOR_3)
371 DrawMaskedBorderExt_DOOR_3(draw_target);
375 void DrawMaskedBorder_FIELD()
377 DrawMaskedBorderExt_FIELD(DRAW_BORDER_TO_BACKBUFFER);
380 void DrawMaskedBorder(int redraw_mask)
382 DrawMaskedBorderExt(redraw_mask, DRAW_BORDER_TO_BACKBUFFER);
385 void DrawMaskedBorderToTarget(int draw_target)
387 DrawMaskedBorderExt(REDRAW_ALL, draw_target);
390 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
392 int fx = FX, fy = FY;
393 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
394 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
396 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
397 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
398 int dx_var = dx * TILESIZE_VAR / TILESIZE;
399 int dy_var = dy * TILESIZE_VAR / TILESIZE;
402 ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
403 ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
405 if (EVEN(SCR_FIELDX))
407 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
408 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
410 fx += (dx_var > 0 ? TILEX_VAR : 0);
417 if (EVEN(SCR_FIELDY))
419 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
420 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
422 fy += (dy_var > 0 ? TILEY_VAR : 0);
429 if (full_lev_fieldx <= SCR_FIELDX)
431 if (EVEN(SCR_FIELDX))
432 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
434 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
437 if (full_lev_fieldy <= SCR_FIELDY)
439 if (EVEN(SCR_FIELDY))
440 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
442 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
445 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
448 void BlitScreenToBitmap(Bitmap *target_bitmap)
450 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
451 BlitScreenToBitmap_EM(target_bitmap);
452 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
453 BlitScreenToBitmap_SP(target_bitmap);
454 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
455 BlitScreenToBitmap_RND(target_bitmap);
457 redraw_mask |= REDRAW_FIELD;
460 void DrawFramesPerSecond()
463 int font_nr = FONT_TEXT_2;
464 int font_width = getFontWidth(font_nr);
466 sprintf(text, "%04.1f fps", global.frames_per_second);
468 DrawTextExt(backbuffer, WIN_XSIZE - font_width * strlen(text), 0, text,
469 font_nr, BLIT_OPAQUE);
473 static void PrintFrameTimeDebugging()
475 static unsigned int last_counter = 0;
476 unsigned int counter = Counter();
477 int diff_1 = counter - last_counter;
478 int diff_2 = diff_1 - GAME_FRAME_DELAY;
480 int diff_2_cut = MIN(ABS(diff_2), diff_2_max);
481 char diff_bar[2 * diff_2_max + 5];
485 diff_bar[pos++] = (diff_2 < -diff_2_max ? '<' : ' ');
487 for (i = 0; i < diff_2_max; i++)
488 diff_bar[pos++] = (diff_2 >= 0 ? ' ' :
489 i >= diff_2_max - diff_2_cut ? '-' : ' ');
491 diff_bar[pos++] = '|';
493 for (i = 0; i < diff_2_max; i++)
494 diff_bar[pos++] = (diff_2 <= 0 ? ' ' : i < diff_2_cut ? '+' : ' ');
496 diff_bar[pos++] = (diff_2 > diff_2_max ? '>' : ' ');
498 diff_bar[pos++] = '\0';
500 Error(ERR_INFO, "%06d [%02d] [%c%02d] %s",
503 (diff_2 < 0 ? '-' : diff_2 > 0 ? '+' : ' '), ABS(diff_2),
506 last_counter = counter;
512 static int last_redraw_mask = REDRAW_NONE;
514 // force screen redraw in every frame to continue drawing global animations
515 // (but always use the last redraw mask to prevent unwanted side effects)
516 if (redraw_mask == REDRAW_NONE)
517 redraw_mask = last_redraw_mask;
519 last_redraw_mask = redraw_mask;
522 // masked border now drawn immediately when blitting backbuffer to window
524 // draw masked border to all viewports, if defined
525 DrawMaskedBorder(redraw_mask);
528 // draw frames per second (only if debug mode is enabled)
529 if (redraw_mask & REDRAW_FPS)
530 DrawFramesPerSecond();
532 // redraw complete window if both playfield and (some) doors need redraw
533 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
534 redraw_mask = REDRAW_ALL;
536 /* although redrawing the whole window would be fine for normal gameplay,
537 being able to only redraw the playfield is required for deactivating
538 certain drawing areas (mainly playfield) to work, which is needed for
539 warp-forward to be fast enough (by skipping redraw of most frames) */
541 if (redraw_mask & REDRAW_ALL)
543 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
545 else if (redraw_mask & REDRAW_FIELD)
547 BlitBitmap(backbuffer, window,
548 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
550 else if (redraw_mask & REDRAW_DOORS)
552 if (redraw_mask & REDRAW_DOOR_1)
553 BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
555 if (redraw_mask & REDRAW_DOOR_2)
556 BlitBitmap(backbuffer, window, VX, VY, VXSIZE, VYSIZE, VX, VY);
558 if (redraw_mask & REDRAW_DOOR_3)
559 BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
562 redraw_mask = REDRAW_NONE;
565 PrintFrameTimeDebugging();
569 static void FadeCrossSaveBackbuffer()
571 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
574 static void FadeCrossRestoreBackbuffer()
576 int redraw_mask_last = redraw_mask;
578 BlitBitmap(bitmap_db_cross, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
580 // do not change redraw mask when restoring backbuffer after cross-fading
581 redraw_mask = redraw_mask_last;
584 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
586 static int fade_type_skip = FADE_TYPE_NONE;
587 void (*draw_border_function)(void) = NULL;
588 Bitmap *bitmap = (fade_mode & FADE_TYPE_TRANSFORM ? bitmap_db_cross : NULL);
589 int x, y, width, height;
590 int fade_delay, post_delay;
592 if (fade_type == FADE_TYPE_FADE_OUT)
594 if (fade_type_skip != FADE_TYPE_NONE)
596 /* skip all fade operations until specified fade operation */
597 if (fade_type & fade_type_skip)
598 fade_type_skip = FADE_TYPE_NONE;
604 FadeCrossSaveBackbuffer();
607 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
610 FadeCrossSaveBackbuffer();
617 redraw_mask |= fade_mask;
619 if (fade_type == FADE_TYPE_SKIP)
621 fade_type_skip = fade_mode;
626 fade_delay = fading.fade_delay;
627 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
629 if (fade_type_skip != FADE_TYPE_NONE)
631 /* skip all fade operations until specified fade operation */
632 if (fade_type & fade_type_skip)
633 fade_type_skip = FADE_TYPE_NONE;
638 if (global.autoplay_leveldir)
643 if (fade_mask == REDRAW_FIELD)
648 height = FADE_SYSIZE;
650 if (border.draw_masked_when_fading)
651 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
653 DrawMaskedBorder_FIELD(); /* draw once */
655 else /* REDRAW_ALL */
663 if (!setup.fade_screens ||
665 fading.fade_mode == FADE_MODE_NONE)
667 if (fade_mode == FADE_MODE_FADE_OUT)
670 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
672 redraw_mask &= ~fade_mask;
677 FadeRectangle(bitmap, x, y, width, height, fade_mode, fade_delay, post_delay,
678 draw_border_function);
680 if (fade_type == FADE_TYPE_FADE_OUT)
681 FadeCrossRestoreBackbuffer();
683 redraw_mask &= ~fade_mask;
686 static void SetScreenStates_BeforeFadingIn()
690 static void SetScreenStates_AfterFadingIn()
692 global.anim_status = global.anim_status_next;
694 // force update of global animation status in case of rapid screen changes
695 redraw_mask = REDRAW_ALL;
699 static void SetScreenStates_BeforeFadingOut()
701 global.anim_status = GAME_MODE_PSEUDO_FADING;
704 static void SetScreenStates_AfterFadingOut()
706 global.border_status = game_status;
709 void FadeIn(int fade_mask)
711 SetScreenStates_BeforeFadingIn();
714 DrawMaskedBorder(REDRAW_ALL);
717 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
718 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
720 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
724 FADE_SXSIZE = FULL_SXSIZE;
725 FADE_SYSIZE = FULL_SYSIZE;
727 SetScreenStates_AfterFadingIn();
730 void FadeOut(int fade_mask)
732 SetScreenStates_BeforeFadingOut();
735 DrawMaskedBorder(REDRAW_ALL);
738 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
739 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
741 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
743 SetScreenStates_AfterFadingOut();
746 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
748 static struct TitleFadingInfo fading_leave_stored;
751 fading_leave_stored = fading_leave;
753 fading = fading_leave_stored;
756 void FadeSetEnterMenu()
758 fading = menu.enter_menu;
760 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
763 void FadeSetLeaveMenu()
765 fading = menu.leave_menu;
767 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
770 void FadeSetEnterScreen()
772 fading = menu.enter_screen[game_status];
774 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
777 void FadeSetNextScreen()
779 fading = menu.next_screen[game_status];
781 // (do not overwrite fade mode set by FadeSetEnterScreen)
782 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
785 void FadeSetLeaveScreen()
787 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
790 void FadeSetFromType(int type)
792 if (type & TYPE_ENTER_SCREEN)
793 FadeSetEnterScreen();
794 else if (type & TYPE_ENTER)
796 else if (type & TYPE_LEAVE)
800 void FadeSetDisabled()
802 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
804 fading = fading_none;
807 void FadeSkipNextFadeIn()
809 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
812 void FadeSkipNextFadeOut()
814 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
817 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
819 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
821 return (graphic == IMG_UNDEFINED ? NULL :
822 graphic_info[graphic].bitmap != NULL || redefined ?
823 graphic_info[graphic].bitmap :
824 graphic_info[default_graphic].bitmap);
827 Bitmap *getBackgroundBitmap(int graphic)
829 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
832 Bitmap *getGlobalBorderBitmap(int graphic)
834 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
837 Bitmap *getGlobalBorderBitmapFromStatus(int status)
840 (status == GAME_MODE_MAIN ||
841 status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
842 status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
843 status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
844 status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
847 return getGlobalBorderBitmap(graphic);
850 void SetWindowBackgroundImageIfDefined(int graphic)
852 if (graphic_info[graphic].bitmap)
853 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
856 void SetMainBackgroundImageIfDefined(int graphic)
858 if (graphic_info[graphic].bitmap)
859 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
862 void SetDoorBackgroundImageIfDefined(int graphic)
864 if (graphic_info[graphic].bitmap)
865 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
868 void SetWindowBackgroundImage(int graphic)
870 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
873 void SetMainBackgroundImage(int graphic)
875 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
878 void SetDoorBackgroundImage(int graphic)
880 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
883 void SetPanelBackground()
885 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
887 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
888 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
890 SetDoorBackgroundBitmap(bitmap_db_panel);
893 void DrawBackground(int x, int y, int width, int height)
895 /* "drawto" might still point to playfield buffer here (hall of fame) */
896 ClearRectangleOnBackground(backbuffer, x, y, width, height);
898 if (IN_GFX_FIELD_FULL(x, y))
899 redraw_mask |= REDRAW_FIELD;
900 else if (IN_GFX_DOOR_1(x, y))
901 redraw_mask |= REDRAW_DOOR_1;
902 else if (IN_GFX_DOOR_2(x, y))
903 redraw_mask |= REDRAW_DOOR_2;
904 else if (IN_GFX_DOOR_3(x, y))
905 redraw_mask |= REDRAW_DOOR_3;
908 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
910 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
912 if (font->bitmap == NULL)
915 DrawBackground(x, y, width, height);
918 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
920 struct GraphicInfo *g = &graphic_info[graphic];
922 if (g->bitmap == NULL)
925 DrawBackground(x, y, width, height);
928 static int game_status_last = -1;
929 static Bitmap *global_border_bitmap_last = NULL;
930 static Bitmap *global_border_bitmap = NULL;
931 static int real_sx_last = -1, real_sy_last = -1;
932 static int full_sxsize_last = -1, full_sysize_last = -1;
933 static int dx_last = -1, dy_last = -1;
934 static int dxsize_last = -1, dysize_last = -1;
935 static int vx_last = -1, vy_last = -1;
936 static int vxsize_last = -1, vysize_last = -1;
938 boolean CheckIfGlobalBorderHasChanged()
940 // if game status has not changed, global border has not changed either
941 if (game_status == game_status_last)
944 // determine and store new global border bitmap for current game status
945 global_border_bitmap = getGlobalBorderBitmapFromStatus(game_status);
947 return (global_border_bitmap_last != global_border_bitmap);
950 boolean CheckIfGlobalBorderRedrawIsNeeded()
952 // if game status has not changed, nothing has to be redrawn
953 if (game_status == game_status_last)
956 // redraw if last screen was title screen
957 if (game_status_last == GAME_MODE_TITLE)
960 // redraw if global screen border has changed
961 if (CheckIfGlobalBorderHasChanged())
964 // redraw if position or size of playfield area has changed
965 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
966 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
969 // redraw if position or size of door area has changed
970 if (dx_last != DX || dy_last != DY ||
971 dxsize_last != DXSIZE || dysize_last != DYSIZE)
974 // redraw if position or size of tape area has changed
975 if (vx_last != VX || vy_last != VY ||
976 vxsize_last != VXSIZE || vysize_last != VYSIZE)
982 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
985 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
987 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
990 void RedrawGlobalBorder()
992 Bitmap *bitmap = getGlobalBorderBitmapFromStatus(game_status);
994 RedrawGlobalBorderFromBitmap(bitmap);
996 redraw_mask = REDRAW_ALL;
999 static void RedrawGlobalBorderIfNeeded()
1001 if (game_status == game_status_last)
1004 // copy current draw buffer to later copy back areas that have not changed
1005 if (game_status_last != GAME_MODE_TITLE)
1006 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
1008 if (CheckIfGlobalBorderRedrawIsNeeded())
1010 // redraw global screen border (or clear, if defined to be empty)
1011 RedrawGlobalBorderFromBitmap(global_border_bitmap);
1013 // copy previous playfield and door areas, if they are defined on both
1014 // previous and current screen and if they still have the same size
1016 if (real_sx_last != -1 && real_sy_last != -1 &&
1017 REAL_SX != -1 && REAL_SY != -1 &&
1018 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
1019 BlitBitmap(bitmap_db_store, backbuffer,
1020 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
1023 if (dx_last != -1 && dy_last != -1 &&
1024 DX != -1 && DY != -1 &&
1025 dxsize_last == DXSIZE && dysize_last == DYSIZE)
1026 BlitBitmap(bitmap_db_store, backbuffer,
1027 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
1029 if (vx_last != -1 && vy_last != -1 &&
1030 VX != -1 && VY != -1 &&
1031 vxsize_last == VXSIZE && vysize_last == VYSIZE)
1032 BlitBitmap(bitmap_db_store, backbuffer,
1033 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
1035 redraw_mask = REDRAW_ALL;
1038 game_status_last = game_status;
1040 global_border_bitmap_last = global_border_bitmap;
1042 real_sx_last = REAL_SX;
1043 real_sy_last = REAL_SY;
1044 full_sxsize_last = FULL_SXSIZE;
1045 full_sysize_last = FULL_SYSIZE;
1048 dxsize_last = DXSIZE;
1049 dysize_last = DYSIZE;
1052 vxsize_last = VXSIZE;
1053 vysize_last = VYSIZE;
1058 RedrawGlobalBorderIfNeeded();
1060 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
1061 /* (when entering hall of fame after playing) */
1062 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
1064 /* !!! maybe this should be done before clearing the background !!! */
1065 if (game_status == GAME_MODE_PLAYING)
1067 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
1068 SetDrawtoField(DRAW_FIELDBUFFER);
1072 SetDrawtoField(DRAW_BACKBUFFER);
1076 void MarkTileDirty(int x, int y)
1078 redraw_mask |= REDRAW_FIELD;
1081 void SetBorderElement()
1085 BorderElement = EL_EMPTY;
1087 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1089 for (x = 0; x < lev_fieldx; x++)
1091 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1092 BorderElement = EL_STEELWALL;
1094 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1100 void FloodFillLevel(int from_x, int from_y, int fill_element,
1101 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1102 int max_fieldx, int max_fieldy)
1106 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1107 static int safety = 0;
1109 /* check if starting field still has the desired content */
1110 if (field[from_x][from_y] == fill_element)
1115 if (safety > max_fieldx * max_fieldy)
1116 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1118 old_element = field[from_x][from_y];
1119 field[from_x][from_y] = fill_element;
1121 for (i = 0; i < 4; i++)
1123 x = from_x + check[i][0];
1124 y = from_y + check[i][1];
1126 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1127 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1133 void SetRandomAnimationValue(int x, int y)
1135 gfx.anim_random_frame = GfxRandom[x][y];
1138 int getGraphicAnimationFrame(int graphic, int sync_frame)
1140 /* animation synchronized with global frame counter, not move position */
1141 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1142 sync_frame = FrameCounter;
1144 return getAnimationFrame(graphic_info[graphic].anim_frames,
1145 graphic_info[graphic].anim_delay,
1146 graphic_info[graphic].anim_mode,
1147 graphic_info[graphic].anim_start_frame,
1151 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1152 Bitmap **bitmap, int *x, int *y,
1153 boolean get_backside)
1155 struct GraphicInfo *g = &graphic_info[graphic];
1156 Bitmap *src_bitmap = g->bitmap;
1157 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1158 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1159 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1161 // if no in-game graphics defined, always use standard graphic size
1162 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1163 tilesize = TILESIZE;
1165 if (tilesize == gfx.standard_tile_size)
1166 src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1167 else if (tilesize == game.tile_size)
1168 src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
1170 src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1172 if (g->offset_y == 0) /* frames are ordered horizontally */
1174 int max_width = g->anim_frames_per_line * g->width;
1175 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1177 src_x = pos % max_width;
1178 src_y = src_y % g->height + pos / max_width * g->height;
1180 else if (g->offset_x == 0) /* frames are ordered vertically */
1182 int max_height = g->anim_frames_per_line * g->height;
1183 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1185 src_x = src_x % g->width + pos / max_height * g->width;
1186 src_y = pos % max_height;
1188 else /* frames are ordered diagonally */
1190 src_x = src_x + frame * g->offset_x;
1191 src_y = src_y + frame * g->offset_y;
1194 *bitmap = src_bitmap;
1195 *x = src_x * tilesize / g->tile_size;
1196 *y = src_y * tilesize / g->tile_size;
1199 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1200 int *x, int *y, boolean get_backside)
1202 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1206 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1207 Bitmap **bitmap, int *x, int *y)
1209 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1212 void getFixedGraphicSource(int graphic, int frame,
1213 Bitmap **bitmap, int *x, int *y)
1215 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1218 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1220 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1223 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1224 int *x, int *y, boolean get_backside)
1226 struct GraphicInfo *g = &graphic_info[graphic];
1227 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1228 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1230 if (TILESIZE_VAR != TILESIZE)
1231 return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1234 *bitmap = g->bitmap;
1236 if (g->offset_y == 0) /* frames are ordered horizontally */
1238 int max_width = g->anim_frames_per_line * g->width;
1239 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1241 *x = pos % max_width;
1242 *y = src_y % g->height + pos / max_width * g->height;
1244 else if (g->offset_x == 0) /* frames are ordered vertically */
1246 int max_height = g->anim_frames_per_line * g->height;
1247 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1249 *x = src_x % g->width + pos / max_height * g->width;
1250 *y = pos % max_height;
1252 else /* frames are ordered diagonally */
1254 *x = src_x + frame * g->offset_x;
1255 *y = src_y + frame * g->offset_y;
1258 *x = *x * TILESIZE_VAR / g->tile_size;
1259 *y = *y * TILESIZE_VAR / g->tile_size;
1262 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1264 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1267 void DrawGraphic(int x, int y, int graphic, int frame)
1270 if (!IN_SCR_FIELD(x, y))
1272 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1273 printf("DrawGraphic(): This should never happen!\n");
1278 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1281 MarkTileDirty(x, y);
1284 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1287 if (!IN_SCR_FIELD(x, y))
1289 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1290 printf("DrawGraphic(): This should never happen!\n");
1295 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1297 MarkTileDirty(x, y);
1300 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1306 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1308 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1311 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1317 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1318 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1321 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1324 if (!IN_SCR_FIELD(x, y))
1326 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1327 printf("DrawGraphicThruMask(): This should never happen!\n");
1332 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1335 MarkTileDirty(x, y);
1338 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1341 if (!IN_SCR_FIELD(x, y))
1343 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1344 printf("DrawGraphicThruMask(): This should never happen!\n");
1349 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1351 MarkTileDirty(x, y);
1354 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1360 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1362 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1366 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1367 int graphic, int frame)
1372 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1374 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1378 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1380 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1382 MarkTileDirty(x / tilesize, y / tilesize);
1385 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1391 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1392 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1395 void DrawMiniGraphic(int x, int y, int graphic)
1397 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1398 MarkTileDirty(x / 2, y / 2);
1401 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1406 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1407 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1410 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1411 int graphic, int frame,
1412 int cut_mode, int mask_mode)
1417 int width = TILEX, height = TILEY;
1420 if (dx || dy) /* shifted graphic */
1422 if (x < BX1) /* object enters playfield from the left */
1429 else if (x > BX2) /* object enters playfield from the right */
1435 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1441 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1443 else if (dx) /* general horizontal movement */
1444 MarkTileDirty(x + SIGN(dx), y);
1446 if (y < BY1) /* object enters playfield from the top */
1448 if (cut_mode == CUT_BELOW) /* object completely above top border */
1456 else if (y > BY2) /* object enters playfield from the bottom */
1462 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1468 else if (dy > 0 && cut_mode == CUT_ABOVE)
1470 if (y == BY2) /* object completely above bottom border */
1476 MarkTileDirty(x, y + 1);
1477 } /* object leaves playfield to the bottom */
1478 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1480 else if (dy) /* general vertical movement */
1481 MarkTileDirty(x, y + SIGN(dy));
1485 if (!IN_SCR_FIELD(x, y))
1487 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1488 printf("DrawGraphicShifted(): This should never happen!\n");
1493 width = width * TILESIZE_VAR / TILESIZE;
1494 height = height * TILESIZE_VAR / TILESIZE;
1495 cx = cx * TILESIZE_VAR / TILESIZE;
1496 cy = cy * TILESIZE_VAR / TILESIZE;
1497 dx = dx * TILESIZE_VAR / TILESIZE;
1498 dy = dy * TILESIZE_VAR / TILESIZE;
1500 if (width > 0 && height > 0)
1502 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1507 dst_x = FX + x * TILEX_VAR + dx;
1508 dst_y = FY + y * TILEY_VAR + dy;
1510 if (mask_mode == USE_MASKING)
1511 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1514 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1517 MarkTileDirty(x, y);
1521 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1522 int graphic, int frame,
1523 int cut_mode, int mask_mode)
1528 int width = TILEX_VAR, height = TILEY_VAR;
1531 int x2 = x + SIGN(dx);
1532 int y2 = y + SIGN(dy);
1534 /* movement with two-tile animations must be sync'ed with movement position,
1535 not with current GfxFrame (which can be higher when using slow movement) */
1536 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1537 int anim_frames = graphic_info[graphic].anim_frames;
1539 /* (we also need anim_delay here for movement animations with less frames) */
1540 int anim_delay = graphic_info[graphic].anim_delay;
1541 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1543 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1544 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1546 /* re-calculate animation frame for two-tile movement animation */
1547 frame = getGraphicAnimationFrame(graphic, sync_frame);
1549 /* check if movement start graphic inside screen area and should be drawn */
1550 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1552 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1554 dst_x = FX + x1 * TILEX_VAR;
1555 dst_y = FY + y1 * TILEY_VAR;
1557 if (mask_mode == USE_MASKING)
1558 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1561 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1564 MarkTileDirty(x1, y1);
1567 /* check if movement end graphic inside screen area and should be drawn */
1568 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1570 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1572 dst_x = FX + x2 * TILEX_VAR;
1573 dst_y = FY + y2 * TILEY_VAR;
1575 if (mask_mode == USE_MASKING)
1576 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1579 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1582 MarkTileDirty(x2, y2);
1586 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1587 int graphic, int frame,
1588 int cut_mode, int mask_mode)
1592 DrawGraphic(x, y, graphic, frame);
1597 if (graphic_info[graphic].double_movement) /* EM style movement images */
1598 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1600 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1603 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1604 int frame, int cut_mode)
1606 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1609 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1610 int cut_mode, int mask_mode)
1612 int lx = LEVELX(x), ly = LEVELY(y);
1616 if (IN_LEV_FIELD(lx, ly))
1618 SetRandomAnimationValue(lx, ly);
1620 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1621 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1623 /* do not use double (EM style) movement graphic when not moving */
1624 if (graphic_info[graphic].double_movement && !dx && !dy)
1626 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1627 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1630 else /* border element */
1632 graphic = el2img(element);
1633 frame = getGraphicAnimationFrame(graphic, -1);
1636 if (element == EL_EXPANDABLE_WALL)
1638 boolean left_stopped = FALSE, right_stopped = FALSE;
1640 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1641 left_stopped = TRUE;
1642 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1643 right_stopped = TRUE;
1645 if (left_stopped && right_stopped)
1647 else if (left_stopped)
1649 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1650 frame = graphic_info[graphic].anim_frames - 1;
1652 else if (right_stopped)
1654 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1655 frame = graphic_info[graphic].anim_frames - 1;
1660 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1661 else if (mask_mode == USE_MASKING)
1662 DrawGraphicThruMask(x, y, graphic, frame);
1664 DrawGraphic(x, y, graphic, frame);
1667 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1668 int cut_mode, int mask_mode)
1670 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1671 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1672 cut_mode, mask_mode);
1675 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1678 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1681 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1684 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1687 void DrawLevelElementThruMask(int x, int y, int element)
1689 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1692 void DrawLevelFieldThruMask(int x, int y)
1694 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1697 /* !!! implementation of quicksand is totally broken !!! */
1698 #define IS_CRUMBLED_TILE(x, y, e) \
1699 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1700 !IS_MOVING(x, y) || \
1701 (e) == EL_QUICKSAND_EMPTYING || \
1702 (e) == EL_QUICKSAND_FAST_EMPTYING))
1704 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1709 int width, height, cx, cy;
1710 int sx = SCREENX(x), sy = SCREENY(y);
1711 int crumbled_border_size = graphic_info[graphic].border_size;
1714 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1716 for (i = 1; i < 4; i++)
1718 int dxx = (i & 1 ? dx : 0);
1719 int dyy = (i & 2 ? dy : 0);
1722 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1725 /* check if neighbour field is of same crumble type */
1726 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1727 graphic_info[graphic].class ==
1728 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1730 /* return if check prevents inner corner */
1731 if (same == (dxx == dx && dyy == dy))
1735 /* if we reach this point, we have an inner corner */
1737 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1739 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1740 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1741 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1742 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1744 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1745 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1748 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1753 int width, height, bx, by, cx, cy;
1754 int sx = SCREENX(x), sy = SCREENY(y);
1755 int crumbled_border_size = graphic_info[graphic].border_size;
1756 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1757 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1760 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1762 /* draw simple, sloppy, non-corner-accurate crumbled border */
1764 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1765 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1766 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1767 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1769 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1770 FX + sx * TILEX_VAR + cx,
1771 FY + sy * TILEY_VAR + cy);
1773 /* (remaining middle border part must be at least as big as corner part) */
1774 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1775 crumbled_border_size >= TILESIZE / 3)
1778 /* correct corners of crumbled border, if needed */
1780 for (i = -1; i <= 1; i += 2)
1782 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1783 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1784 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1787 /* check if neighbour field is of same crumble type */
1788 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1789 graphic_info[graphic].class ==
1790 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1792 /* no crumbled corner, but continued crumbled border */
1794 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1795 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1796 int b1 = (i == 1 ? crumbled_border_size_var :
1797 TILESIZE_VAR - 2 * crumbled_border_size_var);
1799 width = crumbled_border_size_var;
1800 height = crumbled_border_size_var;
1802 if (dir == 1 || dir == 2)
1817 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1819 FX + sx * TILEX_VAR + cx,
1820 FY + sy * TILEY_VAR + cy);
1825 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1827 int sx = SCREENX(x), sy = SCREENY(y);
1830 static int xy[4][2] =
1838 if (!IN_LEV_FIELD(x, y))
1841 element = TILE_GFX_ELEMENT(x, y);
1843 /* crumble field itself */
1844 if (IS_CRUMBLED_TILE(x, y, element))
1846 if (!IN_SCR_FIELD(sx, sy))
1849 for (i = 0; i < 4; i++)
1851 int xx = x + xy[i][0];
1852 int yy = y + xy[i][1];
1854 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1857 /* check if neighbour field is of same crumble type */
1858 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1859 graphic_info[graphic].class ==
1860 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1863 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1866 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1867 graphic_info[graphic].anim_frames == 2)
1869 for (i = 0; i < 4; i++)
1871 int dx = (i & 1 ? +1 : -1);
1872 int dy = (i & 2 ? +1 : -1);
1874 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1878 MarkTileDirty(sx, sy);
1880 else /* center field not crumbled -- crumble neighbour fields */
1882 for (i = 0; i < 4; i++)
1884 int xx = x + xy[i][0];
1885 int yy = y + xy[i][1];
1886 int sxx = sx + xy[i][0];
1887 int syy = sy + xy[i][1];
1889 if (!IN_LEV_FIELD(xx, yy) ||
1890 !IN_SCR_FIELD(sxx, syy))
1893 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1896 element = TILE_GFX_ELEMENT(xx, yy);
1898 if (!IS_CRUMBLED_TILE(xx, yy, element))
1901 graphic = el_act2crm(element, ACTION_DEFAULT);
1903 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1905 MarkTileDirty(sxx, syy);
1910 void DrawLevelFieldCrumbled(int x, int y)
1914 if (!IN_LEV_FIELD(x, y))
1917 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1918 GfxElement[x][y] != EL_UNDEFINED &&
1919 GFX_CRUMBLED(GfxElement[x][y]))
1921 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1926 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1928 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1931 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1934 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1935 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1936 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1937 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1938 int sx = SCREENX(x), sy = SCREENY(y);
1940 DrawGraphic(sx, sy, graphic1, frame1);
1941 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1944 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1946 int sx = SCREENX(x), sy = SCREENY(y);
1947 static int xy[4][2] =
1956 for (i = 0; i < 4; i++)
1958 int xx = x + xy[i][0];
1959 int yy = y + xy[i][1];
1960 int sxx = sx + xy[i][0];
1961 int syy = sy + xy[i][1];
1963 if (!IN_LEV_FIELD(xx, yy) ||
1964 !IN_SCR_FIELD(sxx, syy) ||
1965 !GFX_CRUMBLED(Feld[xx][yy]) ||
1969 DrawLevelField(xx, yy);
1973 static int getBorderElement(int x, int y)
1977 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1978 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
1979 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
1980 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1981 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
1982 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
1983 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
1985 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1986 int steel_position = (x == -1 && y == -1 ? 0 :
1987 x == lev_fieldx && y == -1 ? 1 :
1988 x == -1 && y == lev_fieldy ? 2 :
1989 x == lev_fieldx && y == lev_fieldy ? 3 :
1990 x == -1 || x == lev_fieldx ? 4 :
1991 y == -1 || y == lev_fieldy ? 5 : 6);
1993 return border[steel_position][steel_type];
1996 void DrawScreenElement(int x, int y, int element)
1998 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1999 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
2002 void DrawLevelElement(int x, int y, int element)
2004 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2005 DrawScreenElement(SCREENX(x), SCREENY(y), element);
2008 void DrawScreenField(int x, int y)
2010 int lx = LEVELX(x), ly = LEVELY(y);
2011 int element, content;
2013 if (!IN_LEV_FIELD(lx, ly))
2015 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
2018 element = getBorderElement(lx, ly);
2020 DrawScreenElement(x, y, element);
2025 element = Feld[lx][ly];
2026 content = Store[lx][ly];
2028 if (IS_MOVING(lx, ly))
2030 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
2031 boolean cut_mode = NO_CUTTING;
2033 if (element == EL_QUICKSAND_EMPTYING ||
2034 element == EL_QUICKSAND_FAST_EMPTYING ||
2035 element == EL_MAGIC_WALL_EMPTYING ||
2036 element == EL_BD_MAGIC_WALL_EMPTYING ||
2037 element == EL_DC_MAGIC_WALL_EMPTYING ||
2038 element == EL_AMOEBA_DROPPING)
2039 cut_mode = CUT_ABOVE;
2040 else if (element == EL_QUICKSAND_FILLING ||
2041 element == EL_QUICKSAND_FAST_FILLING ||
2042 element == EL_MAGIC_WALL_FILLING ||
2043 element == EL_BD_MAGIC_WALL_FILLING ||
2044 element == EL_DC_MAGIC_WALL_FILLING)
2045 cut_mode = CUT_BELOW;
2047 if (cut_mode == CUT_ABOVE)
2048 DrawScreenElement(x, y, element);
2050 DrawScreenElement(x, y, EL_EMPTY);
2053 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
2054 else if (cut_mode == NO_CUTTING)
2055 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
2058 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
2060 if (cut_mode == CUT_BELOW &&
2061 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
2062 DrawLevelElement(lx, ly + 1, element);
2065 if (content == EL_ACID)
2067 int dir = MovDir[lx][ly];
2068 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
2069 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
2071 DrawLevelElementThruMask(newlx, newly, EL_ACID);
2074 else if (IS_BLOCKED(lx, ly))
2079 boolean cut_mode = NO_CUTTING;
2080 int element_old, content_old;
2082 Blocked2Moving(lx, ly, &oldx, &oldy);
2085 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2086 MovDir[oldx][oldy] == MV_RIGHT);
2088 element_old = Feld[oldx][oldy];
2089 content_old = Store[oldx][oldy];
2091 if (element_old == EL_QUICKSAND_EMPTYING ||
2092 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2093 element_old == EL_MAGIC_WALL_EMPTYING ||
2094 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2095 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2096 element_old == EL_AMOEBA_DROPPING)
2097 cut_mode = CUT_ABOVE;
2099 DrawScreenElement(x, y, EL_EMPTY);
2102 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2104 else if (cut_mode == NO_CUTTING)
2105 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2108 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2111 else if (IS_DRAWABLE(element))
2112 DrawScreenElement(x, y, element);
2114 DrawScreenElement(x, y, EL_EMPTY);
2117 void DrawLevelField(int x, int y)
2119 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2120 DrawScreenField(SCREENX(x), SCREENY(y));
2121 else if (IS_MOVING(x, y))
2125 Moving2Blocked(x, y, &newx, &newy);
2126 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2127 DrawScreenField(SCREENX(newx), SCREENY(newy));
2129 else if (IS_BLOCKED(x, y))
2133 Blocked2Moving(x, y, &oldx, &oldy);
2134 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2135 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2139 void DrawSizedElement(int x, int y, int element, int tilesize)
2143 graphic = el2edimg(element);
2144 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2147 void DrawMiniElement(int x, int y, int element)
2151 graphic = el2edimg(element);
2152 DrawMiniGraphic(x, y, graphic);
2155 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2158 int x = sx + scroll_x, y = sy + scroll_y;
2160 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2161 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2162 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2163 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2165 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2168 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2170 int x = sx + scroll_x, y = sy + scroll_y;
2172 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2173 DrawMiniElement(sx, sy, EL_EMPTY);
2174 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2175 DrawMiniElement(sx, sy, Feld[x][y]);
2177 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2180 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2181 int x, int y, int xsize, int ysize,
2182 int tile_width, int tile_height)
2186 int dst_x = startx + x * tile_width;
2187 int dst_y = starty + y * tile_height;
2188 int width = graphic_info[graphic].width;
2189 int height = graphic_info[graphic].height;
2190 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2191 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2192 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2193 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2194 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2195 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2196 boolean draw_masked = graphic_info[graphic].draw_masked;
2198 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2200 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2202 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2206 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2207 inner_sx + (x - 1) * tile_width % inner_width);
2208 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2209 inner_sy + (y - 1) * tile_height % inner_height);
2212 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2215 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2219 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2220 int x, int y, int xsize, int ysize, int font_nr)
2222 int font_width = getFontWidth(font_nr);
2223 int font_height = getFontHeight(font_nr);
2225 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2226 font_width, font_height);
2229 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2231 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2232 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2233 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2234 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2235 boolean no_delay = (tape.warp_forward);
2236 unsigned int anim_delay = 0;
2237 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2238 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2239 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2240 int font_width = getFontWidth(font_nr);
2241 int font_height = getFontHeight(font_nr);
2242 int max_xsize = level.envelope[envelope_nr].xsize;
2243 int max_ysize = level.envelope[envelope_nr].ysize;
2244 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2245 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2246 int xend = max_xsize;
2247 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2248 int xstep = (xstart < xend ? 1 : 0);
2249 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2251 int end = MAX(xend - xstart, yend - ystart);
2254 for (i = start; i <= end; i++)
2256 int last_frame = end; // last frame of this "for" loop
2257 int x = xstart + i * xstep;
2258 int y = ystart + i * ystep;
2259 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2260 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2261 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2262 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2265 SetDrawtoField(DRAW_FIELDBUFFER);
2267 BlitScreenToBitmap(backbuffer);
2269 SetDrawtoField(DRAW_BACKBUFFER);
2271 for (yy = 0; yy < ysize; yy++)
2272 for (xx = 0; xx < xsize; xx++)
2273 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2275 DrawTextBuffer(sx + font_width, sy + font_height,
2276 level.envelope[envelope_nr].text, font_nr, max_xsize,
2277 xsize - 2, ysize - 2, 0, mask_mode,
2278 level.envelope[envelope_nr].autowrap,
2279 level.envelope[envelope_nr].centered, FALSE);
2281 redraw_mask |= REDRAW_FIELD;
2284 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2288 void ShowEnvelope(int envelope_nr)
2290 int element = EL_ENVELOPE_1 + envelope_nr;
2291 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2292 int sound_opening = element_info[element].sound[ACTION_OPENING];
2293 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2294 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2295 boolean no_delay = (tape.warp_forward);
2296 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2297 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2298 int anim_mode = graphic_info[graphic].anim_mode;
2299 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2300 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2302 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2304 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2306 if (anim_mode == ANIM_DEFAULT)
2307 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2309 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2312 Delay(wait_delay_value);
2314 WaitForEventToContinue();
2316 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2318 if (anim_mode != ANIM_NONE)
2319 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2321 if (anim_mode == ANIM_DEFAULT)
2322 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2324 game.envelope_active = FALSE;
2326 SetDrawtoField(DRAW_FIELDBUFFER);
2328 redraw_mask |= REDRAW_FIELD;
2332 static void setRequestBasePosition(int *x, int *y)
2334 int sx_base, sy_base;
2336 if (request.x != -1)
2337 sx_base = request.x;
2338 else if (request.align == ALIGN_LEFT)
2340 else if (request.align == ALIGN_RIGHT)
2341 sx_base = SX + SXSIZE;
2343 sx_base = SX + SXSIZE / 2;
2345 if (request.y != -1)
2346 sy_base = request.y;
2347 else if (request.valign == VALIGN_TOP)
2349 else if (request.valign == VALIGN_BOTTOM)
2350 sy_base = SY + SYSIZE;
2352 sy_base = SY + SYSIZE / 2;
2358 static void setRequestPositionExt(int *x, int *y, int width, int height,
2359 boolean add_border_size)
2361 int border_size = request.border_size;
2362 int sx_base, sy_base;
2365 setRequestBasePosition(&sx_base, &sy_base);
2367 if (request.align == ALIGN_LEFT)
2369 else if (request.align == ALIGN_RIGHT)
2370 sx = sx_base - width;
2372 sx = sx_base - width / 2;
2374 if (request.valign == VALIGN_TOP)
2376 else if (request.valign == VALIGN_BOTTOM)
2377 sy = sy_base - height;
2379 sy = sy_base - height / 2;
2381 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2382 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2384 if (add_border_size)
2394 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2396 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2399 void DrawEnvelopeRequest(char *text)
2401 int last_game_status = game_status; /* save current game status */
2402 char *text_final = text;
2403 char *text_door_style = NULL;
2404 int graphic = IMG_BACKGROUND_REQUEST;
2405 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2406 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2407 int font_nr = FONT_REQUEST;
2408 int font_width = getFontWidth(font_nr);
2409 int font_height = getFontHeight(font_nr);
2410 int border_size = request.border_size;
2411 int line_spacing = request.line_spacing;
2412 int line_height = font_height + line_spacing;
2413 int max_text_width = request.width - 2 * border_size;
2414 int max_text_height = request.height - 2 * border_size;
2415 int line_length = max_text_width / font_width;
2416 int max_lines = max_text_height / line_height;
2417 int text_width = line_length * font_width;
2418 int width = request.width;
2419 int height = request.height;
2420 int tile_size = MAX(request.step_offset, 1);
2421 int x_steps = width / tile_size;
2422 int y_steps = height / tile_size;
2423 int sx_offset = border_size;
2424 int sy_offset = border_size;
2428 if (request.centered)
2429 sx_offset = (request.width - text_width) / 2;
2431 if (request.wrap_single_words && !request.autowrap)
2433 char *src_text_ptr, *dst_text_ptr;
2435 text_door_style = checked_malloc(2 * strlen(text) + 1);
2437 src_text_ptr = text;
2438 dst_text_ptr = text_door_style;
2440 while (*src_text_ptr)
2442 if (*src_text_ptr == ' ' ||
2443 *src_text_ptr == '?' ||
2444 *src_text_ptr == '!')
2445 *dst_text_ptr++ = '\n';
2447 if (*src_text_ptr != ' ')
2448 *dst_text_ptr++ = *src_text_ptr;
2453 *dst_text_ptr = '\0';
2455 text_final = text_door_style;
2458 setRequestPosition(&sx, &sy, FALSE);
2460 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2462 for (y = 0; y < y_steps; y++)
2463 for (x = 0; x < x_steps; x++)
2464 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2465 x, y, x_steps, y_steps,
2466 tile_size, tile_size);
2468 /* force DOOR font inside door area */
2469 SetGameStatus(GAME_MODE_PSEUDO_DOOR);
2471 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2472 line_length, -1, max_lines, line_spacing, mask_mode,
2473 request.autowrap, request.centered, FALSE);
2475 SetGameStatus(last_game_status); /* restore current game status */
2477 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2478 RedrawGadget(tool_gadget[i]);
2480 // store readily prepared envelope request for later use when animating
2481 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2483 if (text_door_style)
2484 free(text_door_style);
2487 void AnimateEnvelopeRequest(int anim_mode, int action)
2489 int graphic = IMG_BACKGROUND_REQUEST;
2490 boolean draw_masked = graphic_info[graphic].draw_masked;
2491 int delay_value_normal = request.step_delay;
2492 int delay_value_fast = delay_value_normal / 2;
2493 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2494 boolean no_delay = (tape.warp_forward);
2495 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2496 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2497 unsigned int anim_delay = 0;
2499 int tile_size = MAX(request.step_offset, 1);
2500 int max_xsize = request.width / tile_size;
2501 int max_ysize = request.height / tile_size;
2502 int max_xsize_inner = max_xsize - 2;
2503 int max_ysize_inner = max_ysize - 2;
2505 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2506 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2507 int xend = max_xsize_inner;
2508 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2509 int xstep = (xstart < xend ? 1 : 0);
2510 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2512 int end = MAX(xend - xstart, yend - ystart);
2515 if (setup.quick_doors)
2522 for (i = start; i <= end; i++)
2524 int last_frame = end; // last frame of this "for" loop
2525 int x = xstart + i * xstep;
2526 int y = ystart + i * ystep;
2527 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2528 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2529 int xsize_size_left = (xsize - 1) * tile_size;
2530 int ysize_size_top = (ysize - 1) * tile_size;
2531 int max_xsize_pos = (max_xsize - 1) * tile_size;
2532 int max_ysize_pos = (max_ysize - 1) * tile_size;
2533 int width = xsize * tile_size;
2534 int height = ysize * tile_size;
2539 setRequestPosition(&src_x, &src_y, FALSE);
2540 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2542 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2544 for (yy = 0; yy < 2; yy++)
2546 for (xx = 0; xx < 2; xx++)
2548 int src_xx = src_x + xx * max_xsize_pos;
2549 int src_yy = src_y + yy * max_ysize_pos;
2550 int dst_xx = dst_x + xx * xsize_size_left;
2551 int dst_yy = dst_y + yy * ysize_size_top;
2552 int xx_size = (xx ? tile_size : xsize_size_left);
2553 int yy_size = (yy ? tile_size : ysize_size_top);
2556 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2557 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2559 BlitBitmap(bitmap_db_cross, backbuffer,
2560 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2564 redraw_mask |= REDRAW_FIELD;
2569 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2573 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2575 int graphic = IMG_BACKGROUND_REQUEST;
2576 int sound_opening = SND_REQUEST_OPENING;
2577 int sound_closing = SND_REQUEST_CLOSING;
2578 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2579 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2580 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2581 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2582 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2584 if (game_status == GAME_MODE_PLAYING)
2585 BlitScreenToBitmap(backbuffer);
2587 SetDrawtoField(DRAW_BACKBUFFER);
2589 // SetDrawBackgroundMask(REDRAW_NONE);
2591 if (action == ACTION_OPENING)
2593 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2595 if (req_state & REQ_ASK)
2597 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2598 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2600 else if (req_state & REQ_CONFIRM)
2602 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2604 else if (req_state & REQ_PLAYER)
2606 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2607 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2608 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2609 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2612 DrawEnvelopeRequest(text);
2614 if (game_status != GAME_MODE_MAIN)
2618 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2620 if (action == ACTION_OPENING)
2622 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2624 if (anim_mode == ANIM_DEFAULT)
2625 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2627 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2631 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2633 if (anim_mode != ANIM_NONE)
2634 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2636 if (anim_mode == ANIM_DEFAULT)
2637 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2640 game.envelope_active = FALSE;
2642 if (action == ACTION_CLOSING)
2644 if (game_status != GAME_MODE_MAIN)
2647 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2650 // SetDrawBackgroundMask(last_draw_background_mask);
2652 redraw_mask |= REDRAW_FIELD;
2654 if (game_status == GAME_MODE_MAIN)
2659 if (action == ACTION_CLOSING &&
2660 game_status == GAME_MODE_PLAYING &&
2661 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2662 SetDrawtoField(DRAW_FIELDBUFFER);
2665 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2669 int graphic = el2preimg(element);
2671 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2672 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2675 void DrawLevel(int draw_background_mask)
2679 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2680 SetDrawBackgroundMask(draw_background_mask);
2684 for (x = BX1; x <= BX2; x++)
2685 for (y = BY1; y <= BY2; y++)
2686 DrawScreenField(x, y);
2688 redraw_mask |= REDRAW_FIELD;
2691 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2696 for (x = 0; x < size_x; x++)
2697 for (y = 0; y < size_y; y++)
2698 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2700 redraw_mask |= REDRAW_FIELD;
2703 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2707 for (x = 0; x < size_x; x++)
2708 for (y = 0; y < size_y; y++)
2709 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2711 redraw_mask |= REDRAW_FIELD;
2714 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2716 boolean show_level_border = (BorderElement != EL_EMPTY);
2717 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2718 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2719 int tile_size = preview.tile_size;
2720 int preview_width = preview.xsize * tile_size;
2721 int preview_height = preview.ysize * tile_size;
2722 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2723 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2724 int real_preview_width = real_preview_xsize * tile_size;
2725 int real_preview_height = real_preview_ysize * tile_size;
2726 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2727 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2730 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2733 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2735 dst_x += (preview_width - real_preview_width) / 2;
2736 dst_y += (preview_height - real_preview_height) / 2;
2738 for (x = 0; x < real_preview_xsize; x++)
2740 for (y = 0; y < real_preview_ysize; y++)
2742 int lx = from_x + x + (show_level_border ? -1 : 0);
2743 int ly = from_y + y + (show_level_border ? -1 : 0);
2744 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2745 getBorderElement(lx, ly));
2747 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2748 element, tile_size);
2752 redraw_mask |= REDRAW_FIELD;
2755 #define MICROLABEL_EMPTY 0
2756 #define MICROLABEL_LEVEL_NAME 1
2757 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2758 #define MICROLABEL_LEVEL_AUTHOR 3
2759 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2760 #define MICROLABEL_IMPORTED_FROM 5
2761 #define MICROLABEL_IMPORTED_BY_HEAD 6
2762 #define MICROLABEL_IMPORTED_BY 7
2764 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2766 int max_text_width = SXSIZE;
2767 int font_width = getFontWidth(font_nr);
2769 if (pos->align == ALIGN_CENTER)
2770 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2771 else if (pos->align == ALIGN_RIGHT)
2772 max_text_width = pos->x;
2774 max_text_width = SXSIZE - pos->x;
2776 return max_text_width / font_width;
2779 static void DrawPreviewLevelLabelExt(int mode)
2781 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2782 char label_text[MAX_OUTPUT_LINESIZE + 1];
2783 int max_len_label_text;
2784 int font_nr = pos->font;
2787 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2790 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2791 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2792 mode == MICROLABEL_IMPORTED_BY_HEAD)
2793 font_nr = pos->font_alt;
2795 max_len_label_text = getMaxTextLength(pos, font_nr);
2797 if (pos->size != -1)
2798 max_len_label_text = pos->size;
2800 for (i = 0; i < max_len_label_text; i++)
2801 label_text[i] = ' ';
2802 label_text[max_len_label_text] = '\0';
2804 if (strlen(label_text) > 0)
2805 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2808 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2809 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2810 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2811 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2812 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2813 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2814 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2815 max_len_label_text);
2816 label_text[max_len_label_text] = '\0';
2818 if (strlen(label_text) > 0)
2819 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2821 redraw_mask |= REDRAW_FIELD;
2824 static void DrawPreviewLevelExt(boolean restart)
2826 static unsigned int scroll_delay = 0;
2827 static unsigned int label_delay = 0;
2828 static int from_x, from_y, scroll_direction;
2829 static int label_state, label_counter;
2830 unsigned int scroll_delay_value = preview.step_delay;
2831 boolean show_level_border = (BorderElement != EL_EMPTY);
2832 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2833 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2834 int last_game_status = game_status; /* save current game status */
2841 if (preview.anim_mode == ANIM_CENTERED)
2843 if (level_xsize > preview.xsize)
2844 from_x = (level_xsize - preview.xsize) / 2;
2845 if (level_ysize > preview.ysize)
2846 from_y = (level_ysize - preview.ysize) / 2;
2849 from_x += preview.xoffset;
2850 from_y += preview.yoffset;
2852 scroll_direction = MV_RIGHT;
2856 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2857 DrawPreviewLevelLabelExt(label_state);
2859 /* initialize delay counters */
2860 DelayReached(&scroll_delay, 0);
2861 DelayReached(&label_delay, 0);
2863 if (leveldir_current->name)
2865 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2866 char label_text[MAX_OUTPUT_LINESIZE + 1];
2867 int font_nr = pos->font;
2868 int max_len_label_text = getMaxTextLength(pos, font_nr);
2870 if (pos->size != -1)
2871 max_len_label_text = pos->size;
2873 strncpy(label_text, leveldir_current->name, max_len_label_text);
2874 label_text[max_len_label_text] = '\0';
2876 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2877 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2880 SetGameStatus(last_game_status); /* restore current game status */
2885 /* scroll preview level, if needed */
2886 if (preview.anim_mode != ANIM_NONE &&
2887 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2888 DelayReached(&scroll_delay, scroll_delay_value))
2890 switch (scroll_direction)
2895 from_x -= preview.step_offset;
2896 from_x = (from_x < 0 ? 0 : from_x);
2899 scroll_direction = MV_UP;
2903 if (from_x < level_xsize - preview.xsize)
2905 from_x += preview.step_offset;
2906 from_x = (from_x > level_xsize - preview.xsize ?
2907 level_xsize - preview.xsize : from_x);
2910 scroll_direction = MV_DOWN;
2916 from_y -= preview.step_offset;
2917 from_y = (from_y < 0 ? 0 : from_y);
2920 scroll_direction = MV_RIGHT;
2924 if (from_y < level_ysize - preview.ysize)
2926 from_y += preview.step_offset;
2927 from_y = (from_y > level_ysize - preview.ysize ?
2928 level_ysize - preview.ysize : from_y);
2931 scroll_direction = MV_LEFT;
2938 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2941 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2942 /* redraw micro level label, if needed */
2943 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2944 !strEqual(level.author, ANONYMOUS_NAME) &&
2945 !strEqual(level.author, leveldir_current->name) &&
2946 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2948 int max_label_counter = 23;
2950 if (leveldir_current->imported_from != NULL &&
2951 strlen(leveldir_current->imported_from) > 0)
2952 max_label_counter += 14;
2953 if (leveldir_current->imported_by != NULL &&
2954 strlen(leveldir_current->imported_by) > 0)
2955 max_label_counter += 14;
2957 label_counter = (label_counter + 1) % max_label_counter;
2958 label_state = (label_counter >= 0 && label_counter <= 7 ?
2959 MICROLABEL_LEVEL_NAME :
2960 label_counter >= 9 && label_counter <= 12 ?
2961 MICROLABEL_LEVEL_AUTHOR_HEAD :
2962 label_counter >= 14 && label_counter <= 21 ?
2963 MICROLABEL_LEVEL_AUTHOR :
2964 label_counter >= 23 && label_counter <= 26 ?
2965 MICROLABEL_IMPORTED_FROM_HEAD :
2966 label_counter >= 28 && label_counter <= 35 ?
2967 MICROLABEL_IMPORTED_FROM :
2968 label_counter >= 37 && label_counter <= 40 ?
2969 MICROLABEL_IMPORTED_BY_HEAD :
2970 label_counter >= 42 && label_counter <= 49 ?
2971 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2973 if (leveldir_current->imported_from == NULL &&
2974 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2975 label_state == MICROLABEL_IMPORTED_FROM))
2976 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2977 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2979 DrawPreviewLevelLabelExt(label_state);
2982 SetGameStatus(last_game_status); /* restore current game status */
2985 void DrawPreviewLevelInitial()
2987 DrawPreviewLevelExt(TRUE);
2990 void DrawPreviewLevelAnimation()
2992 DrawPreviewLevelExt(FALSE);
2995 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2996 int graphic, int sync_frame,
2999 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3001 if (mask_mode == USE_MASKING)
3002 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3004 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
3007 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
3008 int graphic, int sync_frame, int mask_mode)
3010 int frame = getGraphicAnimationFrame(graphic, sync_frame);
3012 if (mask_mode == USE_MASKING)
3013 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
3015 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
3018 inline static void DrawGraphicAnimation(int x, int y, int graphic)
3020 int lx = LEVELX(x), ly = LEVELY(y);
3022 if (!IN_SCR_FIELD(x, y))
3025 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
3026 graphic, GfxFrame[lx][ly], NO_MASKING);
3028 MarkTileDirty(x, y);
3031 void DrawFixedGraphicAnimation(int x, int y, int graphic)
3033 int lx = LEVELX(x), ly = LEVELY(y);
3035 if (!IN_SCR_FIELD(x, y))
3038 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
3039 graphic, GfxFrame[lx][ly], NO_MASKING);
3040 MarkTileDirty(x, y);
3043 void DrawLevelGraphicAnimation(int x, int y, int graphic)
3045 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3048 void DrawLevelElementAnimation(int x, int y, int element)
3050 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3052 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
3055 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
3057 int sx = SCREENX(x), sy = SCREENY(y);
3059 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3062 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3065 DrawGraphicAnimation(sx, sy, graphic);
3068 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
3069 DrawLevelFieldCrumbled(x, y);
3071 if (GFX_CRUMBLED(Feld[x][y]))
3072 DrawLevelFieldCrumbled(x, y);
3076 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
3078 int sx = SCREENX(x), sy = SCREENY(y);
3081 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3084 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3086 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3089 DrawGraphicAnimation(sx, sy, graphic);
3091 if (GFX_CRUMBLED(element))
3092 DrawLevelFieldCrumbled(x, y);
3095 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3097 if (player->use_murphy)
3099 /* this works only because currently only one player can be "murphy" ... */
3100 static int last_horizontal_dir = MV_LEFT;
3101 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3103 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3104 last_horizontal_dir = move_dir;
3106 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3108 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3110 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3116 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3119 static boolean equalGraphics(int graphic1, int graphic2)
3121 struct GraphicInfo *g1 = &graphic_info[graphic1];
3122 struct GraphicInfo *g2 = &graphic_info[graphic2];
3124 return (g1->bitmap == g2->bitmap &&
3125 g1->src_x == g2->src_x &&
3126 g1->src_y == g2->src_y &&
3127 g1->anim_frames == g2->anim_frames &&
3128 g1->anim_delay == g2->anim_delay &&
3129 g1->anim_mode == g2->anim_mode);
3132 void DrawAllPlayers()
3136 for (i = 0; i < MAX_PLAYERS; i++)
3137 if (stored_player[i].active)
3138 DrawPlayer(&stored_player[i]);
3141 void DrawPlayerField(int x, int y)
3143 if (!IS_PLAYER(x, y))
3146 DrawPlayer(PLAYERINFO(x, y));
3149 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3151 void DrawPlayer(struct PlayerInfo *player)
3153 int jx = player->jx;
3154 int jy = player->jy;
3155 int move_dir = player->MovDir;
3156 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3157 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3158 int last_jx = (player->is_moving ? jx - dx : jx);
3159 int last_jy = (player->is_moving ? jy - dy : jy);
3160 int next_jx = jx + dx;
3161 int next_jy = jy + dy;
3162 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3163 boolean player_is_opaque = FALSE;
3164 int sx = SCREENX(jx), sy = SCREENY(jy);
3165 int sxx = 0, syy = 0;
3166 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3168 int action = ACTION_DEFAULT;
3169 int last_player_graphic = getPlayerGraphic(player, move_dir);
3170 int last_player_frame = player->Frame;
3173 /* GfxElement[][] is set to the element the player is digging or collecting;
3174 remove also for off-screen player if the player is not moving anymore */
3175 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3176 GfxElement[jx][jy] = EL_UNDEFINED;
3178 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3182 if (!IN_LEV_FIELD(jx, jy))
3184 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3185 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3186 printf("DrawPlayerField(): This should never happen!\n");
3191 if (element == EL_EXPLOSION)
3194 action = (player->is_pushing ? ACTION_PUSHING :
3195 player->is_digging ? ACTION_DIGGING :
3196 player->is_collecting ? ACTION_COLLECTING :
3197 player->is_moving ? ACTION_MOVING :
3198 player->is_snapping ? ACTION_SNAPPING :
3199 player->is_dropping ? ACTION_DROPPING :
3200 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3202 if (player->is_waiting)
3203 move_dir = player->dir_waiting;
3205 InitPlayerGfxAnimation(player, action, move_dir);
3207 /* ----------------------------------------------------------------------- */
3208 /* draw things in the field the player is leaving, if needed */
3209 /* ----------------------------------------------------------------------- */
3211 if (player->is_moving)
3213 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3215 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3217 if (last_element == EL_DYNAMITE_ACTIVE ||
3218 last_element == EL_EM_DYNAMITE_ACTIVE ||
3219 last_element == EL_SP_DISK_RED_ACTIVE)
3220 DrawDynamite(last_jx, last_jy);
3222 DrawLevelFieldThruMask(last_jx, last_jy);
3224 else if (last_element == EL_DYNAMITE_ACTIVE ||
3225 last_element == EL_EM_DYNAMITE_ACTIVE ||
3226 last_element == EL_SP_DISK_RED_ACTIVE)
3227 DrawDynamite(last_jx, last_jy);
3229 /* !!! this is not enough to prevent flickering of players which are
3230 moving next to each others without a free tile between them -- this
3231 can only be solved by drawing all players layer by layer (first the
3232 background, then the foreground etc.) !!! => TODO */
3233 else if (!IS_PLAYER(last_jx, last_jy))
3234 DrawLevelField(last_jx, last_jy);
3237 DrawLevelField(last_jx, last_jy);
3240 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3241 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3244 if (!IN_SCR_FIELD(sx, sy))
3247 /* ----------------------------------------------------------------------- */
3248 /* draw things behind the player, if needed */
3249 /* ----------------------------------------------------------------------- */
3252 DrawLevelElement(jx, jy, Back[jx][jy]);
3253 else if (IS_ACTIVE_BOMB(element))
3254 DrawLevelElement(jx, jy, EL_EMPTY);
3257 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3259 int old_element = GfxElement[jx][jy];
3260 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3261 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3263 if (GFX_CRUMBLED(old_element))
3264 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3266 DrawGraphic(sx, sy, old_graphic, frame);
3268 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3269 player_is_opaque = TRUE;
3273 GfxElement[jx][jy] = EL_UNDEFINED;
3275 /* make sure that pushed elements are drawn with correct frame rate */
3276 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3278 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3279 GfxFrame[jx][jy] = player->StepFrame;
3281 DrawLevelField(jx, jy);
3285 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3286 /* ----------------------------------------------------------------------- */
3287 /* draw player himself */
3288 /* ----------------------------------------------------------------------- */
3290 graphic = getPlayerGraphic(player, move_dir);
3292 /* in the case of changed player action or direction, prevent the current
3293 animation frame from being restarted for identical animations */
3294 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3295 player->Frame = last_player_frame;
3297 frame = getGraphicAnimationFrame(graphic, player->Frame);
3301 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3302 sxx = player->GfxPos;
3304 syy = player->GfxPos;
3307 if (player_is_opaque)
3308 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3310 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3312 if (SHIELD_ON(player))
3314 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3315 IMG_SHIELD_NORMAL_ACTIVE);
3316 int frame = getGraphicAnimationFrame(graphic, -1);
3318 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3322 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3325 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3326 sxx = player->GfxPos;
3328 syy = player->GfxPos;
3332 /* ----------------------------------------------------------------------- */
3333 /* draw things the player is pushing, if needed */
3334 /* ----------------------------------------------------------------------- */
3336 if (player->is_pushing && player->is_moving)
3338 int px = SCREENX(jx), py = SCREENY(jy);
3339 int pxx = (TILEX - ABS(sxx)) * dx;
3340 int pyy = (TILEY - ABS(syy)) * dy;
3341 int gfx_frame = GfxFrame[jx][jy];
3347 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3349 element = Feld[next_jx][next_jy];
3350 gfx_frame = GfxFrame[next_jx][next_jy];
3353 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3355 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3356 frame = getGraphicAnimationFrame(graphic, sync_frame);
3358 /* draw background element under pushed element (like the Sokoban field) */
3359 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3361 /* this allows transparent pushing animation over non-black background */
3364 DrawLevelElement(jx, jy, Back[jx][jy]);
3366 DrawLevelElement(jx, jy, EL_EMPTY);
3368 if (Back[next_jx][next_jy])
3369 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3371 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3373 else if (Back[next_jx][next_jy])
3374 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3377 /* do not draw (EM style) pushing animation when pushing is finished */
3378 /* (two-tile animations usually do not contain start and end frame) */
3379 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3380 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3382 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3384 /* masked drawing is needed for EMC style (double) movement graphics */
3385 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3386 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3390 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3391 /* ----------------------------------------------------------------------- */
3392 /* draw player himself */
3393 /* ----------------------------------------------------------------------- */
3395 graphic = getPlayerGraphic(player, move_dir);
3397 /* in the case of changed player action or direction, prevent the current
3398 animation frame from being restarted for identical animations */
3399 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3400 player->Frame = last_player_frame;
3402 frame = getGraphicAnimationFrame(graphic, player->Frame);
3406 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3407 sxx = player->GfxPos;
3409 syy = player->GfxPos;
3412 if (player_is_opaque)
3413 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3415 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3417 if (SHIELD_ON(player))
3419 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3420 IMG_SHIELD_NORMAL_ACTIVE);
3421 int frame = getGraphicAnimationFrame(graphic, -1);
3423 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3427 /* ----------------------------------------------------------------------- */
3428 /* draw things in front of player (active dynamite or dynabombs) */
3429 /* ----------------------------------------------------------------------- */
3431 if (IS_ACTIVE_BOMB(element))
3433 graphic = el2img(element);
3434 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3436 if (game.emulation == EMU_SUPAPLEX)
3437 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3439 DrawGraphicThruMask(sx, sy, graphic, frame);
3442 if (player_is_moving && last_element == EL_EXPLOSION)
3444 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3445 GfxElement[last_jx][last_jy] : EL_EMPTY);
3446 int graphic = el_act2img(element, ACTION_EXPLODING);
3447 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3448 int phase = ExplodePhase[last_jx][last_jy] - 1;
3449 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3452 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3455 /* ----------------------------------------------------------------------- */
3456 /* draw elements the player is just walking/passing through/under */
3457 /* ----------------------------------------------------------------------- */
3459 if (player_is_moving)
3461 /* handle the field the player is leaving ... */
3462 if (IS_ACCESSIBLE_INSIDE(last_element))
3463 DrawLevelField(last_jx, last_jy);
3464 else if (IS_ACCESSIBLE_UNDER(last_element))
3465 DrawLevelFieldThruMask(last_jx, last_jy);
3468 /* do not redraw accessible elements if the player is just pushing them */
3469 if (!player_is_moving || !player->is_pushing)
3471 /* ... and the field the player is entering */
3472 if (IS_ACCESSIBLE_INSIDE(element))
3473 DrawLevelField(jx, jy);
3474 else if (IS_ACCESSIBLE_UNDER(element))
3475 DrawLevelFieldThruMask(jx, jy);
3478 MarkTileDirty(sx, sy);
3481 /* ------------------------------------------------------------------------- */
3483 void WaitForEventToContinue()
3485 boolean still_wait = TRUE;
3487 /* simulate releasing mouse button over last gadget, if still pressed */
3489 HandleGadgets(-1, -1, 0);
3491 button_status = MB_RELEASED;
3505 case EVENT_BUTTONPRESS:
3506 case EVENT_KEYPRESS:
3510 case EVENT_KEYRELEASE:
3511 ClearPlayerAction();
3515 HandleOtherEvents(&event);
3519 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3526 WaitUntilDelayReached(&sync_frame_delay, sync_frame_delay_value);
3530 #define MAX_REQUEST_LINES 13
3531 #define MAX_REQUEST_LINE_FONT1_LEN 7
3532 #define MAX_REQUEST_LINE_FONT2_LEN 10
3534 static int RequestHandleEvents(unsigned int req_state)
3536 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3537 local_player->LevelSolved_GameEnd);
3538 int width = request.width;
3539 int height = request.height;
3543 setRequestPosition(&sx, &sy, FALSE);
3545 button_status = MB_RELEASED;
3547 request_gadget_id = -1;
3554 SetDrawtoField(DRAW_FIELDBUFFER);
3556 HandleGameActions();
3558 SetDrawtoField(DRAW_BACKBUFFER);
3560 if (global.use_envelope_request)
3562 /* copy current state of request area to middle of playfield area */
3563 BlitBitmap(bitmap_db_cross, drawto, sx, sy, width, height, sx, sy);
3571 while (NextValidEvent(&event))
3575 case EVENT_BUTTONPRESS:
3576 case EVENT_BUTTONRELEASE:
3577 case EVENT_MOTIONNOTIFY:
3581 if (event.type == EVENT_MOTIONNOTIFY)
3586 motion_status = TRUE;
3587 mx = ((MotionEvent *) &event)->x;
3588 my = ((MotionEvent *) &event)->y;
3592 motion_status = FALSE;
3593 mx = ((ButtonEvent *) &event)->x;
3594 my = ((ButtonEvent *) &event)->y;
3595 if (event.type == EVENT_BUTTONPRESS)
3596 button_status = ((ButtonEvent *) &event)->button;
3598 button_status = MB_RELEASED;
3601 /* this sets 'request_gadget_id' */
3602 HandleGadgets(mx, my, button_status);
3604 switch (request_gadget_id)
3606 case TOOL_CTRL_ID_YES:
3609 case TOOL_CTRL_ID_NO:
3612 case TOOL_CTRL_ID_CONFIRM:
3613 result = TRUE | FALSE;
3616 case TOOL_CTRL_ID_PLAYER_1:
3619 case TOOL_CTRL_ID_PLAYER_2:
3622 case TOOL_CTRL_ID_PLAYER_3:
3625 case TOOL_CTRL_ID_PLAYER_4:
3636 case EVENT_KEYPRESS:
3637 switch (GetEventKey((KeyEvent *)&event, TRUE))
3640 if (req_state & REQ_CONFIRM)
3645 #if defined(TARGET_SDL2)
3652 #if defined(TARGET_SDL2)
3662 if (req_state & REQ_PLAYER)
3666 case EVENT_KEYRELEASE:
3667 ClearPlayerAction();
3671 HandleOtherEvents(&event);
3676 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3678 int joy = AnyJoystick();
3680 if (joy & JOY_BUTTON_1)
3682 else if (joy & JOY_BUTTON_2)
3688 if (global.use_envelope_request)
3690 /* copy back current state of pressed buttons inside request area */
3691 BlitBitmap(drawto, bitmap_db_cross, sx, sy, width, height, sx, sy);
3701 WaitUntilDelayReached(&sync_frame_delay, sync_frame_delay_value);
3707 static boolean RequestDoor(char *text, unsigned int req_state)
3709 unsigned int old_door_state;
3710 int last_game_status = game_status; /* save current game status */
3711 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3712 int font_nr = FONT_TEXT_2;
3717 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3719 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3720 font_nr = FONT_TEXT_1;
3723 if (game_status == GAME_MODE_PLAYING)
3724 BlitScreenToBitmap(backbuffer);
3726 /* disable deactivated drawing when quick-loading level tape recording */
3727 if (tape.playing && tape.deactivate_display)
3728 TapeDeactivateDisplayOff(TRUE);
3730 SetMouseCursor(CURSOR_DEFAULT);
3732 #if defined(NETWORK_AVALIABLE)
3733 /* pause network game while waiting for request to answer */
3734 if (options.network &&
3735 game_status == GAME_MODE_PLAYING &&
3736 req_state & REQUEST_WAIT_FOR_INPUT)
3737 SendToServer_PausePlaying();
3740 old_door_state = GetDoorState();
3742 /* simulate releasing mouse button over last gadget, if still pressed */
3744 HandleGadgets(-1, -1, 0);
3748 /* draw released gadget before proceeding */
3751 if (old_door_state & DOOR_OPEN_1)
3753 CloseDoor(DOOR_CLOSE_1);
3755 /* save old door content */
3756 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3757 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3760 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3761 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3763 /* clear door drawing field */
3764 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3766 /* force DOOR font inside door area */
3767 SetGameStatus(GAME_MODE_PSEUDO_DOOR);
3769 /* write text for request */
3770 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3772 char text_line[max_request_line_len + 1];
3778 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3780 tc = *(text_ptr + tx);
3781 // if (!tc || tc == ' ')
3782 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3786 if ((tc == '?' || tc == '!') && tl == 0)
3796 strncpy(text_line, text_ptr, tl);
3799 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3800 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3801 text_line, font_nr);
3803 text_ptr += tl + (tc == ' ' ? 1 : 0);
3804 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3807 SetGameStatus(last_game_status); /* restore current game status */
3809 if (req_state & REQ_ASK)
3811 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3812 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3814 else if (req_state & REQ_CONFIRM)
3816 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3818 else if (req_state & REQ_PLAYER)
3820 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3821 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3822 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3823 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3826 /* copy request gadgets to door backbuffer */
3827 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3829 OpenDoor(DOOR_OPEN_1);
3831 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3833 if (game_status == GAME_MODE_PLAYING)
3835 SetPanelBackground();
3836 SetDrawBackgroundMask(REDRAW_DOOR_1);
3840 SetDrawBackgroundMask(REDRAW_FIELD);
3846 if (game_status != GAME_MODE_MAIN)
3849 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3851 // ---------- handle request buttons ----------
3852 result = RequestHandleEvents(req_state);
3854 if (game_status != GAME_MODE_MAIN)
3859 if (!(req_state & REQ_STAY_OPEN))
3861 CloseDoor(DOOR_CLOSE_1);
3863 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3864 (req_state & REQ_REOPEN))
3865 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3870 if (game_status == GAME_MODE_PLAYING)
3872 SetPanelBackground();
3873 SetDrawBackgroundMask(REDRAW_DOOR_1);
3877 SetDrawBackgroundMask(REDRAW_FIELD);
3880 #if defined(NETWORK_AVALIABLE)
3881 /* continue network game after request */
3882 if (options.network &&
3883 game_status == GAME_MODE_PLAYING &&
3884 req_state & REQUEST_WAIT_FOR_INPUT)
3885 SendToServer_ContinuePlaying();
3888 /* restore deactivated drawing when quick-loading level tape recording */
3889 if (tape.playing && tape.deactivate_display)
3890 TapeDeactivateDisplayOn();
3895 static boolean RequestEnvelope(char *text, unsigned int req_state)
3899 if (game_status == GAME_MODE_PLAYING)
3900 BlitScreenToBitmap(backbuffer);
3902 /* disable deactivated drawing when quick-loading level tape recording */
3903 if (tape.playing && tape.deactivate_display)
3904 TapeDeactivateDisplayOff(TRUE);
3906 SetMouseCursor(CURSOR_DEFAULT);
3908 #if defined(NETWORK_AVALIABLE)
3909 /* pause network game while waiting for request to answer */
3910 if (options.network &&
3911 game_status == GAME_MODE_PLAYING &&
3912 req_state & REQUEST_WAIT_FOR_INPUT)
3913 SendToServer_PausePlaying();
3916 /* simulate releasing mouse button over last gadget, if still pressed */
3918 HandleGadgets(-1, -1, 0);
3922 // (replace with setting corresponding request background)
3923 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3924 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3926 /* clear door drawing field */
3927 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3929 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3931 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3933 if (game_status == GAME_MODE_PLAYING)
3935 SetPanelBackground();
3936 SetDrawBackgroundMask(REDRAW_DOOR_1);
3940 SetDrawBackgroundMask(REDRAW_FIELD);
3946 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3948 // ---------- handle request buttons ----------
3949 result = RequestHandleEvents(req_state);
3951 if (game_status != GAME_MODE_MAIN)
3956 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3960 if (game_status == GAME_MODE_PLAYING)
3962 SetPanelBackground();
3963 SetDrawBackgroundMask(REDRAW_DOOR_1);
3967 SetDrawBackgroundMask(REDRAW_FIELD);
3970 #if defined(NETWORK_AVALIABLE)
3971 /* continue network game after request */
3972 if (options.network &&
3973 game_status == GAME_MODE_PLAYING &&
3974 req_state & REQUEST_WAIT_FOR_INPUT)
3975 SendToServer_ContinuePlaying();
3978 /* restore deactivated drawing when quick-loading level tape recording */
3979 if (tape.playing && tape.deactivate_display)
3980 TapeDeactivateDisplayOn();
3985 boolean Request(char *text, unsigned int req_state)
3987 if (global.use_envelope_request)
3988 return RequestEnvelope(text, req_state);
3990 return RequestDoor(text, req_state);
3993 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3995 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3996 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3999 if (dpo1->sort_priority != dpo2->sort_priority)
4000 compare_result = dpo1->sort_priority - dpo2->sort_priority;
4002 compare_result = dpo1->nr - dpo2->nr;
4004 return compare_result;
4007 void InitGraphicCompatibilityInfo_Doors()
4013 struct DoorInfo *door;
4017 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
4018 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
4020 { -1, -1, -1, NULL }
4022 struct Rect door_rect_list[] =
4024 { DX, DY, DXSIZE, DYSIZE },
4025 { VX, VY, VXSIZE, VYSIZE }
4029 for (i = 0; doors[i].door_token != -1; i++)
4031 int door_token = doors[i].door_token;
4032 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4033 int part_1 = doors[i].part_1;
4034 int part_8 = doors[i].part_8;
4035 int part_2 = part_1 + 1;
4036 int part_3 = part_1 + 2;
4037 struct DoorInfo *door = doors[i].door;
4038 struct Rect *door_rect = &door_rect_list[door_index];
4039 boolean door_gfx_redefined = FALSE;
4041 /* check if any door part graphic definitions have been redefined */
4043 for (j = 0; door_part_controls[j].door_token != -1; j++)
4045 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4046 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
4048 if (dpc->door_token == door_token && fi->redefined)
4049 door_gfx_redefined = TRUE;
4052 /* check for old-style door graphic/animation modifications */
4054 if (!door_gfx_redefined)
4056 if (door->anim_mode & ANIM_STATIC_PANEL)
4058 door->panel.step_xoffset = 0;
4059 door->panel.step_yoffset = 0;
4062 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
4064 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
4065 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
4066 int num_door_steps, num_panel_steps;
4068 /* remove door part graphics other than the two default wings */
4070 for (j = 0; door_part_controls[j].door_token != -1; j++)
4072 struct DoorPartControlInfo *dpc = &door_part_controls[j];
4073 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4075 if (dpc->graphic >= part_3 &&
4076 dpc->graphic <= part_8)
4080 /* set graphics and screen positions of the default wings */
4082 g_part_1->width = door_rect->width;
4083 g_part_1->height = door_rect->height;
4084 g_part_2->width = door_rect->width;
4085 g_part_2->height = door_rect->height;
4086 g_part_2->src_x = door_rect->width;
4087 g_part_2->src_y = g_part_1->src_y;
4089 door->part_2.x = door->part_1.x;
4090 door->part_2.y = door->part_1.y;
4092 if (door->width != -1)
4094 g_part_1->width = door->width;
4095 g_part_2->width = door->width;
4097 // special treatment for graphics and screen position of right wing
4098 g_part_2->src_x += door_rect->width - door->width;
4099 door->part_2.x += door_rect->width - door->width;
4102 if (door->height != -1)
4104 g_part_1->height = door->height;
4105 g_part_2->height = door->height;
4107 // special treatment for graphics and screen position of bottom wing
4108 g_part_2->src_y += door_rect->height - door->height;
4109 door->part_2.y += door_rect->height - door->height;
4112 /* set animation delays for the default wings and panels */
4114 door->part_1.step_delay = door->step_delay;
4115 door->part_2.step_delay = door->step_delay;
4116 door->panel.step_delay = door->step_delay;
4118 /* set animation draw order for the default wings */
4120 door->part_1.sort_priority = 2; /* draw left wing over ... */
4121 door->part_2.sort_priority = 1; /* ... right wing */
4123 /* set animation draw offset for the default wings */
4125 if (door->anim_mode & ANIM_HORIZONTAL)
4127 door->part_1.step_xoffset = door->step_offset;
4128 door->part_1.step_yoffset = 0;
4129 door->part_2.step_xoffset = door->step_offset * -1;
4130 door->part_2.step_yoffset = 0;
4132 num_door_steps = g_part_1->width / door->step_offset;
4134 else // ANIM_VERTICAL
4136 door->part_1.step_xoffset = 0;
4137 door->part_1.step_yoffset = door->step_offset;
4138 door->part_2.step_xoffset = 0;
4139 door->part_2.step_yoffset = door->step_offset * -1;
4141 num_door_steps = g_part_1->height / door->step_offset;
4144 /* set animation draw offset for the default panels */
4146 if (door->step_offset > 1)
4148 num_panel_steps = 2 * door_rect->height / door->step_offset;
4149 door->panel.start_step = num_panel_steps - num_door_steps;
4150 door->panel.start_step_closing = door->panel.start_step;
4154 num_panel_steps = door_rect->height / door->step_offset;
4155 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4156 door->panel.start_step_closing = door->panel.start_step;
4157 door->panel.step_delay *= 2;
4168 for (i = 0; door_part_controls[i].door_token != -1; i++)
4170 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4171 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4173 /* initialize "start_step_opening" and "start_step_closing", if needed */
4174 if (dpc->pos->start_step_opening == 0 &&
4175 dpc->pos->start_step_closing == 0)
4177 // dpc->pos->start_step_opening = dpc->pos->start_step;
4178 dpc->pos->start_step_closing = dpc->pos->start_step;
4181 /* fill structure for door part draw order (sorted below) */
4183 dpo->sort_priority = dpc->pos->sort_priority;
4186 /* sort door part controls according to sort_priority and graphic number */
4187 qsort(door_part_order, MAX_DOOR_PARTS,
4188 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4191 unsigned int OpenDoor(unsigned int door_state)
4193 if (door_state & DOOR_COPY_BACK)
4195 if (door_state & DOOR_OPEN_1)
4196 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4197 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4199 if (door_state & DOOR_OPEN_2)
4200 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4201 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4203 door_state &= ~DOOR_COPY_BACK;
4206 return MoveDoor(door_state);
4209 unsigned int CloseDoor(unsigned int door_state)
4211 unsigned int old_door_state = GetDoorState();
4213 if (!(door_state & DOOR_NO_COPY_BACK))
4215 if (old_door_state & DOOR_OPEN_1)
4216 BlitBitmap(backbuffer, bitmap_db_door_1,
4217 DX, DY, DXSIZE, DYSIZE, 0, 0);
4219 if (old_door_state & DOOR_OPEN_2)
4220 BlitBitmap(backbuffer, bitmap_db_door_2,
4221 VX, VY, VXSIZE, VYSIZE, 0, 0);
4223 door_state &= ~DOOR_NO_COPY_BACK;
4226 return MoveDoor(door_state);
4229 unsigned int GetDoorState()
4231 return MoveDoor(DOOR_GET_STATE);
4234 unsigned int SetDoorState(unsigned int door_state)
4236 return MoveDoor(door_state | DOOR_SET_STATE);
4239 int euclid(int a, int b)
4241 return (b ? euclid(b, a % b) : a);
4244 unsigned int MoveDoor(unsigned int door_state)
4246 struct Rect door_rect_list[] =
4248 { DX, DY, DXSIZE, DYSIZE },
4249 { VX, VY, VXSIZE, VYSIZE }
4251 static int door1 = DOOR_CLOSE_1;
4252 static int door2 = DOOR_CLOSE_2;
4253 unsigned int door_delay = 0;
4254 unsigned int door_delay_value;
4257 if (door_state == DOOR_GET_STATE)
4258 return (door1 | door2);
4260 if (door_state & DOOR_SET_STATE)
4262 if (door_state & DOOR_ACTION_1)
4263 door1 = door_state & DOOR_ACTION_1;
4264 if (door_state & DOOR_ACTION_2)
4265 door2 = door_state & DOOR_ACTION_2;
4267 return (door1 | door2);
4270 if (!(door_state & DOOR_FORCE_REDRAW))
4272 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4273 door_state &= ~DOOR_OPEN_1;
4274 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4275 door_state &= ~DOOR_CLOSE_1;
4276 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4277 door_state &= ~DOOR_OPEN_2;
4278 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4279 door_state &= ~DOOR_CLOSE_2;
4282 if (global.autoplay_leveldir)
4284 door_state |= DOOR_NO_DELAY;
4285 door_state &= ~DOOR_CLOSE_ALL;
4288 if (game_status == GAME_MODE_EDITOR)
4289 door_state |= DOOR_NO_DELAY;
4291 if (door_state & DOOR_ACTION)
4293 boolean door_panel_drawn[NUM_DOORS];
4294 boolean panel_has_doors[NUM_DOORS];
4295 boolean door_part_skip[MAX_DOOR_PARTS];
4296 boolean door_part_done[MAX_DOOR_PARTS];
4297 boolean door_part_done_all;
4298 int num_steps[MAX_DOOR_PARTS];
4299 int max_move_delay = 0; // delay for complete animations of all doors
4300 int max_step_delay = 0; // delay (ms) between two animation frames
4301 int num_move_steps = 0; // number of animation steps for all doors
4302 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4303 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4304 int current_move_delay = 0;
4308 for (i = 0; i < NUM_DOORS; i++)
4309 panel_has_doors[i] = FALSE;
4311 for (i = 0; i < MAX_DOOR_PARTS; i++)
4313 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4314 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4315 int door_token = dpc->door_token;
4317 door_part_done[i] = FALSE;
4318 door_part_skip[i] = (!(door_state & door_token) ||
4322 for (i = 0; i < MAX_DOOR_PARTS; i++)
4324 int nr = door_part_order[i].nr;
4325 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4326 struct DoorPartPosInfo *pos = dpc->pos;
4327 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4328 int door_token = dpc->door_token;
4329 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4330 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4331 int step_xoffset = ABS(pos->step_xoffset);
4332 int step_yoffset = ABS(pos->step_yoffset);
4333 int step_delay = pos->step_delay;
4334 int current_door_state = door_state & door_token;
4335 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4336 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4337 boolean part_opening = (is_panel ? door_closing : door_opening);
4338 int start_step = (part_opening ? pos->start_step_opening :
4339 pos->start_step_closing);
4340 float move_xsize = (step_xoffset ? g->width : 0);
4341 float move_ysize = (step_yoffset ? g->height : 0);
4342 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4343 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4344 int move_steps = (move_xsteps && move_ysteps ?
4345 MIN(move_xsteps, move_ysteps) :
4346 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4347 int move_delay = move_steps * step_delay;
4349 if (door_part_skip[nr])
4352 max_move_delay = MAX(max_move_delay, move_delay);
4353 max_step_delay = (max_step_delay == 0 ? step_delay :
4354 euclid(max_step_delay, step_delay));
4355 num_steps[nr] = move_steps;
4359 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4361 panel_has_doors[door_index] = TRUE;
4365 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4367 num_move_steps = max_move_delay / max_step_delay;
4368 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4370 door_delay_value = max_step_delay;
4372 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4374 start = num_move_steps - 1;
4378 /* opening door sound has priority over simultaneously closing door */
4379 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4380 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4381 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4382 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4385 for (k = start; k < num_move_steps; k++)
4387 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4389 door_part_done_all = TRUE;
4391 for (i = 0; i < NUM_DOORS; i++)
4392 door_panel_drawn[i] = FALSE;
4394 for (i = 0; i < MAX_DOOR_PARTS; i++)
4396 int nr = door_part_order[i].nr;
4397 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4398 struct DoorPartPosInfo *pos = dpc->pos;
4399 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4400 int door_token = dpc->door_token;
4401 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4402 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4403 boolean is_panel_and_door_has_closed = FALSE;
4404 struct Rect *door_rect = &door_rect_list[door_index];
4405 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4407 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4408 int current_door_state = door_state & door_token;
4409 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4410 boolean door_closing = !door_opening;
4411 boolean part_opening = (is_panel ? door_closing : door_opening);
4412 boolean part_closing = !part_opening;
4413 int start_step = (part_opening ? pos->start_step_opening :
4414 pos->start_step_closing);
4415 int step_delay = pos->step_delay;
4416 int step_factor = step_delay / max_step_delay;
4417 int k1 = (step_factor ? k / step_factor + 1 : k);
4418 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4419 int kk = MAX(0, k2);
4422 int src_x, src_y, src_xx, src_yy;
4423 int dst_x, dst_y, dst_xx, dst_yy;
4426 if (door_part_skip[nr])
4429 if (!(door_state & door_token))
4437 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4438 int kk_door = MAX(0, k2_door);
4439 int sync_frame = kk_door * door_delay_value;
4440 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4442 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4447 if (!door_panel_drawn[door_index])
4449 ClearRectangle(drawto, door_rect->x, door_rect->y,
4450 door_rect->width, door_rect->height);
4452 door_panel_drawn[door_index] = TRUE;
4455 // draw opening or closing door parts
4457 if (pos->step_xoffset < 0) // door part on right side
4460 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4463 if (dst_xx + width > door_rect->width)
4464 width = door_rect->width - dst_xx;
4466 else // door part on left side
4469 dst_xx = pos->x - kk * pos->step_xoffset;
4473 src_xx = ABS(dst_xx);
4477 width = g->width - src_xx;
4479 if (width > door_rect->width)
4480 width = door_rect->width;
4482 // printf("::: k == %d [%d] \n", k, start_step);
4485 if (pos->step_yoffset < 0) // door part on bottom side
4488 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4491 if (dst_yy + height > door_rect->height)
4492 height = door_rect->height - dst_yy;
4494 else // door part on top side
4497 dst_yy = pos->y - kk * pos->step_yoffset;
4501 src_yy = ABS(dst_yy);
4505 height = g->height - src_yy;
4508 src_x = g_src_x + src_xx;
4509 src_y = g_src_y + src_yy;
4511 dst_x = door_rect->x + dst_xx;
4512 dst_y = door_rect->y + dst_yy;
4514 is_panel_and_door_has_closed =
4517 panel_has_doors[door_index] &&
4518 k >= num_move_steps_doors_only - 1);
4520 if (width >= 0 && width <= g->width &&
4521 height >= 0 && height <= g->height &&
4522 !is_panel_and_door_has_closed)
4524 if (is_panel || !pos->draw_masked)
4525 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4528 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4532 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4534 if ((part_opening && (width < 0 || height < 0)) ||
4535 (part_closing && (width >= g->width && height >= g->height)))
4536 door_part_done[nr] = TRUE;
4538 // continue door part animations, but not panel after door has closed
4539 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4540 door_part_done_all = FALSE;
4543 if (!(door_state & DOOR_NO_DELAY))
4547 if (game_status == GAME_MODE_MAIN)
4550 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4552 current_move_delay += max_step_delay;
4555 if (door_part_done_all)
4560 if (door_state & DOOR_ACTION_1)
4561 door1 = door_state & DOOR_ACTION_1;
4562 if (door_state & DOOR_ACTION_2)
4563 door2 = door_state & DOOR_ACTION_2;
4565 // draw masked border over door area
4566 DrawMaskedBorder(REDRAW_DOOR_1);
4567 DrawMaskedBorder(REDRAW_DOOR_2);
4569 return (door1 | door2);
4572 static boolean useSpecialEditorDoor()
4574 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4575 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4577 // do not draw special editor door if editor border defined or redefined
4578 if (graphic_info[graphic].bitmap != NULL || redefined)
4581 // do not draw special editor door if global border defined to be empty
4582 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4585 // do not draw special editor door if viewport definitions do not match
4589 EY + EYSIZE != VY + VYSIZE)
4595 void DrawSpecialEditorDoor()
4597 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4598 int top_border_width = gfx1->width;
4599 int top_border_height = gfx1->height;
4600 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4601 int ex = EX - outer_border;
4602 int ey = EY - outer_border;
4603 int vy = VY - outer_border;
4604 int exsize = EXSIZE + 2 * outer_border;
4606 if (!useSpecialEditorDoor())
4609 /* draw bigger level editor toolbox window */
4610 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4611 top_border_width, top_border_height, ex, ey - top_border_height);
4612 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4613 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4615 redraw_mask |= REDRAW_ALL;
4618 void UndrawSpecialEditorDoor()
4620 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4621 int top_border_width = gfx1->width;
4622 int top_border_height = gfx1->height;
4623 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4624 int ex = EX - outer_border;
4625 int ey = EY - outer_border;
4626 int ey_top = ey - top_border_height;
4627 int exsize = EXSIZE + 2 * outer_border;
4628 int eysize = EYSIZE + 2 * outer_border;
4630 if (!useSpecialEditorDoor())
4633 /* draw normal tape recorder window */
4634 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4636 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4637 ex, ey_top, top_border_width, top_border_height,
4639 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4640 ex, ey, exsize, eysize, ex, ey);
4644 // if screen background is set to "[NONE]", clear editor toolbox window
4645 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4646 ClearRectangle(drawto, ex, ey, exsize, eysize);
4649 redraw_mask |= REDRAW_ALL;
4653 /* ---------- new tool button stuff ---------------------------------------- */
4658 struct TextPosInfo *pos;
4661 } toolbutton_info[NUM_TOOL_BUTTONS] =
4664 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4665 TOOL_CTRL_ID_YES, "yes"
4668 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4669 TOOL_CTRL_ID_NO, "no"
4672 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4673 TOOL_CTRL_ID_CONFIRM, "confirm"
4676 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4677 TOOL_CTRL_ID_PLAYER_1, "player 1"
4680 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4681 TOOL_CTRL_ID_PLAYER_2, "player 2"
4684 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4685 TOOL_CTRL_ID_PLAYER_3, "player 3"
4688 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4689 TOOL_CTRL_ID_PLAYER_4, "player 4"
4693 void CreateToolButtons()
4697 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4699 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4700 struct TextPosInfo *pos = toolbutton_info[i].pos;
4701 struct GadgetInfo *gi;
4702 Bitmap *deco_bitmap = None;
4703 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4704 unsigned int event_mask = GD_EVENT_RELEASED;
4707 int gd_x = gfx->src_x;
4708 int gd_y = gfx->src_y;
4709 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4710 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4713 if (global.use_envelope_request)
4714 setRequestPosition(&dx, &dy, TRUE);
4716 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4718 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4720 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4721 pos->size, &deco_bitmap, &deco_x, &deco_y);
4722 deco_xpos = (gfx->width - pos->size) / 2;
4723 deco_ypos = (gfx->height - pos->size) / 2;
4726 gi = CreateGadget(GDI_CUSTOM_ID, id,
4727 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4728 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4729 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4730 GDI_WIDTH, gfx->width,
4731 GDI_HEIGHT, gfx->height,
4732 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4733 GDI_STATE, GD_BUTTON_UNPRESSED,
4734 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4735 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4736 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4737 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4738 GDI_DECORATION_SIZE, pos->size, pos->size,
4739 GDI_DECORATION_SHIFTING, 1, 1,
4740 GDI_DIRECT_DRAW, FALSE,
4741 GDI_EVENT_MASK, event_mask,
4742 GDI_CALLBACK_ACTION, HandleToolButtons,
4746 Error(ERR_EXIT, "cannot create gadget");
4748 tool_gadget[id] = gi;
4752 void FreeToolButtons()
4756 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4757 FreeGadget(tool_gadget[i]);
4760 static void UnmapToolButtons()
4764 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4765 UnmapGadget(tool_gadget[i]);
4768 static void HandleToolButtons(struct GadgetInfo *gi)
4770 request_gadget_id = gi->custom_id;
4773 static struct Mapping_EM_to_RND_object
4776 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4777 boolean is_backside; /* backside of moving element */
4783 em_object_mapping_list[] =
4786 Xblank, TRUE, FALSE,
4790 Yacid_splash_eB, FALSE, FALSE,
4791 EL_ACID_SPLASH_RIGHT, -1, -1
4794 Yacid_splash_wB, FALSE, FALSE,
4795 EL_ACID_SPLASH_LEFT, -1, -1
4798 #ifdef EM_ENGINE_BAD_ROLL
4800 Xstone_force_e, FALSE, FALSE,
4801 EL_ROCK, -1, MV_BIT_RIGHT
4804 Xstone_force_w, FALSE, FALSE,
4805 EL_ROCK, -1, MV_BIT_LEFT
4808 Xnut_force_e, FALSE, FALSE,
4809 EL_NUT, -1, MV_BIT_RIGHT
4812 Xnut_force_w, FALSE, FALSE,
4813 EL_NUT, -1, MV_BIT_LEFT
4816 Xspring_force_e, FALSE, FALSE,
4817 EL_SPRING, -1, MV_BIT_RIGHT
4820 Xspring_force_w, FALSE, FALSE,
4821 EL_SPRING, -1, MV_BIT_LEFT
4824 Xemerald_force_e, FALSE, FALSE,
4825 EL_EMERALD, -1, MV_BIT_RIGHT
4828 Xemerald_force_w, FALSE, FALSE,
4829 EL_EMERALD, -1, MV_BIT_LEFT
4832 Xdiamond_force_e, FALSE, FALSE,
4833 EL_DIAMOND, -1, MV_BIT_RIGHT
4836 Xdiamond_force_w, FALSE, FALSE,
4837 EL_DIAMOND, -1, MV_BIT_LEFT
4840 Xbomb_force_e, FALSE, FALSE,
4841 EL_BOMB, -1, MV_BIT_RIGHT
4844 Xbomb_force_w, FALSE, FALSE,
4845 EL_BOMB, -1, MV_BIT_LEFT
4847 #endif /* EM_ENGINE_BAD_ROLL */
4850 Xstone, TRUE, FALSE,
4854 Xstone_pause, FALSE, FALSE,
4858 Xstone_fall, FALSE, FALSE,
4862 Ystone_s, FALSE, FALSE,
4863 EL_ROCK, ACTION_FALLING, -1
4866 Ystone_sB, FALSE, TRUE,
4867 EL_ROCK, ACTION_FALLING, -1
4870 Ystone_e, FALSE, FALSE,
4871 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4874 Ystone_eB, FALSE, TRUE,
4875 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4878 Ystone_w, FALSE, FALSE,
4879 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4882 Ystone_wB, FALSE, TRUE,
4883 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4890 Xnut_pause, FALSE, FALSE,
4894 Xnut_fall, FALSE, FALSE,
4898 Ynut_s, FALSE, FALSE,
4899 EL_NUT, ACTION_FALLING, -1
4902 Ynut_sB, FALSE, TRUE,
4903 EL_NUT, ACTION_FALLING, -1
4906 Ynut_e, FALSE, FALSE,
4907 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4910 Ynut_eB, FALSE, TRUE,
4911 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4914 Ynut_w, FALSE, FALSE,
4915 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4918 Ynut_wB, FALSE, TRUE,
4919 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4922 Xbug_n, TRUE, FALSE,
4926 Xbug_e, TRUE, FALSE,
4927 EL_BUG_RIGHT, -1, -1
4930 Xbug_s, TRUE, FALSE,
4934 Xbug_w, TRUE, FALSE,
4938 Xbug_gon, FALSE, FALSE,
4942 Xbug_goe, FALSE, FALSE,
4943 EL_BUG_RIGHT, -1, -1
4946 Xbug_gos, FALSE, FALSE,
4950 Xbug_gow, FALSE, FALSE,
4954 Ybug_n, FALSE, FALSE,
4955 EL_BUG, ACTION_MOVING, MV_BIT_UP
4958 Ybug_nB, FALSE, TRUE,
4959 EL_BUG, ACTION_MOVING, MV_BIT_UP
4962 Ybug_e, FALSE, FALSE,
4963 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4966 Ybug_eB, FALSE, TRUE,
4967 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4970 Ybug_s, FALSE, FALSE,
4971 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4974 Ybug_sB, FALSE, TRUE,
4975 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4978 Ybug_w, FALSE, FALSE,
4979 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4982 Ybug_wB, FALSE, TRUE,
4983 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4986 Ybug_w_n, FALSE, FALSE,
4987 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4990 Ybug_n_e, FALSE, FALSE,
4991 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4994 Ybug_e_s, FALSE, FALSE,
4995 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4998 Ybug_s_w, FALSE, FALSE,
4999 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5002 Ybug_e_n, FALSE, FALSE,
5003 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5006 Ybug_s_e, FALSE, FALSE,
5007 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5010 Ybug_w_s, FALSE, FALSE,
5011 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5014 Ybug_n_w, FALSE, FALSE,
5015 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5018 Ybug_stone, FALSE, FALSE,
5019 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
5022 Ybug_spring, FALSE, FALSE,
5023 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
5026 Xtank_n, TRUE, FALSE,
5027 EL_SPACESHIP_UP, -1, -1
5030 Xtank_e, TRUE, FALSE,
5031 EL_SPACESHIP_RIGHT, -1, -1
5034 Xtank_s, TRUE, FALSE,
5035 EL_SPACESHIP_DOWN, -1, -1
5038 Xtank_w, TRUE, FALSE,
5039 EL_SPACESHIP_LEFT, -1, -1
5042 Xtank_gon, FALSE, FALSE,
5043 EL_SPACESHIP_UP, -1, -1
5046 Xtank_goe, FALSE, FALSE,
5047 EL_SPACESHIP_RIGHT, -1, -1
5050 Xtank_gos, FALSE, FALSE,
5051 EL_SPACESHIP_DOWN, -1, -1
5054 Xtank_gow, FALSE, FALSE,
5055 EL_SPACESHIP_LEFT, -1, -1
5058 Ytank_n, FALSE, FALSE,
5059 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5062 Ytank_nB, FALSE, TRUE,
5063 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
5066 Ytank_e, FALSE, FALSE,
5067 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5070 Ytank_eB, FALSE, TRUE,
5071 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
5074 Ytank_s, FALSE, FALSE,
5075 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5078 Ytank_sB, FALSE, TRUE,
5079 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5082 Ytank_w, FALSE, FALSE,
5083 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5086 Ytank_wB, FALSE, TRUE,
5087 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5090 Ytank_w_n, FALSE, FALSE,
5091 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5094 Ytank_n_e, FALSE, FALSE,
5095 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5098 Ytank_e_s, FALSE, FALSE,
5099 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5102 Ytank_s_w, FALSE, FALSE,
5103 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5106 Ytank_e_n, FALSE, FALSE,
5107 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5110 Ytank_s_e, FALSE, FALSE,
5111 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5114 Ytank_w_s, FALSE, FALSE,
5115 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5118 Ytank_n_w, FALSE, FALSE,
5119 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5122 Ytank_stone, FALSE, FALSE,
5123 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5126 Ytank_spring, FALSE, FALSE,
5127 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5130 Xandroid, TRUE, FALSE,
5131 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5134 Xandroid_1_n, FALSE, FALSE,
5135 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5138 Xandroid_2_n, FALSE, FALSE,
5139 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5142 Xandroid_1_e, FALSE, FALSE,
5143 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5146 Xandroid_2_e, FALSE, FALSE,
5147 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5150 Xandroid_1_w, FALSE, FALSE,
5151 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5154 Xandroid_2_w, FALSE, FALSE,
5155 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5158 Xandroid_1_s, FALSE, FALSE,
5159 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5162 Xandroid_2_s, FALSE, FALSE,
5163 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5166 Yandroid_n, FALSE, FALSE,
5167 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5170 Yandroid_nB, FALSE, TRUE,
5171 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5174 Yandroid_ne, FALSE, FALSE,
5175 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5178 Yandroid_neB, FALSE, TRUE,
5179 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5182 Yandroid_e, FALSE, FALSE,
5183 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5186 Yandroid_eB, FALSE, TRUE,
5187 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5190 Yandroid_se, FALSE, FALSE,
5191 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5194 Yandroid_seB, FALSE, TRUE,
5195 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5198 Yandroid_s, FALSE, FALSE,
5199 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5202 Yandroid_sB, FALSE, TRUE,
5203 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5206 Yandroid_sw, FALSE, FALSE,
5207 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5210 Yandroid_swB, FALSE, TRUE,
5211 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5214 Yandroid_w, FALSE, FALSE,
5215 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5218 Yandroid_wB, FALSE, TRUE,
5219 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5222 Yandroid_nw, FALSE, FALSE,
5223 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5226 Yandroid_nwB, FALSE, TRUE,
5227 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5230 Xspring, TRUE, FALSE,
5234 Xspring_pause, FALSE, FALSE,
5238 Xspring_e, FALSE, FALSE,
5242 Xspring_w, FALSE, FALSE,
5246 Xspring_fall, FALSE, FALSE,
5250 Yspring_s, FALSE, FALSE,
5251 EL_SPRING, ACTION_FALLING, -1
5254 Yspring_sB, FALSE, TRUE,
5255 EL_SPRING, ACTION_FALLING, -1
5258 Yspring_e, FALSE, FALSE,
5259 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5262 Yspring_eB, FALSE, TRUE,
5263 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5266 Yspring_w, FALSE, FALSE,
5267 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5270 Yspring_wB, FALSE, TRUE,
5271 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5274 Yspring_kill_e, FALSE, FALSE,
5275 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5278 Yspring_kill_eB, FALSE, TRUE,
5279 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5282 Yspring_kill_w, FALSE, FALSE,
5283 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5286 Yspring_kill_wB, FALSE, TRUE,
5287 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5290 Xeater_n, TRUE, FALSE,
5291 EL_YAMYAM_UP, -1, -1
5294 Xeater_e, TRUE, FALSE,
5295 EL_YAMYAM_RIGHT, -1, -1
5298 Xeater_w, TRUE, FALSE,
5299 EL_YAMYAM_LEFT, -1, -1
5302 Xeater_s, TRUE, FALSE,
5303 EL_YAMYAM_DOWN, -1, -1
5306 Yeater_n, FALSE, FALSE,
5307 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5310 Yeater_nB, FALSE, TRUE,
5311 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5314 Yeater_e, FALSE, FALSE,
5315 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5318 Yeater_eB, FALSE, TRUE,
5319 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5322 Yeater_s, FALSE, FALSE,
5323 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5326 Yeater_sB, FALSE, TRUE,
5327 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5330 Yeater_w, FALSE, FALSE,
5331 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5334 Yeater_wB, FALSE, TRUE,
5335 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5338 Yeater_stone, FALSE, FALSE,
5339 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5342 Yeater_spring, FALSE, FALSE,
5343 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5346 Xalien, TRUE, FALSE,
5350 Xalien_pause, FALSE, FALSE,
5354 Yalien_n, FALSE, FALSE,
5355 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5358 Yalien_nB, FALSE, TRUE,
5359 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5362 Yalien_e, FALSE, FALSE,
5363 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5366 Yalien_eB, FALSE, TRUE,
5367 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5370 Yalien_s, FALSE, FALSE,
5371 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5374 Yalien_sB, FALSE, TRUE,
5375 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5378 Yalien_w, FALSE, FALSE,
5379 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5382 Yalien_wB, FALSE, TRUE,
5383 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5386 Yalien_stone, FALSE, FALSE,
5387 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5390 Yalien_spring, FALSE, FALSE,
5391 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5394 Xemerald, TRUE, FALSE,
5398 Xemerald_pause, FALSE, FALSE,
5402 Xemerald_fall, FALSE, FALSE,
5406 Xemerald_shine, FALSE, FALSE,
5407 EL_EMERALD, ACTION_TWINKLING, -1
5410 Yemerald_s, FALSE, FALSE,
5411 EL_EMERALD, ACTION_FALLING, -1
5414 Yemerald_sB, FALSE, TRUE,
5415 EL_EMERALD, ACTION_FALLING, -1
5418 Yemerald_e, FALSE, FALSE,
5419 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5422 Yemerald_eB, FALSE, TRUE,
5423 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5426 Yemerald_w, FALSE, FALSE,
5427 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5430 Yemerald_wB, FALSE, TRUE,
5431 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5434 Yemerald_eat, FALSE, FALSE,
5435 EL_EMERALD, ACTION_COLLECTING, -1
5438 Yemerald_stone, FALSE, FALSE,
5439 EL_NUT, ACTION_BREAKING, -1
5442 Xdiamond, TRUE, FALSE,
5446 Xdiamond_pause, FALSE, FALSE,
5450 Xdiamond_fall, FALSE, FALSE,
5454 Xdiamond_shine, FALSE, FALSE,
5455 EL_DIAMOND, ACTION_TWINKLING, -1
5458 Ydiamond_s, FALSE, FALSE,
5459 EL_DIAMOND, ACTION_FALLING, -1
5462 Ydiamond_sB, FALSE, TRUE,
5463 EL_DIAMOND, ACTION_FALLING, -1
5466 Ydiamond_e, FALSE, FALSE,
5467 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5470 Ydiamond_eB, FALSE, TRUE,
5471 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5474 Ydiamond_w, FALSE, FALSE,
5475 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5478 Ydiamond_wB, FALSE, TRUE,
5479 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5482 Ydiamond_eat, FALSE, FALSE,
5483 EL_DIAMOND, ACTION_COLLECTING, -1
5486 Ydiamond_stone, FALSE, FALSE,
5487 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5490 Xdrip_fall, TRUE, FALSE,
5491 EL_AMOEBA_DROP, -1, -1
5494 Xdrip_stretch, FALSE, FALSE,
5495 EL_AMOEBA_DROP, ACTION_FALLING, -1
5498 Xdrip_stretchB, FALSE, TRUE,
5499 EL_AMOEBA_DROP, ACTION_FALLING, -1
5502 Xdrip_eat, FALSE, FALSE,
5503 EL_AMOEBA_DROP, ACTION_GROWING, -1
5506 Ydrip_s1, FALSE, FALSE,
5507 EL_AMOEBA_DROP, ACTION_FALLING, -1
5510 Ydrip_s1B, FALSE, TRUE,
5511 EL_AMOEBA_DROP, ACTION_FALLING, -1
5514 Ydrip_s2, FALSE, FALSE,
5515 EL_AMOEBA_DROP, ACTION_FALLING, -1
5518 Ydrip_s2B, FALSE, TRUE,
5519 EL_AMOEBA_DROP, ACTION_FALLING, -1
5526 Xbomb_pause, FALSE, FALSE,
5530 Xbomb_fall, FALSE, FALSE,
5534 Ybomb_s, FALSE, FALSE,
5535 EL_BOMB, ACTION_FALLING, -1
5538 Ybomb_sB, FALSE, TRUE,
5539 EL_BOMB, ACTION_FALLING, -1
5542 Ybomb_e, FALSE, FALSE,
5543 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5546 Ybomb_eB, FALSE, TRUE,
5547 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5550 Ybomb_w, FALSE, FALSE,
5551 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5554 Ybomb_wB, FALSE, TRUE,
5555 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5558 Ybomb_eat, FALSE, FALSE,
5559 EL_BOMB, ACTION_ACTIVATING, -1
5562 Xballoon, TRUE, FALSE,
5566 Yballoon_n, FALSE, FALSE,
5567 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5570 Yballoon_nB, FALSE, TRUE,
5571 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5574 Yballoon_e, FALSE, FALSE,
5575 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5578 Yballoon_eB, FALSE, TRUE,
5579 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5582 Yballoon_s, FALSE, FALSE,
5583 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5586 Yballoon_sB, FALSE, TRUE,
5587 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5590 Yballoon_w, FALSE, FALSE,
5591 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5594 Yballoon_wB, FALSE, TRUE,
5595 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5598 Xgrass, TRUE, FALSE,
5599 EL_EMC_GRASS, -1, -1
5602 Ygrass_nB, FALSE, FALSE,
5603 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5606 Ygrass_eB, FALSE, FALSE,
5607 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5610 Ygrass_sB, FALSE, FALSE,
5611 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5614 Ygrass_wB, FALSE, FALSE,
5615 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5622 Ydirt_nB, FALSE, FALSE,
5623 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5626 Ydirt_eB, FALSE, FALSE,
5627 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5630 Ydirt_sB, FALSE, FALSE,
5631 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5634 Ydirt_wB, FALSE, FALSE,
5635 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5638 Xacid_ne, TRUE, FALSE,
5639 EL_ACID_POOL_TOPRIGHT, -1, -1
5642 Xacid_se, TRUE, FALSE,
5643 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5646 Xacid_s, TRUE, FALSE,
5647 EL_ACID_POOL_BOTTOM, -1, -1
5650 Xacid_sw, TRUE, FALSE,
5651 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5654 Xacid_nw, TRUE, FALSE,
5655 EL_ACID_POOL_TOPLEFT, -1, -1
5658 Xacid_1, TRUE, FALSE,
5662 Xacid_2, FALSE, FALSE,
5666 Xacid_3, FALSE, FALSE,
5670 Xacid_4, FALSE, FALSE,
5674 Xacid_5, FALSE, FALSE,
5678 Xacid_6, FALSE, FALSE,
5682 Xacid_7, FALSE, FALSE,
5686 Xacid_8, FALSE, FALSE,
5690 Xball_1, TRUE, FALSE,
5691 EL_EMC_MAGIC_BALL, -1, -1
5694 Xball_1B, FALSE, FALSE,
5695 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5698 Xball_2, FALSE, FALSE,
5699 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5702 Xball_2B, FALSE, FALSE,
5703 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5706 Yball_eat, FALSE, FALSE,
5707 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5710 Ykey_1_eat, FALSE, FALSE,
5711 EL_EM_KEY_1, ACTION_COLLECTING, -1
5714 Ykey_2_eat, FALSE, FALSE,
5715 EL_EM_KEY_2, ACTION_COLLECTING, -1
5718 Ykey_3_eat, FALSE, FALSE,
5719 EL_EM_KEY_3, ACTION_COLLECTING, -1
5722 Ykey_4_eat, FALSE, FALSE,
5723 EL_EM_KEY_4, ACTION_COLLECTING, -1
5726 Ykey_5_eat, FALSE, FALSE,
5727 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5730 Ykey_6_eat, FALSE, FALSE,
5731 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5734 Ykey_7_eat, FALSE, FALSE,
5735 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5738 Ykey_8_eat, FALSE, FALSE,
5739 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5742 Ylenses_eat, FALSE, FALSE,
5743 EL_EMC_LENSES, ACTION_COLLECTING, -1
5746 Ymagnify_eat, FALSE, FALSE,
5747 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5750 Ygrass_eat, FALSE, FALSE,
5751 EL_EMC_GRASS, ACTION_SNAPPING, -1
5754 Ydirt_eat, FALSE, FALSE,
5755 EL_SAND, ACTION_SNAPPING, -1
5758 Xgrow_ns, TRUE, FALSE,
5759 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5762 Ygrow_ns_eat, FALSE, FALSE,
5763 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5766 Xgrow_ew, TRUE, FALSE,
5767 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5770 Ygrow_ew_eat, FALSE, FALSE,
5771 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5774 Xwonderwall, TRUE, FALSE,
5775 EL_MAGIC_WALL, -1, -1
5778 XwonderwallB, FALSE, FALSE,
5779 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5782 Xamoeba_1, TRUE, FALSE,
5783 EL_AMOEBA_DRY, ACTION_OTHER, -1
5786 Xamoeba_2, FALSE, FALSE,
5787 EL_AMOEBA_DRY, ACTION_OTHER, -1
5790 Xamoeba_3, FALSE, FALSE,
5791 EL_AMOEBA_DRY, ACTION_OTHER, -1
5794 Xamoeba_4, FALSE, FALSE,
5795 EL_AMOEBA_DRY, ACTION_OTHER, -1
5798 Xamoeba_5, TRUE, FALSE,
5799 EL_AMOEBA_WET, ACTION_OTHER, -1
5802 Xamoeba_6, FALSE, FALSE,
5803 EL_AMOEBA_WET, ACTION_OTHER, -1
5806 Xamoeba_7, FALSE, FALSE,
5807 EL_AMOEBA_WET, ACTION_OTHER, -1
5810 Xamoeba_8, FALSE, FALSE,
5811 EL_AMOEBA_WET, ACTION_OTHER, -1
5814 Xdoor_1, TRUE, FALSE,
5815 EL_EM_GATE_1, -1, -1
5818 Xdoor_2, TRUE, FALSE,
5819 EL_EM_GATE_2, -1, -1
5822 Xdoor_3, TRUE, FALSE,
5823 EL_EM_GATE_3, -1, -1
5826 Xdoor_4, TRUE, FALSE,
5827 EL_EM_GATE_4, -1, -1
5830 Xdoor_5, TRUE, FALSE,
5831 EL_EMC_GATE_5, -1, -1
5834 Xdoor_6, TRUE, FALSE,
5835 EL_EMC_GATE_6, -1, -1
5838 Xdoor_7, TRUE, FALSE,
5839 EL_EMC_GATE_7, -1, -1
5842 Xdoor_8, TRUE, FALSE,
5843 EL_EMC_GATE_8, -1, -1
5846 Xkey_1, TRUE, FALSE,
5850 Xkey_2, TRUE, FALSE,
5854 Xkey_3, TRUE, FALSE,
5858 Xkey_4, TRUE, FALSE,
5862 Xkey_5, TRUE, FALSE,
5863 EL_EMC_KEY_5, -1, -1
5866 Xkey_6, TRUE, FALSE,
5867 EL_EMC_KEY_6, -1, -1
5870 Xkey_7, TRUE, FALSE,
5871 EL_EMC_KEY_7, -1, -1
5874 Xkey_8, TRUE, FALSE,
5875 EL_EMC_KEY_8, -1, -1
5878 Xwind_n, TRUE, FALSE,
5879 EL_BALLOON_SWITCH_UP, -1, -1
5882 Xwind_e, TRUE, FALSE,
5883 EL_BALLOON_SWITCH_RIGHT, -1, -1
5886 Xwind_s, TRUE, FALSE,
5887 EL_BALLOON_SWITCH_DOWN, -1, -1
5890 Xwind_w, TRUE, FALSE,
5891 EL_BALLOON_SWITCH_LEFT, -1, -1
5894 Xwind_nesw, TRUE, FALSE,
5895 EL_BALLOON_SWITCH_ANY, -1, -1
5898 Xwind_stop, TRUE, FALSE,
5899 EL_BALLOON_SWITCH_NONE, -1, -1
5903 EL_EM_EXIT_CLOSED, -1, -1
5906 Xexit_1, TRUE, FALSE,
5907 EL_EM_EXIT_OPEN, -1, -1
5910 Xexit_2, FALSE, FALSE,
5911 EL_EM_EXIT_OPEN, -1, -1
5914 Xexit_3, FALSE, FALSE,
5915 EL_EM_EXIT_OPEN, -1, -1
5918 Xdynamite, TRUE, FALSE,
5919 EL_EM_DYNAMITE, -1, -1
5922 Ydynamite_eat, FALSE, FALSE,
5923 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5926 Xdynamite_1, TRUE, FALSE,
5927 EL_EM_DYNAMITE_ACTIVE, -1, -1
5930 Xdynamite_2, FALSE, FALSE,
5931 EL_EM_DYNAMITE_ACTIVE, -1, -1
5934 Xdynamite_3, FALSE, FALSE,
5935 EL_EM_DYNAMITE_ACTIVE, -1, -1
5938 Xdynamite_4, FALSE, FALSE,
5939 EL_EM_DYNAMITE_ACTIVE, -1, -1
5942 Xbumper, TRUE, FALSE,
5943 EL_EMC_SPRING_BUMPER, -1, -1
5946 XbumperB, FALSE, FALSE,
5947 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5950 Xwheel, TRUE, FALSE,
5951 EL_ROBOT_WHEEL, -1, -1
5954 XwheelB, FALSE, FALSE,
5955 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5958 Xswitch, TRUE, FALSE,
5959 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5962 XswitchB, FALSE, FALSE,
5963 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5967 EL_QUICKSAND_EMPTY, -1, -1
5970 Xsand_stone, TRUE, FALSE,
5971 EL_QUICKSAND_FULL, -1, -1
5974 Xsand_stonein_1, FALSE, TRUE,
5975 EL_ROCK, ACTION_FILLING, -1
5978 Xsand_stonein_2, FALSE, TRUE,
5979 EL_ROCK, ACTION_FILLING, -1
5982 Xsand_stonein_3, FALSE, TRUE,
5983 EL_ROCK, ACTION_FILLING, -1
5986 Xsand_stonein_4, FALSE, TRUE,
5987 EL_ROCK, ACTION_FILLING, -1
5990 Xsand_stonesand_1, FALSE, FALSE,
5991 EL_QUICKSAND_EMPTYING, -1, -1
5994 Xsand_stonesand_2, FALSE, FALSE,
5995 EL_QUICKSAND_EMPTYING, -1, -1
5998 Xsand_stonesand_3, FALSE, FALSE,
5999 EL_QUICKSAND_EMPTYING, -1, -1
6002 Xsand_stonesand_4, FALSE, FALSE,
6003 EL_QUICKSAND_EMPTYING, -1, -1
6006 Xsand_stonesand_quickout_1, FALSE, FALSE,
6007 EL_QUICKSAND_EMPTYING, -1, -1
6010 Xsand_stonesand_quickout_2, FALSE, FALSE,
6011 EL_QUICKSAND_EMPTYING, -1, -1
6014 Xsand_stoneout_1, FALSE, FALSE,
6015 EL_ROCK, ACTION_EMPTYING, -1
6018 Xsand_stoneout_2, FALSE, FALSE,
6019 EL_ROCK, ACTION_EMPTYING, -1
6022 Xsand_sandstone_1, FALSE, FALSE,
6023 EL_QUICKSAND_FILLING, -1, -1
6026 Xsand_sandstone_2, FALSE, FALSE,
6027 EL_QUICKSAND_FILLING, -1, -1
6030 Xsand_sandstone_3, FALSE, FALSE,
6031 EL_QUICKSAND_FILLING, -1, -1
6034 Xsand_sandstone_4, FALSE, FALSE,
6035 EL_QUICKSAND_FILLING, -1, -1
6038 Xplant, TRUE, FALSE,
6039 EL_EMC_PLANT, -1, -1
6042 Yplant, FALSE, FALSE,
6043 EL_EMC_PLANT, -1, -1
6046 Xlenses, TRUE, FALSE,
6047 EL_EMC_LENSES, -1, -1
6050 Xmagnify, TRUE, FALSE,
6051 EL_EMC_MAGNIFIER, -1, -1
6054 Xdripper, TRUE, FALSE,
6055 EL_EMC_DRIPPER, -1, -1
6058 XdripperB, FALSE, FALSE,
6059 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
6062 Xfake_blank, TRUE, FALSE,
6063 EL_INVISIBLE_WALL, -1, -1
6066 Xfake_blankB, FALSE, FALSE,
6067 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
6070 Xfake_grass, TRUE, FALSE,
6071 EL_EMC_FAKE_GRASS, -1, -1
6074 Xfake_grassB, FALSE, FALSE,
6075 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
6078 Xfake_door_1, TRUE, FALSE,
6079 EL_EM_GATE_1_GRAY, -1, -1
6082 Xfake_door_2, TRUE, FALSE,
6083 EL_EM_GATE_2_GRAY, -1, -1
6086 Xfake_door_3, TRUE, FALSE,
6087 EL_EM_GATE_3_GRAY, -1, -1
6090 Xfake_door_4, TRUE, FALSE,
6091 EL_EM_GATE_4_GRAY, -1, -1
6094 Xfake_door_5, TRUE, FALSE,
6095 EL_EMC_GATE_5_GRAY, -1, -1
6098 Xfake_door_6, TRUE, FALSE,
6099 EL_EMC_GATE_6_GRAY, -1, -1
6102 Xfake_door_7, TRUE, FALSE,
6103 EL_EMC_GATE_7_GRAY, -1, -1
6106 Xfake_door_8, TRUE, FALSE,
6107 EL_EMC_GATE_8_GRAY, -1, -1
6110 Xfake_acid_1, TRUE, FALSE,
6111 EL_EMC_FAKE_ACID, -1, -1
6114 Xfake_acid_2, FALSE, FALSE,
6115 EL_EMC_FAKE_ACID, -1, -1
6118 Xfake_acid_3, FALSE, FALSE,
6119 EL_EMC_FAKE_ACID, -1, -1
6122 Xfake_acid_4, FALSE, FALSE,
6123 EL_EMC_FAKE_ACID, -1, -1
6126 Xfake_acid_5, FALSE, FALSE,
6127 EL_EMC_FAKE_ACID, -1, -1
6130 Xfake_acid_6, FALSE, FALSE,
6131 EL_EMC_FAKE_ACID, -1, -1
6134 Xfake_acid_7, FALSE, FALSE,
6135 EL_EMC_FAKE_ACID, -1, -1
6138 Xfake_acid_8, FALSE, FALSE,
6139 EL_EMC_FAKE_ACID, -1, -1
6142 Xsteel_1, TRUE, FALSE,
6143 EL_STEELWALL, -1, -1
6146 Xsteel_2, TRUE, FALSE,
6147 EL_EMC_STEELWALL_2, -1, -1
6150 Xsteel_3, TRUE, FALSE,
6151 EL_EMC_STEELWALL_3, -1, -1
6154 Xsteel_4, TRUE, FALSE,
6155 EL_EMC_STEELWALL_4, -1, -1
6158 Xwall_1, TRUE, FALSE,
6162 Xwall_2, TRUE, FALSE,
6163 EL_EMC_WALL_14, -1, -1
6166 Xwall_3, TRUE, FALSE,
6167 EL_EMC_WALL_15, -1, -1
6170 Xwall_4, TRUE, FALSE,
6171 EL_EMC_WALL_16, -1, -1
6174 Xround_wall_1, TRUE, FALSE,
6175 EL_WALL_SLIPPERY, -1, -1
6178 Xround_wall_2, TRUE, FALSE,
6179 EL_EMC_WALL_SLIPPERY_2, -1, -1
6182 Xround_wall_3, TRUE, FALSE,
6183 EL_EMC_WALL_SLIPPERY_3, -1, -1
6186 Xround_wall_4, TRUE, FALSE,
6187 EL_EMC_WALL_SLIPPERY_4, -1, -1
6190 Xdecor_1, TRUE, FALSE,
6191 EL_EMC_WALL_8, -1, -1
6194 Xdecor_2, TRUE, FALSE,
6195 EL_EMC_WALL_6, -1, -1
6198 Xdecor_3, TRUE, FALSE,
6199 EL_EMC_WALL_4, -1, -1
6202 Xdecor_4, TRUE, FALSE,
6203 EL_EMC_WALL_7, -1, -1
6206 Xdecor_5, TRUE, FALSE,
6207 EL_EMC_WALL_5, -1, -1
6210 Xdecor_6, TRUE, FALSE,
6211 EL_EMC_WALL_9, -1, -1
6214 Xdecor_7, TRUE, FALSE,
6215 EL_EMC_WALL_10, -1, -1
6218 Xdecor_8, TRUE, FALSE,
6219 EL_EMC_WALL_1, -1, -1
6222 Xdecor_9, TRUE, FALSE,
6223 EL_EMC_WALL_2, -1, -1
6226 Xdecor_10, TRUE, FALSE,
6227 EL_EMC_WALL_3, -1, -1
6230 Xdecor_11, TRUE, FALSE,
6231 EL_EMC_WALL_11, -1, -1
6234 Xdecor_12, TRUE, FALSE,
6235 EL_EMC_WALL_12, -1, -1
6238 Xalpha_0, TRUE, FALSE,
6239 EL_CHAR('0'), -1, -1
6242 Xalpha_1, TRUE, FALSE,
6243 EL_CHAR('1'), -1, -1
6246 Xalpha_2, TRUE, FALSE,
6247 EL_CHAR('2'), -1, -1
6250 Xalpha_3, TRUE, FALSE,
6251 EL_CHAR('3'), -1, -1
6254 Xalpha_4, TRUE, FALSE,
6255 EL_CHAR('4'), -1, -1
6258 Xalpha_5, TRUE, FALSE,
6259 EL_CHAR('5'), -1, -1
6262 Xalpha_6, TRUE, FALSE,
6263 EL_CHAR('6'), -1, -1
6266 Xalpha_7, TRUE, FALSE,
6267 EL_CHAR('7'), -1, -1
6270 Xalpha_8, TRUE, FALSE,
6271 EL_CHAR('8'), -1, -1
6274 Xalpha_9, TRUE, FALSE,
6275 EL_CHAR('9'), -1, -1
6278 Xalpha_excla, TRUE, FALSE,
6279 EL_CHAR('!'), -1, -1
6282 Xalpha_quote, TRUE, FALSE,
6283 EL_CHAR('"'), -1, -1
6286 Xalpha_comma, TRUE, FALSE,
6287 EL_CHAR(','), -1, -1
6290 Xalpha_minus, TRUE, FALSE,
6291 EL_CHAR('-'), -1, -1
6294 Xalpha_perio, TRUE, FALSE,
6295 EL_CHAR('.'), -1, -1
6298 Xalpha_colon, TRUE, FALSE,
6299 EL_CHAR(':'), -1, -1
6302 Xalpha_quest, TRUE, FALSE,
6303 EL_CHAR('?'), -1, -1
6306 Xalpha_a, TRUE, FALSE,
6307 EL_CHAR('A'), -1, -1
6310 Xalpha_b, TRUE, FALSE,
6311 EL_CHAR('B'), -1, -1
6314 Xalpha_c, TRUE, FALSE,
6315 EL_CHAR('C'), -1, -1
6318 Xalpha_d, TRUE, FALSE,
6319 EL_CHAR('D'), -1, -1
6322 Xalpha_e, TRUE, FALSE,
6323 EL_CHAR('E'), -1, -1
6326 Xalpha_f, TRUE, FALSE,
6327 EL_CHAR('F'), -1, -1
6330 Xalpha_g, TRUE, FALSE,
6331 EL_CHAR('G'), -1, -1
6334 Xalpha_h, TRUE, FALSE,
6335 EL_CHAR('H'), -1, -1
6338 Xalpha_i, TRUE, FALSE,
6339 EL_CHAR('I'), -1, -1
6342 Xalpha_j, TRUE, FALSE,
6343 EL_CHAR('J'), -1, -1
6346 Xalpha_k, TRUE, FALSE,
6347 EL_CHAR('K'), -1, -1
6350 Xalpha_l, TRUE, FALSE,
6351 EL_CHAR('L'), -1, -1
6354 Xalpha_m, TRUE, FALSE,
6355 EL_CHAR('M'), -1, -1
6358 Xalpha_n, TRUE, FALSE,
6359 EL_CHAR('N'), -1, -1
6362 Xalpha_o, TRUE, FALSE,
6363 EL_CHAR('O'), -1, -1
6366 Xalpha_p, TRUE, FALSE,
6367 EL_CHAR('P'), -1, -1
6370 Xalpha_q, TRUE, FALSE,
6371 EL_CHAR('Q'), -1, -1
6374 Xalpha_r, TRUE, FALSE,
6375 EL_CHAR('R'), -1, -1
6378 Xalpha_s, TRUE, FALSE,
6379 EL_CHAR('S'), -1, -1
6382 Xalpha_t, TRUE, FALSE,
6383 EL_CHAR('T'), -1, -1
6386 Xalpha_u, TRUE, FALSE,
6387 EL_CHAR('U'), -1, -1
6390 Xalpha_v, TRUE, FALSE,
6391 EL_CHAR('V'), -1, -1
6394 Xalpha_w, TRUE, FALSE,
6395 EL_CHAR('W'), -1, -1
6398 Xalpha_x, TRUE, FALSE,
6399 EL_CHAR('X'), -1, -1
6402 Xalpha_y, TRUE, FALSE,
6403 EL_CHAR('Y'), -1, -1
6406 Xalpha_z, TRUE, FALSE,
6407 EL_CHAR('Z'), -1, -1
6410 Xalpha_arrow_e, TRUE, FALSE,
6411 EL_CHAR('>'), -1, -1
6414 Xalpha_arrow_w, TRUE, FALSE,
6415 EL_CHAR('<'), -1, -1
6418 Xalpha_copyr, TRUE, FALSE,
6419 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6423 Xboom_bug, FALSE, FALSE,
6424 EL_BUG, ACTION_EXPLODING, -1
6427 Xboom_bomb, FALSE, FALSE,
6428 EL_BOMB, ACTION_EXPLODING, -1
6431 Xboom_android, FALSE, FALSE,
6432 EL_EMC_ANDROID, ACTION_OTHER, -1
6435 Xboom_1, FALSE, FALSE,
6436 EL_DEFAULT, ACTION_EXPLODING, -1
6439 Xboom_2, FALSE, FALSE,
6440 EL_DEFAULT, ACTION_EXPLODING, -1
6443 Znormal, FALSE, FALSE,
6447 Zdynamite, FALSE, FALSE,
6451 Zplayer, FALSE, FALSE,
6455 ZBORDER, FALSE, FALSE,
6465 static struct Mapping_EM_to_RND_player
6474 em_player_mapping_list[] =
6478 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6482 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6486 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6490 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6494 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6498 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6502 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6506 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6510 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6514 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6518 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6522 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6526 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6530 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6534 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6538 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6542 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6546 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6550 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6554 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6558 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6562 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6566 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6570 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6574 EL_PLAYER_1, ACTION_DEFAULT, -1,
6578 EL_PLAYER_2, ACTION_DEFAULT, -1,
6582 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6586 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6590 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6594 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6598 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6602 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6606 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6610 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6614 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6618 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6622 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6626 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6630 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6634 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6638 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6642 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6646 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6650 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6654 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6658 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6662 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6666 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6670 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6674 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6678 EL_PLAYER_3, ACTION_DEFAULT, -1,
6682 EL_PLAYER_4, ACTION_DEFAULT, -1,
6691 int map_element_RND_to_EM(int element_rnd)
6693 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6694 static boolean mapping_initialized = FALSE;
6696 if (!mapping_initialized)
6700 /* return "Xalpha_quest" for all undefined elements in mapping array */
6701 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6702 mapping_RND_to_EM[i] = Xalpha_quest;
6704 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6705 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6706 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6707 em_object_mapping_list[i].element_em;
6709 mapping_initialized = TRUE;
6712 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6713 return mapping_RND_to_EM[element_rnd];
6715 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6720 int map_element_EM_to_RND(int element_em)
6722 static unsigned short mapping_EM_to_RND[TILE_MAX];
6723 static boolean mapping_initialized = FALSE;
6725 if (!mapping_initialized)
6729 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6730 for (i = 0; i < TILE_MAX; i++)
6731 mapping_EM_to_RND[i] = EL_UNKNOWN;
6733 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6734 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6735 em_object_mapping_list[i].element_rnd;
6737 mapping_initialized = TRUE;
6740 if (element_em >= 0 && element_em < TILE_MAX)
6741 return mapping_EM_to_RND[element_em];
6743 Error(ERR_WARN, "invalid EM level element %d", element_em);
6748 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6750 struct LevelInfo_EM *level_em = level->native_em_level;
6751 struct LEVEL *lev = level_em->lev;
6754 for (i = 0; i < TILE_MAX; i++)
6755 lev->android_array[i] = Xblank;
6757 for (i = 0; i < level->num_android_clone_elements; i++)
6759 int element_rnd = level->android_clone_element[i];
6760 int element_em = map_element_RND_to_EM(element_rnd);
6762 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6763 if (em_object_mapping_list[j].element_rnd == element_rnd)
6764 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6768 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6770 struct LevelInfo_EM *level_em = level->native_em_level;
6771 struct LEVEL *lev = level_em->lev;
6774 level->num_android_clone_elements = 0;
6776 for (i = 0; i < TILE_MAX; i++)
6778 int element_em = lev->android_array[i];
6780 boolean element_found = FALSE;
6782 if (element_em == Xblank)
6785 element_rnd = map_element_EM_to_RND(element_em);
6787 for (j = 0; j < level->num_android_clone_elements; j++)
6788 if (level->android_clone_element[j] == element_rnd)
6789 element_found = TRUE;
6793 level->android_clone_element[level->num_android_clone_elements++] =
6796 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6801 if (level->num_android_clone_elements == 0)
6803 level->num_android_clone_elements = 1;
6804 level->android_clone_element[0] = EL_EMPTY;
6808 int map_direction_RND_to_EM(int direction)
6810 return (direction == MV_UP ? 0 :
6811 direction == MV_RIGHT ? 1 :
6812 direction == MV_DOWN ? 2 :
6813 direction == MV_LEFT ? 3 :
6817 int map_direction_EM_to_RND(int direction)
6819 return (direction == 0 ? MV_UP :
6820 direction == 1 ? MV_RIGHT :
6821 direction == 2 ? MV_DOWN :
6822 direction == 3 ? MV_LEFT :
6826 int map_element_RND_to_SP(int element_rnd)
6828 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6830 if (element_rnd >= EL_SP_START &&
6831 element_rnd <= EL_SP_END)
6832 element_sp = element_rnd - EL_SP_START;
6833 else if (element_rnd == EL_EMPTY_SPACE)
6835 else if (element_rnd == EL_INVISIBLE_WALL)
6841 int map_element_SP_to_RND(int element_sp)
6843 int element_rnd = EL_UNKNOWN;
6845 if (element_sp >= 0x00 &&
6847 element_rnd = EL_SP_START + element_sp;
6848 else if (element_sp == 0x28)
6849 element_rnd = EL_INVISIBLE_WALL;
6854 int map_action_SP_to_RND(int action_sp)
6858 case actActive: return ACTION_ACTIVE;
6859 case actImpact: return ACTION_IMPACT;
6860 case actExploding: return ACTION_EXPLODING;
6861 case actDigging: return ACTION_DIGGING;
6862 case actSnapping: return ACTION_SNAPPING;
6863 case actCollecting: return ACTION_COLLECTING;
6864 case actPassing: return ACTION_PASSING;
6865 case actPushing: return ACTION_PUSHING;
6866 case actDropping: return ACTION_DROPPING;
6868 default: return ACTION_DEFAULT;
6872 int get_next_element(int element)
6876 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6877 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6878 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6879 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6880 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6881 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6882 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6883 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6884 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6885 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6886 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6888 default: return element;
6892 int el_act_dir2img(int element, int action, int direction)
6894 element = GFX_ELEMENT(element);
6895 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6897 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6898 return element_info[element].direction_graphic[action][direction];
6901 static int el_act_dir2crm(int element, int action, int direction)
6903 element = GFX_ELEMENT(element);
6904 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6906 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6907 return element_info[element].direction_crumbled[action][direction];
6910 int el_act2img(int element, int action)
6912 element = GFX_ELEMENT(element);
6914 return element_info[element].graphic[action];
6917 int el_act2crm(int element, int action)
6919 element = GFX_ELEMENT(element);
6921 return element_info[element].crumbled[action];
6924 int el_dir2img(int element, int direction)
6926 element = GFX_ELEMENT(element);
6928 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6931 int el2baseimg(int element)
6933 return element_info[element].graphic[ACTION_DEFAULT];
6936 int el2img(int element)
6938 element = GFX_ELEMENT(element);
6940 return element_info[element].graphic[ACTION_DEFAULT];
6943 int el2edimg(int element)
6945 element = GFX_ELEMENT(element);
6947 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6950 int el2preimg(int element)
6952 element = GFX_ELEMENT(element);
6954 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6957 int el2panelimg(int element)
6959 element = GFX_ELEMENT(element);
6961 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6964 int font2baseimg(int font_nr)
6966 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6969 int getBeltNrFromBeltElement(int element)
6971 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6972 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6973 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6976 int getBeltNrFromBeltActiveElement(int element)
6978 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6979 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6980 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6983 int getBeltNrFromBeltSwitchElement(int element)
6985 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6986 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6987 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6990 int getBeltDirNrFromBeltElement(int element)
6992 static int belt_base_element[4] =
6994 EL_CONVEYOR_BELT_1_LEFT,
6995 EL_CONVEYOR_BELT_2_LEFT,
6996 EL_CONVEYOR_BELT_3_LEFT,
6997 EL_CONVEYOR_BELT_4_LEFT
7000 int belt_nr = getBeltNrFromBeltElement(element);
7001 int belt_dir_nr = element - belt_base_element[belt_nr];
7003 return (belt_dir_nr % 3);
7006 int getBeltDirNrFromBeltSwitchElement(int element)
7008 static int belt_base_element[4] =
7010 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7011 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7012 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7013 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7016 int belt_nr = getBeltNrFromBeltSwitchElement(element);
7017 int belt_dir_nr = element - belt_base_element[belt_nr];
7019 return (belt_dir_nr % 3);
7022 int getBeltDirFromBeltElement(int element)
7024 static int belt_move_dir[3] =
7031 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
7033 return belt_move_dir[belt_dir_nr];
7036 int getBeltDirFromBeltSwitchElement(int element)
7038 static int belt_move_dir[3] =
7045 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
7047 return belt_move_dir[belt_dir_nr];
7050 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7052 static int belt_base_element[4] =
7054 EL_CONVEYOR_BELT_1_LEFT,
7055 EL_CONVEYOR_BELT_2_LEFT,
7056 EL_CONVEYOR_BELT_3_LEFT,
7057 EL_CONVEYOR_BELT_4_LEFT
7060 return belt_base_element[belt_nr] + belt_dir_nr;
7063 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7065 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7067 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7070 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
7072 static int belt_base_element[4] =
7074 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
7075 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
7076 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
7077 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7080 return belt_base_element[belt_nr] + belt_dir_nr;
7083 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7085 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7087 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7090 boolean getTeamMode_EM()
7092 return game.team_mode;
7095 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7097 int game_frame_delay_value;
7099 game_frame_delay_value =
7100 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7101 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7104 if (tape.playing && tape.warp_forward && !tape.pausing)
7105 game_frame_delay_value = 0;
7107 return game_frame_delay_value;
7110 unsigned int InitRND(int seed)
7112 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7113 return InitEngineRandom_EM(seed);
7114 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7115 return InitEngineRandom_SP(seed);
7117 return InitEngineRandom_RND(seed);
7120 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7121 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7123 inline static int get_effective_element_EM(int tile, int frame_em)
7125 int element = object_mapping[tile].element_rnd;
7126 int action = object_mapping[tile].action;
7127 boolean is_backside = object_mapping[tile].is_backside;
7128 boolean action_removing = (action == ACTION_DIGGING ||
7129 action == ACTION_SNAPPING ||
7130 action == ACTION_COLLECTING);
7136 case Yacid_splash_eB:
7137 case Yacid_splash_wB:
7138 return (frame_em > 5 ? EL_EMPTY : element);
7144 else /* frame_em == 7 */
7148 case Yacid_splash_eB:
7149 case Yacid_splash_wB:
7152 case Yemerald_stone:
7155 case Ydiamond_stone:
7159 case Xdrip_stretchB:
7178 case Xsand_stonein_1:
7179 case Xsand_stonein_2:
7180 case Xsand_stonein_3:
7181 case Xsand_stonein_4:
7185 return (is_backside || action_removing ? EL_EMPTY : element);
7190 inline static boolean check_linear_animation_EM(int tile)
7194 case Xsand_stonesand_1:
7195 case Xsand_stonesand_quickout_1:
7196 case Xsand_sandstone_1:
7197 case Xsand_stonein_1:
7198 case Xsand_stoneout_1:
7217 case Yacid_splash_eB:
7218 case Yacid_splash_wB:
7219 case Yemerald_stone:
7226 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7227 boolean has_crumbled_graphics,
7228 int crumbled, int sync_frame)
7230 /* if element can be crumbled, but certain action graphics are just empty
7231 space (like instantly snapping sand to empty space in 1 frame), do not
7232 treat these empty space graphics as crumbled graphics in EMC engine */
7233 if (crumbled == IMG_EMPTY_SPACE)
7234 has_crumbled_graphics = FALSE;
7236 if (has_crumbled_graphics)
7238 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7239 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7240 g_crumbled->anim_delay,
7241 g_crumbled->anim_mode,
7242 g_crumbled->anim_start_frame,
7245 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7246 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7248 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7250 g_em->has_crumbled_graphics = TRUE;
7254 g_em->crumbled_bitmap = NULL;
7255 g_em->crumbled_src_x = 0;
7256 g_em->crumbled_src_y = 0;
7257 g_em->crumbled_border_size = 0;
7259 g_em->has_crumbled_graphics = FALSE;
7263 void ResetGfxAnimation_EM(int x, int y, int tile)
7268 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7269 int tile, int frame_em, int x, int y)
7271 int action = object_mapping[tile].action;
7272 int direction = object_mapping[tile].direction;
7273 int effective_element = get_effective_element_EM(tile, frame_em);
7274 int graphic = (direction == MV_NONE ?
7275 el_act2img(effective_element, action) :
7276 el_act_dir2img(effective_element, action, direction));
7277 struct GraphicInfo *g = &graphic_info[graphic];
7279 boolean action_removing = (action == ACTION_DIGGING ||
7280 action == ACTION_SNAPPING ||
7281 action == ACTION_COLLECTING);
7282 boolean action_moving = (action == ACTION_FALLING ||
7283 action == ACTION_MOVING ||
7284 action == ACTION_PUSHING ||
7285 action == ACTION_EATING ||
7286 action == ACTION_FILLING ||
7287 action == ACTION_EMPTYING);
7288 boolean action_falling = (action == ACTION_FALLING ||
7289 action == ACTION_FILLING ||
7290 action == ACTION_EMPTYING);
7292 /* special case: graphic uses "2nd movement tile" and has defined
7293 7 frames for movement animation (or less) => use default graphic
7294 for last (8th) frame which ends the movement animation */
7295 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7297 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7298 graphic = (direction == MV_NONE ?
7299 el_act2img(effective_element, action) :
7300 el_act_dir2img(effective_element, action, direction));
7302 g = &graphic_info[graphic];
7305 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7309 else if (action_moving)
7311 boolean is_backside = object_mapping[tile].is_backside;
7315 int direction = object_mapping[tile].direction;
7316 int move_dir = (action_falling ? MV_DOWN : direction);
7321 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7322 if (g->double_movement && frame_em == 0)
7326 if (move_dir == MV_LEFT)
7327 GfxFrame[x - 1][y] = GfxFrame[x][y];
7328 else if (move_dir == MV_RIGHT)
7329 GfxFrame[x + 1][y] = GfxFrame[x][y];
7330 else if (move_dir == MV_UP)
7331 GfxFrame[x][y - 1] = GfxFrame[x][y];
7332 else if (move_dir == MV_DOWN)
7333 GfxFrame[x][y + 1] = GfxFrame[x][y];
7340 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7341 if (tile == Xsand_stonesand_quickout_1 ||
7342 tile == Xsand_stonesand_quickout_2)
7346 if (graphic_info[graphic].anim_global_sync)
7347 sync_frame = FrameCounter;
7348 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7349 sync_frame = GfxFrame[x][y];
7351 sync_frame = 0; /* playfield border (pseudo steel) */
7353 SetRandomAnimationValue(x, y);
7355 int frame = getAnimationFrame(g->anim_frames,
7358 g->anim_start_frame,
7361 g_em->unique_identifier =
7362 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7365 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7366 int tile, int frame_em, int x, int y)
7368 int action = object_mapping[tile].action;
7369 int direction = object_mapping[tile].direction;
7370 boolean is_backside = object_mapping[tile].is_backside;
7371 int effective_element = get_effective_element_EM(tile, frame_em);
7372 int effective_action = action;
7373 int graphic = (direction == MV_NONE ?
7374 el_act2img(effective_element, effective_action) :
7375 el_act_dir2img(effective_element, effective_action,
7377 int crumbled = (direction == MV_NONE ?
7378 el_act2crm(effective_element, effective_action) :
7379 el_act_dir2crm(effective_element, effective_action,
7381 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7382 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7383 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7384 struct GraphicInfo *g = &graphic_info[graphic];
7387 /* special case: graphic uses "2nd movement tile" and has defined
7388 7 frames for movement animation (or less) => use default graphic
7389 for last (8th) frame which ends the movement animation */
7390 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7392 effective_action = ACTION_DEFAULT;
7393 graphic = (direction == MV_NONE ?
7394 el_act2img(effective_element, effective_action) :
7395 el_act_dir2img(effective_element, effective_action,
7397 crumbled = (direction == MV_NONE ?
7398 el_act2crm(effective_element, effective_action) :
7399 el_act_dir2crm(effective_element, effective_action,
7402 g = &graphic_info[graphic];
7405 if (graphic_info[graphic].anim_global_sync)
7406 sync_frame = FrameCounter;
7407 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7408 sync_frame = GfxFrame[x][y];
7410 sync_frame = 0; /* playfield border (pseudo steel) */
7412 SetRandomAnimationValue(x, y);
7414 int frame = getAnimationFrame(g->anim_frames,
7417 g->anim_start_frame,
7420 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7421 g->double_movement && is_backside);
7423 /* (updating the "crumbled" graphic definitions is probably not really needed,
7424 as animations for crumbled graphics can't be longer than one EMC cycle) */
7425 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7429 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7430 int player_nr, int anim, int frame_em)
7432 int element = player_mapping[player_nr][anim].element_rnd;
7433 int action = player_mapping[player_nr][anim].action;
7434 int direction = player_mapping[player_nr][anim].direction;
7435 int graphic = (direction == MV_NONE ?
7436 el_act2img(element, action) :
7437 el_act_dir2img(element, action, direction));
7438 struct GraphicInfo *g = &graphic_info[graphic];
7441 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7443 stored_player[player_nr].StepFrame = frame_em;
7445 sync_frame = stored_player[player_nr].Frame;
7447 int frame = getAnimationFrame(g->anim_frames,
7450 g->anim_start_frame,
7453 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7454 &g_em->src_x, &g_em->src_y, FALSE);
7457 void InitGraphicInfo_EM(void)
7462 int num_em_gfx_errors = 0;
7464 if (graphic_info_em_object[0][0].bitmap == NULL)
7466 /* EM graphics not yet initialized in em_open_all() */
7471 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7474 /* always start with reliable default values */
7475 for (i = 0; i < TILE_MAX; i++)
7477 object_mapping[i].element_rnd = EL_UNKNOWN;
7478 object_mapping[i].is_backside = FALSE;
7479 object_mapping[i].action = ACTION_DEFAULT;
7480 object_mapping[i].direction = MV_NONE;
7483 /* always start with reliable default values */
7484 for (p = 0; p < MAX_PLAYERS; p++)
7486 for (i = 0; i < SPR_MAX; i++)
7488 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7489 player_mapping[p][i].action = ACTION_DEFAULT;
7490 player_mapping[p][i].direction = MV_NONE;
7494 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7496 int e = em_object_mapping_list[i].element_em;
7498 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7499 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7501 if (em_object_mapping_list[i].action != -1)
7502 object_mapping[e].action = em_object_mapping_list[i].action;
7504 if (em_object_mapping_list[i].direction != -1)
7505 object_mapping[e].direction =
7506 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7509 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7511 int a = em_player_mapping_list[i].action_em;
7512 int p = em_player_mapping_list[i].player_nr;
7514 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7516 if (em_player_mapping_list[i].action != -1)
7517 player_mapping[p][a].action = em_player_mapping_list[i].action;
7519 if (em_player_mapping_list[i].direction != -1)
7520 player_mapping[p][a].direction =
7521 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7524 for (i = 0; i < TILE_MAX; i++)
7526 int element = object_mapping[i].element_rnd;
7527 int action = object_mapping[i].action;
7528 int direction = object_mapping[i].direction;
7529 boolean is_backside = object_mapping[i].is_backside;
7530 boolean action_exploding = ((action == ACTION_EXPLODING ||
7531 action == ACTION_SMASHED_BY_ROCK ||
7532 action == ACTION_SMASHED_BY_SPRING) &&
7533 element != EL_DIAMOND);
7534 boolean action_active = (action == ACTION_ACTIVE);
7535 boolean action_other = (action == ACTION_OTHER);
7537 for (j = 0; j < 8; j++)
7539 int effective_element = get_effective_element_EM(i, j);
7540 int effective_action = (j < 7 ? action :
7541 i == Xdrip_stretch ? action :
7542 i == Xdrip_stretchB ? action :
7543 i == Ydrip_s1 ? action :
7544 i == Ydrip_s1B ? action :
7545 i == Xball_1B ? action :
7546 i == Xball_2 ? action :
7547 i == Xball_2B ? action :
7548 i == Yball_eat ? action :
7549 i == Ykey_1_eat ? action :
7550 i == Ykey_2_eat ? action :
7551 i == Ykey_3_eat ? action :
7552 i == Ykey_4_eat ? action :
7553 i == Ykey_5_eat ? action :
7554 i == Ykey_6_eat ? action :
7555 i == Ykey_7_eat ? action :
7556 i == Ykey_8_eat ? action :
7557 i == Ylenses_eat ? action :
7558 i == Ymagnify_eat ? action :
7559 i == Ygrass_eat ? action :
7560 i == Ydirt_eat ? action :
7561 i == Xsand_stonein_1 ? action :
7562 i == Xsand_stonein_2 ? action :
7563 i == Xsand_stonein_3 ? action :
7564 i == Xsand_stonein_4 ? action :
7565 i == Xsand_stoneout_1 ? action :
7566 i == Xsand_stoneout_2 ? action :
7567 i == Xboom_android ? ACTION_EXPLODING :
7568 action_exploding ? ACTION_EXPLODING :
7569 action_active ? action :
7570 action_other ? action :
7572 int graphic = (el_act_dir2img(effective_element, effective_action,
7574 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7576 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7577 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7578 boolean has_action_graphics = (graphic != base_graphic);
7579 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7580 struct GraphicInfo *g = &graphic_info[graphic];
7581 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7584 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7585 boolean special_animation = (action != ACTION_DEFAULT &&
7586 g->anim_frames == 3 &&
7587 g->anim_delay == 2 &&
7588 g->anim_mode & ANIM_LINEAR);
7589 int sync_frame = (i == Xdrip_stretch ? 7 :
7590 i == Xdrip_stretchB ? 7 :
7591 i == Ydrip_s2 ? j + 8 :
7592 i == Ydrip_s2B ? j + 8 :
7601 i == Xfake_acid_1 ? 0 :
7602 i == Xfake_acid_2 ? 10 :
7603 i == Xfake_acid_3 ? 20 :
7604 i == Xfake_acid_4 ? 30 :
7605 i == Xfake_acid_5 ? 40 :
7606 i == Xfake_acid_6 ? 50 :
7607 i == Xfake_acid_7 ? 60 :
7608 i == Xfake_acid_8 ? 70 :
7610 i == Xball_2B ? j + 8 :
7611 i == Yball_eat ? j + 1 :
7612 i == Ykey_1_eat ? j + 1 :
7613 i == Ykey_2_eat ? j + 1 :
7614 i == Ykey_3_eat ? j + 1 :
7615 i == Ykey_4_eat ? j + 1 :
7616 i == Ykey_5_eat ? j + 1 :
7617 i == Ykey_6_eat ? j + 1 :
7618 i == Ykey_7_eat ? j + 1 :
7619 i == Ykey_8_eat ? j + 1 :
7620 i == Ylenses_eat ? j + 1 :
7621 i == Ymagnify_eat ? j + 1 :
7622 i == Ygrass_eat ? j + 1 :
7623 i == Ydirt_eat ? j + 1 :
7624 i == Xamoeba_1 ? 0 :
7625 i == Xamoeba_2 ? 1 :
7626 i == Xamoeba_3 ? 2 :
7627 i == Xamoeba_4 ? 3 :
7628 i == Xamoeba_5 ? 0 :
7629 i == Xamoeba_6 ? 1 :
7630 i == Xamoeba_7 ? 2 :
7631 i == Xamoeba_8 ? 3 :
7632 i == Xexit_2 ? j + 8 :
7633 i == Xexit_3 ? j + 16 :
7634 i == Xdynamite_1 ? 0 :
7635 i == Xdynamite_2 ? 8 :
7636 i == Xdynamite_3 ? 16 :
7637 i == Xdynamite_4 ? 24 :
7638 i == Xsand_stonein_1 ? j + 1 :
7639 i == Xsand_stonein_2 ? j + 9 :
7640 i == Xsand_stonein_3 ? j + 17 :
7641 i == Xsand_stonein_4 ? j + 25 :
7642 i == Xsand_stoneout_1 && j == 0 ? 0 :
7643 i == Xsand_stoneout_1 && j == 1 ? 0 :
7644 i == Xsand_stoneout_1 && j == 2 ? 1 :
7645 i == Xsand_stoneout_1 && j == 3 ? 2 :
7646 i == Xsand_stoneout_1 && j == 4 ? 2 :
7647 i == Xsand_stoneout_1 && j == 5 ? 3 :
7648 i == Xsand_stoneout_1 && j == 6 ? 4 :
7649 i == Xsand_stoneout_1 && j == 7 ? 4 :
7650 i == Xsand_stoneout_2 && j == 0 ? 5 :
7651 i == Xsand_stoneout_2 && j == 1 ? 6 :
7652 i == Xsand_stoneout_2 && j == 2 ? 7 :
7653 i == Xsand_stoneout_2 && j == 3 ? 8 :
7654 i == Xsand_stoneout_2 && j == 4 ? 9 :
7655 i == Xsand_stoneout_2 && j == 5 ? 11 :
7656 i == Xsand_stoneout_2 && j == 6 ? 13 :
7657 i == Xsand_stoneout_2 && j == 7 ? 15 :
7658 i == Xboom_bug && j == 1 ? 2 :
7659 i == Xboom_bug && j == 2 ? 2 :
7660 i == Xboom_bug && j == 3 ? 4 :
7661 i == Xboom_bug && j == 4 ? 4 :
7662 i == Xboom_bug && j == 5 ? 2 :
7663 i == Xboom_bug && j == 6 ? 2 :
7664 i == Xboom_bug && j == 7 ? 0 :
7665 i == Xboom_bomb && j == 1 ? 2 :
7666 i == Xboom_bomb && j == 2 ? 2 :
7667 i == Xboom_bomb && j == 3 ? 4 :
7668 i == Xboom_bomb && j == 4 ? 4 :
7669 i == Xboom_bomb && j == 5 ? 2 :
7670 i == Xboom_bomb && j == 6 ? 2 :
7671 i == Xboom_bomb && j == 7 ? 0 :
7672 i == Xboom_android && j == 7 ? 6 :
7673 i == Xboom_1 && j == 1 ? 2 :
7674 i == Xboom_1 && j == 2 ? 2 :
7675 i == Xboom_1 && j == 3 ? 4 :
7676 i == Xboom_1 && j == 4 ? 4 :
7677 i == Xboom_1 && j == 5 ? 6 :
7678 i == Xboom_1 && j == 6 ? 6 :
7679 i == Xboom_1 && j == 7 ? 8 :
7680 i == Xboom_2 && j == 0 ? 8 :
7681 i == Xboom_2 && j == 1 ? 8 :
7682 i == Xboom_2 && j == 2 ? 10 :
7683 i == Xboom_2 && j == 3 ? 10 :
7684 i == Xboom_2 && j == 4 ? 10 :
7685 i == Xboom_2 && j == 5 ? 12 :
7686 i == Xboom_2 && j == 6 ? 12 :
7687 i == Xboom_2 && j == 7 ? 12 :
7688 special_animation && j == 4 ? 3 :
7689 effective_action != action ? 0 :
7693 Bitmap *debug_bitmap = g_em->bitmap;
7694 int debug_src_x = g_em->src_x;
7695 int debug_src_y = g_em->src_y;
7698 int frame = getAnimationFrame(g->anim_frames,
7701 g->anim_start_frame,
7704 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7705 g->double_movement && is_backside);
7707 g_em->bitmap = src_bitmap;
7708 g_em->src_x = src_x;
7709 g_em->src_y = src_y;
7710 g_em->src_offset_x = 0;
7711 g_em->src_offset_y = 0;
7712 g_em->dst_offset_x = 0;
7713 g_em->dst_offset_y = 0;
7714 g_em->width = TILEX;
7715 g_em->height = TILEY;
7717 g_em->preserve_background = FALSE;
7719 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7722 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7723 effective_action == ACTION_MOVING ||
7724 effective_action == ACTION_PUSHING ||
7725 effective_action == ACTION_EATING)) ||
7726 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7727 effective_action == ACTION_EMPTYING)))
7730 (effective_action == ACTION_FALLING ||
7731 effective_action == ACTION_FILLING ||
7732 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7733 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7734 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7735 int num_steps = (i == Ydrip_s1 ? 16 :
7736 i == Ydrip_s1B ? 16 :
7737 i == Ydrip_s2 ? 16 :
7738 i == Ydrip_s2B ? 16 :
7739 i == Xsand_stonein_1 ? 32 :
7740 i == Xsand_stonein_2 ? 32 :
7741 i == Xsand_stonein_3 ? 32 :
7742 i == Xsand_stonein_4 ? 32 :
7743 i == Xsand_stoneout_1 ? 16 :
7744 i == Xsand_stoneout_2 ? 16 : 8);
7745 int cx = ABS(dx) * (TILEX / num_steps);
7746 int cy = ABS(dy) * (TILEY / num_steps);
7747 int step_frame = (i == Ydrip_s2 ? j + 8 :
7748 i == Ydrip_s2B ? j + 8 :
7749 i == Xsand_stonein_2 ? j + 8 :
7750 i == Xsand_stonein_3 ? j + 16 :
7751 i == Xsand_stonein_4 ? j + 24 :
7752 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7753 int step = (is_backside ? step_frame : num_steps - step_frame);
7755 if (is_backside) /* tile where movement starts */
7757 if (dx < 0 || dy < 0)
7759 g_em->src_offset_x = cx * step;
7760 g_em->src_offset_y = cy * step;
7764 g_em->dst_offset_x = cx * step;
7765 g_em->dst_offset_y = cy * step;
7768 else /* tile where movement ends */
7770 if (dx < 0 || dy < 0)
7772 g_em->dst_offset_x = cx * step;
7773 g_em->dst_offset_y = cy * step;
7777 g_em->src_offset_x = cx * step;
7778 g_em->src_offset_y = cy * step;
7782 g_em->width = TILEX - cx * step;
7783 g_em->height = TILEY - cy * step;
7786 /* create unique graphic identifier to decide if tile must be redrawn */
7787 /* bit 31 - 16 (16 bit): EM style graphic
7788 bit 15 - 12 ( 4 bit): EM style frame
7789 bit 11 - 6 ( 6 bit): graphic width
7790 bit 5 - 0 ( 6 bit): graphic height */
7791 g_em->unique_identifier =
7792 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7796 /* skip check for EMC elements not contained in original EMC artwork */
7797 if (element == EL_EMC_FAKE_ACID)
7800 if (g_em->bitmap != debug_bitmap ||
7801 g_em->src_x != debug_src_x ||
7802 g_em->src_y != debug_src_y ||
7803 g_em->src_offset_x != 0 ||
7804 g_em->src_offset_y != 0 ||
7805 g_em->dst_offset_x != 0 ||
7806 g_em->dst_offset_y != 0 ||
7807 g_em->width != TILEX ||
7808 g_em->height != TILEY)
7810 static int last_i = -1;
7818 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7819 i, element, element_info[element].token_name,
7820 element_action_info[effective_action].suffix, direction);
7822 if (element != effective_element)
7823 printf(" [%d ('%s')]",
7825 element_info[effective_element].token_name);
7829 if (g_em->bitmap != debug_bitmap)
7830 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7831 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7833 if (g_em->src_x != debug_src_x ||
7834 g_em->src_y != debug_src_y)
7835 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7836 j, (is_backside ? 'B' : 'F'),
7837 g_em->src_x, g_em->src_y,
7838 g_em->src_x / 32, g_em->src_y / 32,
7839 debug_src_x, debug_src_y,
7840 debug_src_x / 32, debug_src_y / 32);
7842 if (g_em->src_offset_x != 0 ||
7843 g_em->src_offset_y != 0 ||
7844 g_em->dst_offset_x != 0 ||
7845 g_em->dst_offset_y != 0)
7846 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7848 g_em->src_offset_x, g_em->src_offset_y,
7849 g_em->dst_offset_x, g_em->dst_offset_y);
7851 if (g_em->width != TILEX ||
7852 g_em->height != TILEY)
7853 printf(" %d (%d): size %d,%d should be %d,%d\n",
7855 g_em->width, g_em->height, TILEX, TILEY);
7857 num_em_gfx_errors++;
7864 for (i = 0; i < TILE_MAX; i++)
7866 for (j = 0; j < 8; j++)
7868 int element = object_mapping[i].element_rnd;
7869 int action = object_mapping[i].action;
7870 int direction = object_mapping[i].direction;
7871 boolean is_backside = object_mapping[i].is_backside;
7872 int graphic_action = el_act_dir2img(element, action, direction);
7873 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7875 if ((action == ACTION_SMASHED_BY_ROCK ||
7876 action == ACTION_SMASHED_BY_SPRING ||
7877 action == ACTION_EATING) &&
7878 graphic_action == graphic_default)
7880 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7881 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7882 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7883 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7886 /* no separate animation for "smashed by rock" -- use rock instead */
7887 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7888 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7890 g_em->bitmap = g_xx->bitmap;
7891 g_em->src_x = g_xx->src_x;
7892 g_em->src_y = g_xx->src_y;
7893 g_em->src_offset_x = g_xx->src_offset_x;
7894 g_em->src_offset_y = g_xx->src_offset_y;
7895 g_em->dst_offset_x = g_xx->dst_offset_x;
7896 g_em->dst_offset_y = g_xx->dst_offset_y;
7897 g_em->width = g_xx->width;
7898 g_em->height = g_xx->height;
7899 g_em->unique_identifier = g_xx->unique_identifier;
7902 g_em->preserve_background = TRUE;
7907 for (p = 0; p < MAX_PLAYERS; p++)
7909 for (i = 0; i < SPR_MAX; i++)
7911 int element = player_mapping[p][i].element_rnd;
7912 int action = player_mapping[p][i].action;
7913 int direction = player_mapping[p][i].direction;
7915 for (j = 0; j < 8; j++)
7917 int effective_element = element;
7918 int effective_action = action;
7919 int graphic = (direction == MV_NONE ?
7920 el_act2img(effective_element, effective_action) :
7921 el_act_dir2img(effective_element, effective_action,
7923 struct GraphicInfo *g = &graphic_info[graphic];
7924 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7930 Bitmap *debug_bitmap = g_em->bitmap;
7931 int debug_src_x = g_em->src_x;
7932 int debug_src_y = g_em->src_y;
7935 int frame = getAnimationFrame(g->anim_frames,
7938 g->anim_start_frame,
7941 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7943 g_em->bitmap = src_bitmap;
7944 g_em->src_x = src_x;
7945 g_em->src_y = src_y;
7946 g_em->src_offset_x = 0;
7947 g_em->src_offset_y = 0;
7948 g_em->dst_offset_x = 0;
7949 g_em->dst_offset_y = 0;
7950 g_em->width = TILEX;
7951 g_em->height = TILEY;
7955 /* skip check for EMC elements not contained in original EMC artwork */
7956 if (element == EL_PLAYER_3 ||
7957 element == EL_PLAYER_4)
7960 if (g_em->bitmap != debug_bitmap ||
7961 g_em->src_x != debug_src_x ||
7962 g_em->src_y != debug_src_y)
7964 static int last_i = -1;
7972 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7973 p, i, element, element_info[element].token_name,
7974 element_action_info[effective_action].suffix, direction);
7976 if (element != effective_element)
7977 printf(" [%d ('%s')]",
7979 element_info[effective_element].token_name);
7983 if (g_em->bitmap != debug_bitmap)
7984 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7985 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7987 if (g_em->src_x != debug_src_x ||
7988 g_em->src_y != debug_src_y)
7989 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7991 g_em->src_x, g_em->src_y,
7992 g_em->src_x / 32, g_em->src_y / 32,
7993 debug_src_x, debug_src_y,
7994 debug_src_x / 32, debug_src_y / 32);
7996 num_em_gfx_errors++;
8006 printf("::: [%d errors found]\n", num_em_gfx_errors);
8012 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
8013 boolean any_player_moving,
8014 boolean any_player_snapping,
8015 boolean any_player_dropping)
8017 static boolean player_was_waiting = TRUE;
8019 if (frame == 0 && !any_player_dropping)
8021 if (!player_was_waiting)
8023 if (!SaveEngineSnapshotToList())
8026 player_was_waiting = TRUE;
8029 else if (any_player_moving || any_player_snapping || any_player_dropping)
8031 player_was_waiting = FALSE;
8035 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
8036 boolean murphy_is_dropping)
8038 static boolean player_was_waiting = TRUE;
8040 if (murphy_is_waiting)
8042 if (!player_was_waiting)
8044 if (!SaveEngineSnapshotToList())
8047 player_was_waiting = TRUE;
8052 player_was_waiting = FALSE;
8056 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
8057 boolean any_player_moving,
8058 boolean any_player_snapping,
8059 boolean any_player_dropping)
8061 if (tape.single_step && tape.recording && !tape.pausing)
8062 if (frame == 0 && !any_player_dropping)
8063 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8065 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
8066 any_player_snapping, any_player_dropping);
8069 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
8070 boolean murphy_is_dropping)
8072 if (tape.single_step && tape.recording && !tape.pausing)
8073 if (murphy_is_waiting)
8074 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
8076 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
8079 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8080 int graphic, int sync_frame, int x, int y)
8082 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8084 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8087 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8089 return (IS_NEXT_FRAME(sync_frame, graphic));
8092 int getGraphicInfo_Delay(int graphic)
8094 return graphic_info[graphic].anim_delay;
8097 void PlayMenuSoundExt(int sound)
8099 if (sound == SND_UNDEFINED)
8102 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8103 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8106 if (IS_LOOP_SOUND(sound))
8107 PlaySoundLoop(sound);
8112 void PlayMenuSound()
8114 PlayMenuSoundExt(menu.sound[game_status]);
8117 void PlayMenuSoundStereo(int sound, int stereo_position)
8119 if (sound == SND_UNDEFINED)
8122 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8123 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8126 if (IS_LOOP_SOUND(sound))
8127 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8129 PlaySoundStereo(sound, stereo_position);
8132 void PlayMenuSoundIfLoopExt(int sound)
8134 if (sound == SND_UNDEFINED)
8137 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8138 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8141 if (IS_LOOP_SOUND(sound))
8142 PlaySoundLoop(sound);
8145 void PlayMenuSoundIfLoop()
8147 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8150 void PlayMenuMusicExt(int music)
8152 if (music == MUS_UNDEFINED)
8155 if (!setup.sound_music)
8161 void PlayMenuMusic()
8163 PlayMenuMusicExt(menu.music[game_status]);
8166 void PlaySoundActivating()
8169 PlaySound(SND_MENU_ITEM_ACTIVATING);
8173 void PlaySoundSelecting()
8176 PlaySound(SND_MENU_ITEM_SELECTING);
8180 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8182 boolean change_fullscreen = (setup.fullscreen !=
8183 video.fullscreen_enabled);
8184 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
8185 !strEqual(setup.fullscreen_mode,
8186 video.fullscreen_mode_current));
8187 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8188 setup.window_scaling_percent !=
8189 video.window_scaling_percent);
8191 if (change_window_scaling_percent && video.fullscreen_enabled)
8194 if (!change_window_scaling_percent && !video.fullscreen_available)
8197 #if defined(TARGET_SDL2)
8198 if (change_window_scaling_percent)
8200 SDLSetWindowScaling(setup.window_scaling_percent);
8204 else if (change_fullscreen)
8206 SDLSetWindowFullscreen(setup.fullscreen);
8208 /* set setup value according to successfully changed fullscreen mode */
8209 setup.fullscreen = video.fullscreen_enabled;
8215 if (change_fullscreen ||
8216 change_fullscreen_mode ||
8217 change_window_scaling_percent)
8219 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8221 /* save backbuffer content which gets lost when toggling fullscreen mode */
8222 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8224 if (change_fullscreen_mode)
8226 /* keep fullscreen, but change fullscreen mode (screen resolution) */
8227 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
8230 if (change_window_scaling_percent)
8232 /* keep window mode, but change window scaling */
8233 video.fullscreen_enabled = TRUE; /* force new window scaling */
8236 /* toggle fullscreen */
8237 ChangeVideoModeIfNeeded(setup.fullscreen);
8239 /* set setup value according to successfully changed fullscreen mode */
8240 setup.fullscreen = video.fullscreen_enabled;
8242 /* restore backbuffer content from temporary backbuffer backup bitmap */
8243 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8245 FreeBitmap(tmp_backbuffer);
8247 /* update visible window/screen */
8248 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8252 void JoinRectangles(int *x, int *y, int *width, int *height,
8253 int x2, int y2, int width2, int height2)
8255 // do not join with "off-screen" rectangle
8256 if (x2 == -1 || y2 == -1)
8261 *width = MAX(*width, width2);
8262 *height = MAX(*height, height2);
8265 void SetGameStatus(int game_status_new)
8267 game_status = game_status_new;
8269 global.anim_status_next = game_status;
8272 void ChangeViewportPropertiesIfNeeded()
8274 int gfx_game_mode = game_status;
8275 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8277 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
8278 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8279 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8280 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8281 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8282 int new_win_xsize = vp_window->width;
8283 int new_win_ysize = vp_window->height;
8284 int border_size = vp_playfield->border_size;
8285 int new_sx = vp_playfield->x + border_size;
8286 int new_sy = vp_playfield->y + border_size;
8287 int new_sxsize = vp_playfield->width - 2 * border_size;
8288 int new_sysize = vp_playfield->height - 2 * border_size;
8289 int new_real_sx = vp_playfield->x;
8290 int new_real_sy = vp_playfield->y;
8291 int new_full_sxsize = vp_playfield->width;
8292 int new_full_sysize = vp_playfield->height;
8293 int new_dx = vp_door_1->x;
8294 int new_dy = vp_door_1->y;
8295 int new_dxsize = vp_door_1->width;
8296 int new_dysize = vp_door_1->height;
8297 int new_vx = vp_door_2->x;
8298 int new_vy = vp_door_2->y;
8299 int new_vxsize = vp_door_2->width;
8300 int new_vysize = vp_door_2->height;
8301 int new_ex = vp_door_3->x;
8302 int new_ey = vp_door_3->y;
8303 int new_exsize = vp_door_3->width;
8304 int new_eysize = vp_door_3->height;
8305 int new_tilesize_var =
8306 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8308 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8309 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8310 int new_scr_fieldx = new_sxsize / tilesize;
8311 int new_scr_fieldy = new_sysize / tilesize;
8312 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8313 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8314 boolean init_gfx_buffers = FALSE;
8315 boolean init_video_buffer = FALSE;
8316 boolean init_gadgets_and_toons = FALSE;
8317 boolean init_em_graphics = FALSE;
8319 if (new_win_xsize != WIN_XSIZE ||
8320 new_win_ysize != WIN_YSIZE)
8322 WIN_XSIZE = new_win_xsize;
8323 WIN_YSIZE = new_win_ysize;
8325 init_video_buffer = TRUE;
8326 init_gfx_buffers = TRUE;
8327 init_gadgets_and_toons = TRUE;
8329 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8332 if (new_scr_fieldx != SCR_FIELDX ||
8333 new_scr_fieldy != SCR_FIELDY)
8335 /* this always toggles between MAIN and GAME when using small tile size */
8337 SCR_FIELDX = new_scr_fieldx;
8338 SCR_FIELDY = new_scr_fieldy;
8340 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8351 new_sxsize != SXSIZE ||
8352 new_sysize != SYSIZE ||
8353 new_dxsize != DXSIZE ||
8354 new_dysize != DYSIZE ||
8355 new_vxsize != VXSIZE ||
8356 new_vysize != VYSIZE ||
8357 new_exsize != EXSIZE ||
8358 new_eysize != EYSIZE ||
8359 new_real_sx != REAL_SX ||
8360 new_real_sy != REAL_SY ||
8361 new_full_sxsize != FULL_SXSIZE ||
8362 new_full_sysize != FULL_SYSIZE ||
8363 new_tilesize_var != TILESIZE_VAR
8366 // ------------------------------------------------------------------------
8367 // determine next fading area for changed viewport definitions
8368 // ------------------------------------------------------------------------
8370 // start with current playfield area (default fading area)
8373 FADE_SXSIZE = FULL_SXSIZE;
8374 FADE_SYSIZE = FULL_SYSIZE;
8376 // add new playfield area if position or size has changed
8377 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8378 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8380 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8381 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8384 // add current and new door 1 area if position or size has changed
8385 if (new_dx != DX || new_dy != DY ||
8386 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8388 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8389 DX, DY, DXSIZE, DYSIZE);
8390 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8391 new_dx, new_dy, new_dxsize, new_dysize);
8394 // add current and new door 2 area if position or size has changed
8395 if (new_dx != VX || new_dy != VY ||
8396 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8398 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8399 VX, VY, VXSIZE, VYSIZE);
8400 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8401 new_vx, new_vy, new_vxsize, new_vysize);
8404 // ------------------------------------------------------------------------
8405 // handle changed tile size
8406 // ------------------------------------------------------------------------
8408 if (new_tilesize_var != TILESIZE_VAR)
8410 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8412 // changing tile size invalidates scroll values of engine snapshots
8413 FreeEngineSnapshotSingle();
8415 // changing tile size requires update of graphic mapping for EM engine
8416 init_em_graphics = TRUE;
8427 SXSIZE = new_sxsize;
8428 SYSIZE = new_sysize;
8429 DXSIZE = new_dxsize;
8430 DYSIZE = new_dysize;
8431 VXSIZE = new_vxsize;
8432 VYSIZE = new_vysize;
8433 EXSIZE = new_exsize;
8434 EYSIZE = new_eysize;
8435 REAL_SX = new_real_sx;
8436 REAL_SY = new_real_sy;
8437 FULL_SXSIZE = new_full_sxsize;
8438 FULL_SYSIZE = new_full_sysize;
8439 TILESIZE_VAR = new_tilesize_var;
8441 init_gfx_buffers = TRUE;
8442 init_gadgets_and_toons = TRUE;
8444 // printf("::: viewports: init_gfx_buffers\n");
8445 // printf("::: viewports: init_gadgets_and_toons\n");
8448 if (init_gfx_buffers)
8450 // printf("::: init_gfx_buffers\n");
8452 SCR_FIELDX = new_scr_fieldx_buffers;
8453 SCR_FIELDY = new_scr_fieldy_buffers;
8457 SCR_FIELDX = new_scr_fieldx;
8458 SCR_FIELDY = new_scr_fieldy;
8460 SetDrawDeactivationMask(REDRAW_NONE);
8461 SetDrawBackgroundMask(REDRAW_FIELD);
8464 if (init_video_buffer)
8466 // printf("::: init_video_buffer\n");
8468 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8469 InitImageTextures();
8472 if (init_gadgets_and_toons)
8474 // printf("::: init_gadgets_and_toons\n");
8478 InitGlobalAnimations();
8481 if (init_em_graphics)
8483 InitGraphicInfo_EM();