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 0
29 /* tool button identifiers */
30 #define TOOL_CTRL_ID_YES 0
31 #define TOOL_CTRL_ID_NO 1
32 #define TOOL_CTRL_ID_CONFIRM 2
33 #define TOOL_CTRL_ID_PLAYER_1 3
34 #define TOOL_CTRL_ID_PLAYER_2 4
35 #define TOOL_CTRL_ID_PLAYER_3 5
36 #define TOOL_CTRL_ID_PLAYER_4 6
38 #define NUM_TOOL_BUTTONS 7
40 /* constants for number of doors and door parts */
42 #define NUM_PANELS NUM_DOORS
43 // #define NUM_PANELS 0
44 #define MAX_PARTS_PER_DOOR 8
45 #define MAX_DOOR_PARTS (NUM_DOORS * MAX_PARTS_PER_DOOR + NUM_PANELS)
46 #define DOOR_PART_IS_PANEL(i) ((i) >= NUM_DOORS * MAX_PARTS_PER_DOOR)
49 struct DoorPartOrderInfo
55 static struct DoorPartOrderInfo door_part_order[MAX_DOOR_PARTS];
57 struct DoorPartControlInfo
61 struct DoorPartPosInfo *pos;
64 static struct DoorPartControlInfo door_part_controls[] =
68 IMG_DOOR_1_GFX_PART_1,
73 IMG_DOOR_1_GFX_PART_2,
78 IMG_DOOR_1_GFX_PART_3,
83 IMG_DOOR_1_GFX_PART_4,
88 IMG_DOOR_1_GFX_PART_5,
93 IMG_DOOR_1_GFX_PART_6,
98 IMG_DOOR_1_GFX_PART_7,
103 IMG_DOOR_1_GFX_PART_8,
109 IMG_DOOR_2_GFX_PART_1,
114 IMG_DOOR_2_GFX_PART_2,
119 IMG_DOOR_2_GFX_PART_3,
124 IMG_DOOR_2_GFX_PART_4,
129 IMG_DOOR_2_GFX_PART_5,
134 IMG_DOOR_2_GFX_PART_6,
139 IMG_DOOR_2_GFX_PART_7,
144 IMG_DOOR_2_GFX_PART_8,
150 IMG_BACKGROUND_PANEL,
167 /* forward declaration for internal use */
168 static void UnmapToolButtons();
169 static void HandleToolButtons(struct GadgetInfo *);
170 static int el_act_dir2crm(int, int, int);
171 static int el_act2crm(int, int);
173 static struct GadgetInfo *tool_gadget[NUM_TOOL_BUTTONS];
174 static int request_gadget_id = -1;
176 static unsigned int sync_frame_delay = 0;
177 static unsigned int sync_frame_delay_value = GAME_FRAME_DELAY;
179 static char *print_if_not_empty(int element)
181 static char *s = NULL;
182 char *token_name = element_info[element].token_name;
187 s = checked_malloc(strlen(token_name) + 10 + 1);
189 if (element != EL_EMPTY)
190 sprintf(s, "%d\t['%s']", element, token_name);
192 sprintf(s, "%d", element);
197 void DumpTile(int x, int y)
202 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
208 printf_line("-", 79);
209 printf("Field Info: SCREEN(%d, %d), LEVEL(%d, %d)\n", sx, sy, x, y);
210 printf_line("-", 79);
212 if (!IN_LEV_FIELD(x, y))
214 printf("(not in level field)\n");
220 printf(" Feld: %d\t['%s']\n", Feld[x][y],
221 element_info[Feld[x][y]].token_name);
222 printf(" Back: %s\n", print_if_not_empty(Back[x][y]));
223 printf(" Store: %s\n", print_if_not_empty(Store[x][y]));
224 printf(" Store2: %s\n", print_if_not_empty(Store2[x][y]));
225 printf(" StorePlayer: %s\n", print_if_not_empty(StorePlayer[x][y]));
226 printf(" MovPos: %d\n", MovPos[x][y]);
227 printf(" MovDir: %d\n", MovDir[x][y]);
228 printf(" MovDelay: %d\n", MovDelay[x][y]);
229 printf(" ChangeDelay: %d\n", ChangeDelay[x][y]);
230 printf(" CustomValue: %d\n", CustomValue[x][y]);
231 printf(" GfxElement: %d\n", GfxElement[x][y]);
232 printf(" GfxAction: %d\n", GfxAction[x][y]);
233 printf(" GfxFrame: %d [%d]\n", GfxFrame[x][y], FrameCounter);
237 void SetDrawtoField(int mode)
239 if (mode == DRAW_FIELDBUFFER)
245 BX2 = SCR_FIELDX + 1;
246 BY2 = SCR_FIELDY + 1;
248 drawto_field = fieldbuffer;
250 else /* DRAW_BACKBUFFER */
256 BX2 = SCR_FIELDX - 1;
257 BY2 = SCR_FIELDY - 1;
259 drawto_field = backbuffer;
263 static void RedrawPlayfield_RND()
265 if (game.envelope_active)
268 DrawLevel(REDRAW_ALL);
272 void RedrawPlayfield()
274 if (game_status != GAME_MODE_PLAYING)
277 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
278 RedrawPlayfield_EM(TRUE);
279 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
280 RedrawPlayfield_SP(TRUE);
281 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
282 RedrawPlayfield_RND();
284 BlitScreenToBitmap(backbuffer);
286 BlitBitmap(drawto, window, gfx.sx, gfx.sy, gfx.sxsize, gfx.sysize,
290 static void DrawMaskedBorderExt_Rect(int x, int y, int width, int height,
291 boolean blit_to_screen)
293 Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
296 BlitToScreenMasked(bitmap, x, y, width, height, x, y);
298 BlitBitmapMasked(bitmap, backbuffer, x, y, width, height, x, y);
301 static void DrawMaskedBorderExt_FIELD(boolean blit_to_screen)
303 if (global.border_status >= GAME_MODE_TITLE &&
304 global.border_status <= GAME_MODE_PLAYING &&
305 border.draw_masked[global.border_status])
306 DrawMaskedBorderExt_Rect(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE,
310 static void DrawMaskedBorderExt_DOOR_1(boolean blit_to_screen)
312 // only draw border over closed doors when drawing to backbuffer
313 if (!blit_to_screen && (GetDoorState() & DOOR_OPEN_1))
316 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
317 (global.border_status != GAME_MODE_EDITOR ||
318 border.draw_masked[GFX_SPECIAL_ARG_EDITOR]))
319 DrawMaskedBorderExt_Rect(DX, DY, DXSIZE, DYSIZE, blit_to_screen);
322 static void DrawMaskedBorderExt_DOOR_2(boolean blit_to_screen)
324 // only draw border over closed doors when drawing to backbuffer
325 if (!blit_to_screen && (GetDoorState() & DOOR_OPEN_2))
328 if (border.draw_masked[GFX_SPECIAL_ARG_DOOR] &&
329 global.border_status != GAME_MODE_EDITOR)
330 DrawMaskedBorderExt_Rect(VX, VY, VXSIZE, VYSIZE, blit_to_screen);
333 static void DrawMaskedBorderExt_DOOR_3(boolean blit_to_screen)
335 /* currently not available */
338 static void DrawMaskedBorderExt_ALL(boolean blit_to_screen)
340 DrawMaskedBorderExt_FIELD(blit_to_screen);
341 DrawMaskedBorderExt_DOOR_1(blit_to_screen);
342 DrawMaskedBorderExt_DOOR_2(blit_to_screen);
343 DrawMaskedBorderExt_DOOR_3(blit_to_screen);
346 static void DrawMaskedBorderExt(int redraw_mask, boolean blit_to_screen)
348 /* never draw masked screen borders on borderless screens */
349 if (game_status == GAME_MODE_LOADING ||
350 game_status == GAME_MODE_TITLE)
353 if (redraw_mask & REDRAW_ALL)
354 DrawMaskedBorderExt_ALL(blit_to_screen);
357 if (redraw_mask & REDRAW_FIELD)
358 DrawMaskedBorderExt_FIELD(blit_to_screen);
359 if (redraw_mask & REDRAW_DOOR_1)
360 DrawMaskedBorderExt_DOOR_1(blit_to_screen);
361 if (redraw_mask & REDRAW_DOOR_2)
362 DrawMaskedBorderExt_DOOR_2(blit_to_screen);
363 if (redraw_mask & REDRAW_DOOR_3)
364 DrawMaskedBorderExt_DOOR_3(blit_to_screen);
368 void DrawMaskedBorder_FIELD()
370 DrawMaskedBorderExt_FIELD(FALSE);
373 void DrawMaskedBorder(int redraw_mask)
375 DrawMaskedBorderExt(redraw_mask, FALSE);
378 void DrawMaskedBorderToScreen(int redraw_mask)
380 DrawMaskedBorderExt(redraw_mask, TRUE);
383 void BlitScreenToBitmap_RND(Bitmap *target_bitmap)
385 int fx = FX, fy = FY;
386 int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
387 int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
389 int dx = (ScreenMovDir & (MV_LEFT | MV_RIGHT) ? ScreenGfxPos : 0);
390 int dy = (ScreenMovDir & (MV_UP | MV_DOWN) ? ScreenGfxPos : 0);
391 int dx_var = dx * TILESIZE_VAR / TILESIZE;
392 int dy_var = dy * TILESIZE_VAR / TILESIZE;
395 ffx = (scroll_x - SBX_Left) * TILEX_VAR + dx_var;
396 ffy = (scroll_y - SBY_Upper) * TILEY_VAR + dy_var;
398 if (EVEN(SCR_FIELDX))
400 if (ffx < SBX_Right * TILEX_VAR + TILEX_VAR / 2 + TILEX_VAR)
401 fx += dx_var - MIN(ffx, TILEX_VAR / 2) + TILEX_VAR;
403 fx += (dx_var > 0 ? TILEX_VAR : 0);
410 if (EVEN(SCR_FIELDY))
412 if (ffy < SBY_Lower * TILEY_VAR + TILEY_VAR / 2 + TILEY_VAR)
413 fy += dy_var - MIN(ffy, TILEY_VAR / 2) + TILEY_VAR;
415 fy += (dy_var > 0 ? TILEY_VAR : 0);
422 if (full_lev_fieldx <= SCR_FIELDX)
424 if (EVEN(SCR_FIELDX))
425 fx = 2 * TILEX_VAR - (ODD(lev_fieldx) ? TILEX_VAR / 2 : 0);
427 fx = 2 * TILEX_VAR - (EVEN(lev_fieldx) ? TILEX_VAR / 2 : 0);
430 if (full_lev_fieldy <= SCR_FIELDY)
432 if (EVEN(SCR_FIELDY))
433 fy = 2 * TILEY_VAR - (ODD(lev_fieldy) ? TILEY_VAR / 2 : 0);
435 fy = 2 * TILEY_VAR - (EVEN(lev_fieldy) ? TILEY_VAR / 2 : 0);
438 BlitBitmap(drawto_field, target_bitmap, fx, fy, SXSIZE, SYSIZE, SX, SY);
441 void BlitScreenToBitmap(Bitmap *target_bitmap)
443 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
444 BlitScreenToBitmap_EM(target_bitmap);
445 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
446 BlitScreenToBitmap_SP(target_bitmap);
447 else if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
448 BlitScreenToBitmap_RND(target_bitmap);
450 redraw_mask |= REDRAW_FIELD;
453 void DrawFramesPerSecond()
456 int font_nr = FONT_TEXT_2;
457 int font_width = getFontWidth(font_nr);
459 sprintf(text, "%04.1f fps", global.frames_per_second);
461 DrawTextExt(backbuffer, WIN_XSIZE - font_width * strlen(text), 0, text,
462 font_nr, BLIT_OPAQUE);
467 if (redraw_mask == REDRAW_NONE)
471 // masked border now drawn immediately when blitting backbuffer to window
473 // draw masked border to all viewports, if defined
474 DrawMaskedBorder(redraw_mask);
477 // draw frames per second (only if debug mode is enabled)
478 if (redraw_mask & REDRAW_FPS)
479 DrawFramesPerSecond();
481 // redraw complete window if both playfield and (some) doors need redraw
482 if (redraw_mask & REDRAW_FIELD && redraw_mask & REDRAW_DOORS)
483 redraw_mask = REDRAW_ALL;
485 /* although redrawing the whole window would be fine for normal gameplay,
486 being able to only redraw the playfield is required for deactivating
487 certain drawing areas (mainly playfield) to work, which is needed for
488 warp-forward to be fast enough (by skipping redraw of most frames) */
490 if (redraw_mask & REDRAW_ALL)
492 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
494 else if (redraw_mask & REDRAW_FIELD)
496 BlitBitmap(backbuffer, window,
497 REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE, REAL_SX, REAL_SY);
499 else if (redraw_mask & REDRAW_DOORS)
501 if (redraw_mask & REDRAW_DOOR_1)
502 BlitBitmap(backbuffer, window, DX, DY, DXSIZE, DYSIZE, DX, DY);
504 if (redraw_mask & REDRAW_DOOR_2)
505 BlitBitmap(backbuffer, window, VX, VY, VXSIZE, VYSIZE, VX, VY);
507 if (redraw_mask & REDRAW_DOOR_3)
508 BlitBitmap(backbuffer, window, EX, EY, EXSIZE, EYSIZE, EX, EY);
511 redraw_mask = REDRAW_NONE;
514 static void FadeCrossSaveBackbuffer()
516 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
519 static void FadeCrossRestoreBackbuffer()
521 int redraw_mask_last = redraw_mask;
523 BlitBitmap(bitmap_db_cross, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
525 // do not change redraw mask when restoring backbuffer after cross-fading
526 redraw_mask = redraw_mask_last;
529 static void FadeExt(int fade_mask, int fade_mode, int fade_type)
531 static int fade_type_skip = FADE_TYPE_NONE;
532 void (*draw_border_function)(void) = NULL;
533 Bitmap *bitmap = (fade_mode & FADE_TYPE_TRANSFORM ? bitmap_db_cross : NULL);
534 int x, y, width, height;
535 int fade_delay, post_delay;
537 if (fade_type == FADE_TYPE_FADE_OUT)
539 if (fade_type_skip != FADE_TYPE_NONE)
541 /* skip all fade operations until specified fade operation */
542 if (fade_type & fade_type_skip)
543 fade_type_skip = FADE_TYPE_NONE;
549 FadeCrossSaveBackbuffer();
552 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
555 FadeCrossSaveBackbuffer();
562 redraw_mask |= fade_mask;
564 if (fade_type == FADE_TYPE_SKIP)
566 fade_type_skip = fade_mode;
571 fade_delay = fading.fade_delay;
572 post_delay = (fade_mode == FADE_MODE_FADE_OUT ? fading.post_delay : 0);
574 if (fade_type_skip != FADE_TYPE_NONE)
576 /* skip all fade operations until specified fade operation */
577 if (fade_type & fade_type_skip)
578 fade_type_skip = FADE_TYPE_NONE;
583 if (global.autoplay_leveldir)
588 if (fade_mask == REDRAW_FIELD)
593 height = FADE_SYSIZE;
595 if (border.draw_masked_when_fading)
596 draw_border_function = DrawMaskedBorder_FIELD; /* update when fading */
598 DrawMaskedBorder_FIELD(); /* draw once */
600 else /* REDRAW_ALL */
608 if (!setup.fade_screens ||
610 fading.fade_mode == FADE_MODE_NONE)
612 if (fade_mode == FADE_MODE_FADE_OUT)
615 BlitBitmap(backbuffer, window, x, y, width, height, x, y);
617 redraw_mask &= ~fade_mask;
622 FadeRectangle(bitmap, x, y, width, height, fade_mode, fade_delay, post_delay,
623 draw_border_function);
625 if (fade_type == FADE_TYPE_FADE_OUT)
626 FadeCrossRestoreBackbuffer();
628 redraw_mask &= ~fade_mask;
631 void FadeIn(int fade_mask)
634 DrawMaskedBorder(REDRAW_ALL);
637 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
638 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_IN);
640 FadeExt(fade_mask, FADE_MODE_FADE_IN, FADE_TYPE_FADE_IN);
644 FADE_SXSIZE = FULL_SXSIZE;
645 FADE_SYSIZE = FULL_SYSIZE;
647 global.anim_status = global.anim_status_next;
650 void FadeOut(int fade_mask)
652 global.anim_status = GAME_MODE_PSEUDO_FADING;
655 DrawMaskedBorder(REDRAW_ALL);
658 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
659 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
661 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
663 global.border_status = game_status;
666 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
668 static struct TitleFadingInfo fading_leave_stored;
671 fading_leave_stored = fading_leave;
673 fading = fading_leave_stored;
676 void FadeSetEnterMenu()
678 fading = menu.enter_menu;
680 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
683 void FadeSetLeaveMenu()
685 fading = menu.leave_menu;
687 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
690 void FadeSetEnterScreen()
692 fading = menu.enter_screen[game_status];
694 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
697 void FadeSetNextScreen()
699 fading = menu.next_screen[game_status];
701 // (do not overwrite fade mode set by FadeSetEnterScreen)
702 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
705 void FadeSetLeaveScreen()
707 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
710 void FadeSetFromType(int type)
712 if (type & TYPE_ENTER_SCREEN)
713 FadeSetEnterScreen();
714 else if (type & TYPE_ENTER)
716 else if (type & TYPE_LEAVE)
720 void FadeSetDisabled()
722 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
724 fading = fading_none;
727 void FadeSkipNextFadeIn()
729 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
732 void FadeSkipNextFadeOut()
734 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
737 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
739 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
741 return (graphic == IMG_UNDEFINED ? NULL :
742 graphic_info[graphic].bitmap != NULL || redefined ?
743 graphic_info[graphic].bitmap :
744 graphic_info[default_graphic].bitmap);
747 Bitmap *getBackgroundBitmap(int graphic)
749 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
752 Bitmap *getGlobalBorderBitmap(int graphic)
754 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
757 Bitmap *getGlobalBorderBitmapFromGameStatus()
760 (game_status == GAME_MODE_MAIN ||
761 game_status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
762 game_status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
763 game_status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
764 game_status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
767 return getGlobalBorderBitmap(graphic);
770 void SetWindowBackgroundImageIfDefined(int graphic)
772 if (graphic_info[graphic].bitmap)
773 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
776 void SetMainBackgroundImageIfDefined(int graphic)
778 if (graphic_info[graphic].bitmap)
779 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
782 void SetDoorBackgroundImageIfDefined(int graphic)
784 if (graphic_info[graphic].bitmap)
785 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
788 void SetWindowBackgroundImage(int graphic)
790 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
793 void SetMainBackgroundImage(int graphic)
795 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
798 void SetDoorBackgroundImage(int graphic)
800 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
803 void SetPanelBackground()
805 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
807 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
808 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
810 SetDoorBackgroundBitmap(bitmap_db_panel);
813 void DrawBackground(int x, int y, int width, int height)
815 /* "drawto" might still point to playfield buffer here (hall of fame) */
816 ClearRectangleOnBackground(backbuffer, x, y, width, height);
818 if (IN_GFX_FIELD_FULL(x, y))
819 redraw_mask |= REDRAW_FIELD;
820 else if (IN_GFX_DOOR_1(x, y))
821 redraw_mask |= REDRAW_DOOR_1;
822 else if (IN_GFX_DOOR_2(x, y))
823 redraw_mask |= REDRAW_DOOR_2;
824 else if (IN_GFX_DOOR_3(x, y))
825 redraw_mask |= REDRAW_DOOR_3;
828 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
830 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
832 if (font->bitmap == NULL)
835 DrawBackground(x, y, width, height);
838 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
840 struct GraphicInfo *g = &graphic_info[graphic];
842 if (g->bitmap == NULL)
845 DrawBackground(x, y, width, height);
848 static int game_status_last = -1;
849 static Bitmap *global_border_bitmap_last = NULL;
850 static Bitmap *global_border_bitmap = NULL;
851 static int real_sx_last = -1, real_sy_last = -1;
852 static int full_sxsize_last = -1, full_sysize_last = -1;
853 static int dx_last = -1, dy_last = -1;
854 static int dxsize_last = -1, dysize_last = -1;
855 static int vx_last = -1, vy_last = -1;
856 static int vxsize_last = -1, vysize_last = -1;
858 boolean CheckIfGlobalBorderHasChanged()
860 // if game status has not changed, global border has not changed either
861 if (game_status == game_status_last)
864 // determine and store new global border bitmap for current game status
865 global_border_bitmap = getGlobalBorderBitmapFromGameStatus();
867 return (global_border_bitmap_last != global_border_bitmap);
870 boolean CheckIfGlobalBorderRedrawIsNeeded()
872 // if game status has not changed, nothing has to be redrawn
873 if (game_status == game_status_last)
876 // redraw if last screen was title screen
877 if (game_status_last == GAME_MODE_TITLE)
880 // redraw if global screen border has changed
881 if (CheckIfGlobalBorderHasChanged())
884 // redraw if position or size of playfield area has changed
885 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
886 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
889 // redraw if position or size of door area has changed
890 if (dx_last != DX || dy_last != DY ||
891 dxsize_last != DXSIZE || dysize_last != DYSIZE)
894 // redraw if position or size of tape area has changed
895 if (vx_last != VX || vy_last != VY ||
896 vxsize_last != VXSIZE || vysize_last != VYSIZE)
902 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
905 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
907 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
910 void RedrawGlobalBorder()
912 Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
914 RedrawGlobalBorderFromBitmap(bitmap);
916 redraw_mask = REDRAW_ALL;
919 static void RedrawGlobalBorderIfNeeded()
921 if (game_status == game_status_last)
924 // copy current draw buffer to later copy back areas that have not changed
925 if (game_status_last != GAME_MODE_TITLE)
926 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
928 if (CheckIfGlobalBorderRedrawIsNeeded())
930 // redraw global screen border (or clear, if defined to be empty)
931 RedrawGlobalBorderFromBitmap(global_border_bitmap);
933 // copy previous playfield and door areas, if they are defined on both
934 // previous and current screen and if they still have the same size
936 if (real_sx_last != -1 && real_sy_last != -1 &&
937 REAL_SX != -1 && REAL_SY != -1 &&
938 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
939 BlitBitmap(bitmap_db_store, backbuffer,
940 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
943 if (dx_last != -1 && dy_last != -1 &&
944 DX != -1 && DY != -1 &&
945 dxsize_last == DXSIZE && dysize_last == DYSIZE)
946 BlitBitmap(bitmap_db_store, backbuffer,
947 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
949 if (vx_last != -1 && vy_last != -1 &&
950 VX != -1 && VY != -1 &&
951 vxsize_last == VXSIZE && vysize_last == VYSIZE)
952 BlitBitmap(bitmap_db_store, backbuffer,
953 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
955 redraw_mask = REDRAW_ALL;
958 game_status_last = game_status;
960 global_border_bitmap_last = global_border_bitmap;
962 real_sx_last = REAL_SX;
963 real_sy_last = REAL_SY;
964 full_sxsize_last = FULL_SXSIZE;
965 full_sysize_last = FULL_SYSIZE;
968 dxsize_last = DXSIZE;
969 dysize_last = DYSIZE;
972 vxsize_last = VXSIZE;
973 vysize_last = VYSIZE;
978 RedrawGlobalBorderIfNeeded();
980 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
981 /* (when entering hall of fame after playing) */
982 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
984 /* !!! maybe this should be done before clearing the background !!! */
985 if (game_status == GAME_MODE_PLAYING)
987 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
988 SetDrawtoField(DRAW_FIELDBUFFER);
992 SetDrawtoField(DRAW_BACKBUFFER);
996 void MarkTileDirty(int x, int y)
998 redraw_mask |= REDRAW_FIELD;
1001 void SetBorderElement()
1005 BorderElement = EL_EMPTY;
1007 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1009 for (x = 0; x < lev_fieldx; x++)
1011 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1012 BorderElement = EL_STEELWALL;
1014 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1020 void FloodFillLevel(int from_x, int from_y, int fill_element,
1021 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1022 int max_fieldx, int max_fieldy)
1026 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1027 static int safety = 0;
1029 /* check if starting field still has the desired content */
1030 if (field[from_x][from_y] == fill_element)
1035 if (safety > max_fieldx * max_fieldy)
1036 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1038 old_element = field[from_x][from_y];
1039 field[from_x][from_y] = fill_element;
1041 for (i = 0; i < 4; i++)
1043 x = from_x + check[i][0];
1044 y = from_y + check[i][1];
1046 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1047 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1053 void SetRandomAnimationValue(int x, int y)
1055 gfx.anim_random_frame = GfxRandom[x][y];
1058 int getGraphicAnimationFrame(int graphic, int sync_frame)
1060 /* animation synchronized with global frame counter, not move position */
1061 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1062 sync_frame = FrameCounter;
1064 return getAnimationFrame(graphic_info[graphic].anim_frames,
1065 graphic_info[graphic].anim_delay,
1066 graphic_info[graphic].anim_mode,
1067 graphic_info[graphic].anim_start_frame,
1071 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1072 Bitmap **bitmap, int *x, int *y,
1073 boolean get_backside)
1075 struct GraphicInfo *g = &graphic_info[graphic];
1076 Bitmap *src_bitmap = g->bitmap;
1077 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1078 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1079 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1081 // if no in-game graphics defined, always use standard graphic size
1082 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1083 tilesize = TILESIZE;
1085 if (tilesize == gfx.standard_tile_size)
1086 src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1087 else if (tilesize == game.tile_size)
1088 src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
1090 src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1092 if (g->offset_y == 0) /* frames are ordered horizontally */
1094 int max_width = g->anim_frames_per_line * g->width;
1095 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1097 src_x = pos % max_width;
1098 src_y = src_y % g->height + pos / max_width * g->height;
1100 else if (g->offset_x == 0) /* frames are ordered vertically */
1102 int max_height = g->anim_frames_per_line * g->height;
1103 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1105 src_x = src_x % g->width + pos / max_height * g->width;
1106 src_y = pos % max_height;
1108 else /* frames are ordered diagonally */
1110 src_x = src_x + frame * g->offset_x;
1111 src_y = src_y + frame * g->offset_y;
1114 *bitmap = src_bitmap;
1115 *x = src_x * tilesize / g->tile_size;
1116 *y = src_y * tilesize / g->tile_size;
1119 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1120 int *x, int *y, boolean get_backside)
1122 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1126 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1127 Bitmap **bitmap, int *x, int *y)
1129 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1132 void getFixedGraphicSource(int graphic, int frame,
1133 Bitmap **bitmap, int *x, int *y)
1135 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1138 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1140 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1143 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1144 int *x, int *y, boolean get_backside)
1146 struct GraphicInfo *g = &graphic_info[graphic];
1147 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1148 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1150 if (TILESIZE_VAR != TILESIZE)
1151 return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1154 *bitmap = g->bitmap;
1156 if (g->offset_y == 0) /* frames are ordered horizontally */
1158 int max_width = g->anim_frames_per_line * g->width;
1159 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1161 *x = pos % max_width;
1162 *y = src_y % g->height + pos / max_width * g->height;
1164 else if (g->offset_x == 0) /* frames are ordered vertically */
1166 int max_height = g->anim_frames_per_line * g->height;
1167 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1169 *x = src_x % g->width + pos / max_height * g->width;
1170 *y = pos % max_height;
1172 else /* frames are ordered diagonally */
1174 *x = src_x + frame * g->offset_x;
1175 *y = src_y + frame * g->offset_y;
1178 *x = *x * TILESIZE_VAR / g->tile_size;
1179 *y = *y * TILESIZE_VAR / g->tile_size;
1182 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1184 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1187 void DrawGraphic(int x, int y, int graphic, int frame)
1190 if (!IN_SCR_FIELD(x, y))
1192 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1193 printf("DrawGraphic(): This should never happen!\n");
1198 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1201 MarkTileDirty(x, y);
1204 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1207 if (!IN_SCR_FIELD(x, y))
1209 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1210 printf("DrawGraphic(): This should never happen!\n");
1215 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1217 MarkTileDirty(x, y);
1220 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1226 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1228 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1231 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1237 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1238 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1241 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1244 if (!IN_SCR_FIELD(x, y))
1246 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1247 printf("DrawGraphicThruMask(): This should never happen!\n");
1252 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1255 MarkTileDirty(x, y);
1258 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1261 if (!IN_SCR_FIELD(x, y))
1263 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1264 printf("DrawGraphicThruMask(): This should never happen!\n");
1269 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1271 MarkTileDirty(x, y);
1274 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1280 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1282 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1286 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1287 int graphic, int frame)
1292 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1294 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1298 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1300 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1302 MarkTileDirty(x / tilesize, y / tilesize);
1305 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1311 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1312 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1315 void DrawMiniGraphic(int x, int y, int graphic)
1317 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1318 MarkTileDirty(x / 2, y / 2);
1321 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1326 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1327 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1330 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1331 int graphic, int frame,
1332 int cut_mode, int mask_mode)
1337 int width = TILEX, height = TILEY;
1340 if (dx || dy) /* shifted graphic */
1342 if (x < BX1) /* object enters playfield from the left */
1349 else if (x > BX2) /* object enters playfield from the right */
1355 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1361 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1363 else if (dx) /* general horizontal movement */
1364 MarkTileDirty(x + SIGN(dx), y);
1366 if (y < BY1) /* object enters playfield from the top */
1368 if (cut_mode == CUT_BELOW) /* object completely above top border */
1376 else if (y > BY2) /* object enters playfield from the bottom */
1382 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1388 else if (dy > 0 && cut_mode == CUT_ABOVE)
1390 if (y == BY2) /* object completely above bottom border */
1396 MarkTileDirty(x, y + 1);
1397 } /* object leaves playfield to the bottom */
1398 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1400 else if (dy) /* general vertical movement */
1401 MarkTileDirty(x, y + SIGN(dy));
1405 if (!IN_SCR_FIELD(x, y))
1407 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1408 printf("DrawGraphicShifted(): This should never happen!\n");
1413 width = width * TILESIZE_VAR / TILESIZE;
1414 height = height * TILESIZE_VAR / TILESIZE;
1415 cx = cx * TILESIZE_VAR / TILESIZE;
1416 cy = cy * TILESIZE_VAR / TILESIZE;
1417 dx = dx * TILESIZE_VAR / TILESIZE;
1418 dy = dy * TILESIZE_VAR / TILESIZE;
1420 if (width > 0 && height > 0)
1422 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1427 dst_x = FX + x * TILEX_VAR + dx;
1428 dst_y = FY + y * TILEY_VAR + dy;
1430 if (mask_mode == USE_MASKING)
1431 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1434 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1437 MarkTileDirty(x, y);
1441 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1442 int graphic, int frame,
1443 int cut_mode, int mask_mode)
1448 int width = TILEX_VAR, height = TILEY_VAR;
1451 int x2 = x + SIGN(dx);
1452 int y2 = y + SIGN(dy);
1454 /* movement with two-tile animations must be sync'ed with movement position,
1455 not with current GfxFrame (which can be higher when using slow movement) */
1456 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1457 int anim_frames = graphic_info[graphic].anim_frames;
1459 /* (we also need anim_delay here for movement animations with less frames) */
1460 int anim_delay = graphic_info[graphic].anim_delay;
1461 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1463 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1464 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1466 /* re-calculate animation frame for two-tile movement animation */
1467 frame = getGraphicAnimationFrame(graphic, sync_frame);
1469 /* check if movement start graphic inside screen area and should be drawn */
1470 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1472 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1474 dst_x = FX + x1 * TILEX_VAR;
1475 dst_y = FY + y1 * TILEY_VAR;
1477 if (mask_mode == USE_MASKING)
1478 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1481 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1484 MarkTileDirty(x1, y1);
1487 /* check if movement end graphic inside screen area and should be drawn */
1488 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1490 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1492 dst_x = FX + x2 * TILEX_VAR;
1493 dst_y = FY + y2 * TILEY_VAR;
1495 if (mask_mode == USE_MASKING)
1496 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1499 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1502 MarkTileDirty(x2, y2);
1506 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1507 int graphic, int frame,
1508 int cut_mode, int mask_mode)
1512 DrawGraphic(x, y, graphic, frame);
1517 if (graphic_info[graphic].double_movement) /* EM style movement images */
1518 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1520 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1523 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1524 int frame, int cut_mode)
1526 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1529 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1530 int cut_mode, int mask_mode)
1532 int lx = LEVELX(x), ly = LEVELY(y);
1536 if (IN_LEV_FIELD(lx, ly))
1538 SetRandomAnimationValue(lx, ly);
1540 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1541 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1543 /* do not use double (EM style) movement graphic when not moving */
1544 if (graphic_info[graphic].double_movement && !dx && !dy)
1546 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1547 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1550 else /* border element */
1552 graphic = el2img(element);
1553 frame = getGraphicAnimationFrame(graphic, -1);
1556 if (element == EL_EXPANDABLE_WALL)
1558 boolean left_stopped = FALSE, right_stopped = FALSE;
1560 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1561 left_stopped = TRUE;
1562 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1563 right_stopped = TRUE;
1565 if (left_stopped && right_stopped)
1567 else if (left_stopped)
1569 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1570 frame = graphic_info[graphic].anim_frames - 1;
1572 else if (right_stopped)
1574 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1575 frame = graphic_info[graphic].anim_frames - 1;
1580 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1581 else if (mask_mode == USE_MASKING)
1582 DrawGraphicThruMask(x, y, graphic, frame);
1584 DrawGraphic(x, y, graphic, frame);
1587 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1588 int cut_mode, int mask_mode)
1590 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1591 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1592 cut_mode, mask_mode);
1595 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1598 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1601 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1604 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1607 void DrawLevelElementThruMask(int x, int y, int element)
1609 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1612 void DrawLevelFieldThruMask(int x, int y)
1614 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1617 /* !!! implementation of quicksand is totally broken !!! */
1618 #define IS_CRUMBLED_TILE(x, y, e) \
1619 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1620 !IS_MOVING(x, y) || \
1621 (e) == EL_QUICKSAND_EMPTYING || \
1622 (e) == EL_QUICKSAND_FAST_EMPTYING))
1624 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1629 int width, height, cx, cy;
1630 int sx = SCREENX(x), sy = SCREENY(y);
1631 int crumbled_border_size = graphic_info[graphic].border_size;
1634 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1636 for (i = 1; i < 4; i++)
1638 int dxx = (i & 1 ? dx : 0);
1639 int dyy = (i & 2 ? dy : 0);
1642 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1645 /* check if neighbour field is of same crumble type */
1646 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1647 graphic_info[graphic].class ==
1648 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1650 /* return if check prevents inner corner */
1651 if (same == (dxx == dx && dyy == dy))
1655 /* if we reach this point, we have an inner corner */
1657 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1659 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1660 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1661 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1662 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1664 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1665 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1668 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1673 int width, height, bx, by, cx, cy;
1674 int sx = SCREENX(x), sy = SCREENY(y);
1675 int crumbled_border_size = graphic_info[graphic].border_size;
1676 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1677 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1680 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1682 /* draw simple, sloppy, non-corner-accurate crumbled border */
1684 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1685 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1686 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1687 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1689 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1690 FX + sx * TILEX_VAR + cx,
1691 FY + sy * TILEY_VAR + cy);
1693 /* (remaining middle border part must be at least as big as corner part) */
1694 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1695 crumbled_border_size >= TILESIZE / 3)
1698 /* correct corners of crumbled border, if needed */
1700 for (i = -1; i <= 1; i += 2)
1702 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1703 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1704 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1707 /* check if neighbour field is of same crumble type */
1708 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1709 graphic_info[graphic].class ==
1710 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1712 /* no crumbled corner, but continued crumbled border */
1714 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1715 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1716 int b1 = (i == 1 ? crumbled_border_size_var :
1717 TILESIZE_VAR - 2 * crumbled_border_size_var);
1719 width = crumbled_border_size_var;
1720 height = crumbled_border_size_var;
1722 if (dir == 1 || dir == 2)
1737 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1739 FX + sx * TILEX_VAR + cx,
1740 FY + sy * TILEY_VAR + cy);
1745 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1747 int sx = SCREENX(x), sy = SCREENY(y);
1750 static int xy[4][2] =
1758 if (!IN_LEV_FIELD(x, y))
1761 element = TILE_GFX_ELEMENT(x, y);
1763 /* crumble field itself */
1764 if (IS_CRUMBLED_TILE(x, y, element))
1766 if (!IN_SCR_FIELD(sx, sy))
1769 for (i = 0; i < 4; i++)
1771 int xx = x + xy[i][0];
1772 int yy = y + xy[i][1];
1774 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1777 /* check if neighbour field is of same crumble type */
1778 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1779 graphic_info[graphic].class ==
1780 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1783 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1786 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1787 graphic_info[graphic].anim_frames == 2)
1789 for (i = 0; i < 4; i++)
1791 int dx = (i & 1 ? +1 : -1);
1792 int dy = (i & 2 ? +1 : -1);
1794 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1798 MarkTileDirty(sx, sy);
1800 else /* center field not crumbled -- crumble neighbour fields */
1802 for (i = 0; i < 4; i++)
1804 int xx = x + xy[i][0];
1805 int yy = y + xy[i][1];
1806 int sxx = sx + xy[i][0];
1807 int syy = sy + xy[i][1];
1809 if (!IN_LEV_FIELD(xx, yy) ||
1810 !IN_SCR_FIELD(sxx, syy))
1813 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1816 element = TILE_GFX_ELEMENT(xx, yy);
1818 if (!IS_CRUMBLED_TILE(xx, yy, element))
1821 graphic = el_act2crm(element, ACTION_DEFAULT);
1823 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1825 MarkTileDirty(sxx, syy);
1830 void DrawLevelFieldCrumbled(int x, int y)
1834 if (!IN_LEV_FIELD(x, y))
1837 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1838 GfxElement[x][y] != EL_UNDEFINED &&
1839 GFX_CRUMBLED(GfxElement[x][y]))
1841 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1846 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1848 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1851 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1854 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1855 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1856 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1857 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1858 int sx = SCREENX(x), sy = SCREENY(y);
1860 DrawGraphic(sx, sy, graphic1, frame1);
1861 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1864 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1866 int sx = SCREENX(x), sy = SCREENY(y);
1867 static int xy[4][2] =
1876 for (i = 0; i < 4; i++)
1878 int xx = x + xy[i][0];
1879 int yy = y + xy[i][1];
1880 int sxx = sx + xy[i][0];
1881 int syy = sy + xy[i][1];
1883 if (!IN_LEV_FIELD(xx, yy) ||
1884 !IN_SCR_FIELD(sxx, syy) ||
1885 !GFX_CRUMBLED(Feld[xx][yy]) ||
1889 DrawLevelField(xx, yy);
1893 static int getBorderElement(int x, int y)
1897 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1898 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
1899 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
1900 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1901 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
1902 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
1903 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
1905 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1906 int steel_position = (x == -1 && y == -1 ? 0 :
1907 x == lev_fieldx && y == -1 ? 1 :
1908 x == -1 && y == lev_fieldy ? 2 :
1909 x == lev_fieldx && y == lev_fieldy ? 3 :
1910 x == -1 || x == lev_fieldx ? 4 :
1911 y == -1 || y == lev_fieldy ? 5 : 6);
1913 return border[steel_position][steel_type];
1916 void DrawScreenElement(int x, int y, int element)
1918 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1919 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
1922 void DrawLevelElement(int x, int y, int element)
1924 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1925 DrawScreenElement(SCREENX(x), SCREENY(y), element);
1928 void DrawScreenField(int x, int y)
1930 int lx = LEVELX(x), ly = LEVELY(y);
1931 int element, content;
1933 if (!IN_LEV_FIELD(lx, ly))
1935 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1938 element = getBorderElement(lx, ly);
1940 DrawScreenElement(x, y, element);
1945 element = Feld[lx][ly];
1946 content = Store[lx][ly];
1948 if (IS_MOVING(lx, ly))
1950 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1951 boolean cut_mode = NO_CUTTING;
1953 if (element == EL_QUICKSAND_EMPTYING ||
1954 element == EL_QUICKSAND_FAST_EMPTYING ||
1955 element == EL_MAGIC_WALL_EMPTYING ||
1956 element == EL_BD_MAGIC_WALL_EMPTYING ||
1957 element == EL_DC_MAGIC_WALL_EMPTYING ||
1958 element == EL_AMOEBA_DROPPING)
1959 cut_mode = CUT_ABOVE;
1960 else if (element == EL_QUICKSAND_FILLING ||
1961 element == EL_QUICKSAND_FAST_FILLING ||
1962 element == EL_MAGIC_WALL_FILLING ||
1963 element == EL_BD_MAGIC_WALL_FILLING ||
1964 element == EL_DC_MAGIC_WALL_FILLING)
1965 cut_mode = CUT_BELOW;
1967 if (cut_mode == CUT_ABOVE)
1968 DrawScreenElement(x, y, element);
1970 DrawScreenElement(x, y, EL_EMPTY);
1973 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
1974 else if (cut_mode == NO_CUTTING)
1975 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
1978 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
1980 if (cut_mode == CUT_BELOW &&
1981 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
1982 DrawLevelElement(lx, ly + 1, element);
1985 if (content == EL_ACID)
1987 int dir = MovDir[lx][ly];
1988 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
1989 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
1991 DrawLevelElementThruMask(newlx, newly, EL_ACID);
1994 else if (IS_BLOCKED(lx, ly))
1999 boolean cut_mode = NO_CUTTING;
2000 int element_old, content_old;
2002 Blocked2Moving(lx, ly, &oldx, &oldy);
2005 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2006 MovDir[oldx][oldy] == MV_RIGHT);
2008 element_old = Feld[oldx][oldy];
2009 content_old = Store[oldx][oldy];
2011 if (element_old == EL_QUICKSAND_EMPTYING ||
2012 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2013 element_old == EL_MAGIC_WALL_EMPTYING ||
2014 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2015 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2016 element_old == EL_AMOEBA_DROPPING)
2017 cut_mode = CUT_ABOVE;
2019 DrawScreenElement(x, y, EL_EMPTY);
2022 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2024 else if (cut_mode == NO_CUTTING)
2025 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2028 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2031 else if (IS_DRAWABLE(element))
2032 DrawScreenElement(x, y, element);
2034 DrawScreenElement(x, y, EL_EMPTY);
2037 void DrawLevelField(int x, int y)
2039 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2040 DrawScreenField(SCREENX(x), SCREENY(y));
2041 else if (IS_MOVING(x, y))
2045 Moving2Blocked(x, y, &newx, &newy);
2046 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2047 DrawScreenField(SCREENX(newx), SCREENY(newy));
2049 else if (IS_BLOCKED(x, y))
2053 Blocked2Moving(x, y, &oldx, &oldy);
2054 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2055 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2059 void DrawSizedElement(int x, int y, int element, int tilesize)
2063 graphic = el2edimg(element);
2064 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2067 void DrawMiniElement(int x, int y, int element)
2071 graphic = el2edimg(element);
2072 DrawMiniGraphic(x, y, graphic);
2075 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2078 int x = sx + scroll_x, y = sy + scroll_y;
2080 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2081 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2082 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2083 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2085 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2088 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2090 int x = sx + scroll_x, y = sy + scroll_y;
2092 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2093 DrawMiniElement(sx, sy, EL_EMPTY);
2094 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2095 DrawMiniElement(sx, sy, Feld[x][y]);
2097 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2100 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2101 int x, int y, int xsize, int ysize,
2102 int tile_width, int tile_height)
2106 int dst_x = startx + x * tile_width;
2107 int dst_y = starty + y * tile_height;
2108 int width = graphic_info[graphic].width;
2109 int height = graphic_info[graphic].height;
2110 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2111 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2112 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2113 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2114 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2115 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2116 boolean draw_masked = graphic_info[graphic].draw_masked;
2118 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2120 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2122 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2126 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2127 inner_sx + (x - 1) * tile_width % inner_width);
2128 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2129 inner_sy + (y - 1) * tile_height % inner_height);
2132 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2135 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2139 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2140 int x, int y, int xsize, int ysize, int font_nr)
2142 int font_width = getFontWidth(font_nr);
2143 int font_height = getFontHeight(font_nr);
2145 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2146 font_width, font_height);
2149 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2151 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2152 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2153 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2154 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2155 boolean no_delay = (tape.warp_forward);
2156 unsigned int anim_delay = 0;
2157 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2158 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2159 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2160 int font_width = getFontWidth(font_nr);
2161 int font_height = getFontHeight(font_nr);
2162 int max_xsize = level.envelope[envelope_nr].xsize;
2163 int max_ysize = level.envelope[envelope_nr].ysize;
2164 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2165 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2166 int xend = max_xsize;
2167 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2168 int xstep = (xstart < xend ? 1 : 0);
2169 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2171 int end = MAX(xend - xstart, yend - ystart);
2174 for (i = start; i <= end; i++)
2176 int last_frame = end; // last frame of this "for" loop
2177 int x = xstart + i * xstep;
2178 int y = ystart + i * ystep;
2179 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2180 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2181 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2182 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2185 SetDrawtoField(DRAW_FIELDBUFFER);
2187 BlitScreenToBitmap(backbuffer);
2189 SetDrawtoField(DRAW_BACKBUFFER);
2191 for (yy = 0; yy < ysize; yy++)
2192 for (xx = 0; xx < xsize; xx++)
2193 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2195 DrawTextBuffer(sx + font_width, sy + font_height,
2196 level.envelope[envelope_nr].text, font_nr, max_xsize,
2197 xsize - 2, ysize - 2, 0, mask_mode,
2198 level.envelope[envelope_nr].autowrap,
2199 level.envelope[envelope_nr].centered, FALSE);
2201 redraw_mask |= REDRAW_FIELD;
2204 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2208 void ShowEnvelope(int envelope_nr)
2210 int element = EL_ENVELOPE_1 + envelope_nr;
2211 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2212 int sound_opening = element_info[element].sound[ACTION_OPENING];
2213 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2214 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2215 boolean no_delay = (tape.warp_forward);
2216 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2217 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2218 int anim_mode = graphic_info[graphic].anim_mode;
2219 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2220 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2222 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2224 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2226 if (anim_mode == ANIM_DEFAULT)
2227 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2229 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2232 Delay(wait_delay_value);
2234 WaitForEventToContinue();
2236 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2238 if (anim_mode != ANIM_NONE)
2239 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2241 if (anim_mode == ANIM_DEFAULT)
2242 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2244 game.envelope_active = FALSE;
2246 SetDrawtoField(DRAW_FIELDBUFFER);
2248 redraw_mask |= REDRAW_FIELD;
2252 static void setRequestBasePosition(int *x, int *y)
2254 int sx_base, sy_base;
2256 if (request.x != -1)
2257 sx_base = request.x;
2258 else if (request.align == ALIGN_LEFT)
2260 else if (request.align == ALIGN_RIGHT)
2261 sx_base = SX + SXSIZE;
2263 sx_base = SX + SXSIZE / 2;
2265 if (request.y != -1)
2266 sy_base = request.y;
2267 else if (request.valign == VALIGN_TOP)
2269 else if (request.valign == VALIGN_BOTTOM)
2270 sy_base = SY + SYSIZE;
2272 sy_base = SY + SYSIZE / 2;
2278 static void setRequestPositionExt(int *x, int *y, int width, int height,
2279 boolean add_border_size)
2281 int border_size = request.border_size;
2282 int sx_base, sy_base;
2285 setRequestBasePosition(&sx_base, &sy_base);
2287 if (request.align == ALIGN_LEFT)
2289 else if (request.align == ALIGN_RIGHT)
2290 sx = sx_base - width;
2292 sx = sx_base - width / 2;
2294 if (request.valign == VALIGN_TOP)
2296 else if (request.valign == VALIGN_BOTTOM)
2297 sy = sy_base - height;
2299 sy = sy_base - height / 2;
2301 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2302 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2304 if (add_border_size)
2314 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2316 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2319 void DrawEnvelopeRequest(char *text)
2321 int last_game_status = game_status; /* save current game status */
2322 char *text_final = text;
2323 char *text_door_style = NULL;
2324 int graphic = IMG_BACKGROUND_REQUEST;
2325 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2326 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2327 int font_nr = FONT_REQUEST;
2328 int font_width = getFontWidth(font_nr);
2329 int font_height = getFontHeight(font_nr);
2330 int border_size = request.border_size;
2331 int line_spacing = request.line_spacing;
2332 int line_height = font_height + line_spacing;
2333 int max_text_width = request.width - 2 * border_size;
2334 int max_text_height = request.height - 2 * border_size;
2335 int line_length = max_text_width / font_width;
2336 int max_lines = max_text_height / line_height;
2337 int text_width = line_length * font_width;
2338 int width = request.width;
2339 int height = request.height;
2340 int tile_size = MAX(request.step_offset, 1);
2341 int x_steps = width / tile_size;
2342 int y_steps = height / tile_size;
2343 int sx_offset = border_size;
2344 int sy_offset = border_size;
2348 if (request.centered)
2349 sx_offset = (request.width - text_width) / 2;
2351 if (request.wrap_single_words && !request.autowrap)
2353 char *src_text_ptr, *dst_text_ptr;
2355 text_door_style = checked_malloc(2 * strlen(text) + 1);
2357 src_text_ptr = text;
2358 dst_text_ptr = text_door_style;
2360 while (*src_text_ptr)
2362 if (*src_text_ptr == ' ' ||
2363 *src_text_ptr == '?' ||
2364 *src_text_ptr == '!')
2365 *dst_text_ptr++ = '\n';
2367 if (*src_text_ptr != ' ')
2368 *dst_text_ptr++ = *src_text_ptr;
2373 *dst_text_ptr = '\0';
2375 text_final = text_door_style;
2378 setRequestPosition(&sx, &sy, FALSE);
2380 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2382 for (y = 0; y < y_steps; y++)
2383 for (x = 0; x < x_steps; x++)
2384 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2385 x, y, x_steps, y_steps,
2386 tile_size, tile_size);
2388 /* force DOOR font inside door area */
2389 SetGameStatus(GAME_MODE_PSEUDO_DOOR);
2391 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2392 line_length, -1, max_lines, line_spacing, mask_mode,
2393 request.autowrap, request.centered, FALSE);
2395 SetGameStatus(last_game_status); /* restore current game status */
2397 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2398 RedrawGadget(tool_gadget[i]);
2400 // store readily prepared envelope request for later use when animating
2401 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2403 if (text_door_style)
2404 free(text_door_style);
2407 void AnimateEnvelopeRequest(int anim_mode, int action)
2409 int graphic = IMG_BACKGROUND_REQUEST;
2410 boolean draw_masked = graphic_info[graphic].draw_masked;
2411 int delay_value_normal = request.step_delay;
2412 int delay_value_fast = delay_value_normal / 2;
2413 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2414 boolean no_delay = (tape.warp_forward);
2415 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2416 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2417 unsigned int anim_delay = 0;
2419 int tile_size = MAX(request.step_offset, 1);
2420 int max_xsize = request.width / tile_size;
2421 int max_ysize = request.height / tile_size;
2422 int max_xsize_inner = max_xsize - 2;
2423 int max_ysize_inner = max_ysize - 2;
2425 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2426 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2427 int xend = max_xsize_inner;
2428 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2429 int xstep = (xstart < xend ? 1 : 0);
2430 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2432 int end = MAX(xend - xstart, yend - ystart);
2435 if (setup.quick_doors)
2442 for (i = start; i <= end; i++)
2444 int last_frame = end; // last frame of this "for" loop
2445 int x = xstart + i * xstep;
2446 int y = ystart + i * ystep;
2447 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2448 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2449 int xsize_size_left = (xsize - 1) * tile_size;
2450 int ysize_size_top = (ysize - 1) * tile_size;
2451 int max_xsize_pos = (max_xsize - 1) * tile_size;
2452 int max_ysize_pos = (max_ysize - 1) * tile_size;
2453 int width = xsize * tile_size;
2454 int height = ysize * tile_size;
2459 setRequestPosition(&src_x, &src_y, FALSE);
2460 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2462 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2464 for (yy = 0; yy < 2; yy++)
2466 for (xx = 0; xx < 2; xx++)
2468 int src_xx = src_x + xx * max_xsize_pos;
2469 int src_yy = src_y + yy * max_ysize_pos;
2470 int dst_xx = dst_x + xx * xsize_size_left;
2471 int dst_yy = dst_y + yy * ysize_size_top;
2472 int xx_size = (xx ? tile_size : xsize_size_left);
2473 int yy_size = (yy ? tile_size : ysize_size_top);
2476 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2477 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2479 BlitBitmap(bitmap_db_cross, backbuffer,
2480 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2484 redraw_mask |= REDRAW_FIELD;
2489 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2493 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2495 int graphic = IMG_BACKGROUND_REQUEST;
2496 int sound_opening = SND_REQUEST_OPENING;
2497 int sound_closing = SND_REQUEST_CLOSING;
2498 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2499 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2500 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2501 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2502 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2504 if (game_status == GAME_MODE_PLAYING)
2505 BlitScreenToBitmap(backbuffer);
2507 SetDrawtoField(DRAW_BACKBUFFER);
2509 // SetDrawBackgroundMask(REDRAW_NONE);
2511 if (action == ACTION_OPENING)
2513 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2515 if (req_state & REQ_ASK)
2517 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2518 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2520 else if (req_state & REQ_CONFIRM)
2522 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2524 else if (req_state & REQ_PLAYER)
2526 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2527 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2528 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2529 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2532 DrawEnvelopeRequest(text);
2534 if (game_status != GAME_MODE_MAIN)
2538 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2540 if (action == ACTION_OPENING)
2542 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2544 if (anim_mode == ANIM_DEFAULT)
2545 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2547 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2551 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2553 if (anim_mode != ANIM_NONE)
2554 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2556 if (anim_mode == ANIM_DEFAULT)
2557 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2560 game.envelope_active = FALSE;
2562 if (action == ACTION_CLOSING)
2564 if (game_status != GAME_MODE_MAIN)
2567 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2570 // SetDrawBackgroundMask(last_draw_background_mask);
2572 redraw_mask |= REDRAW_FIELD;
2574 if (game_status == GAME_MODE_MAIN)
2579 if (action == ACTION_CLOSING &&
2580 game_status == GAME_MODE_PLAYING &&
2581 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2582 SetDrawtoField(DRAW_FIELDBUFFER);
2585 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2589 int graphic = el2preimg(element);
2591 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2592 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2595 void DrawLevel(int draw_background_mask)
2599 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2600 SetDrawBackgroundMask(draw_background_mask);
2604 for (x = BX1; x <= BX2; x++)
2605 for (y = BY1; y <= BY2; y++)
2606 DrawScreenField(x, y);
2608 redraw_mask |= REDRAW_FIELD;
2611 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2616 for (x = 0; x < size_x; x++)
2617 for (y = 0; y < size_y; y++)
2618 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2620 redraw_mask |= REDRAW_FIELD;
2623 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2627 for (x = 0; x < size_x; x++)
2628 for (y = 0; y < size_y; y++)
2629 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2631 redraw_mask |= REDRAW_FIELD;
2634 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2636 boolean show_level_border = (BorderElement != EL_EMPTY);
2637 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2638 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2639 int tile_size = preview.tile_size;
2640 int preview_width = preview.xsize * tile_size;
2641 int preview_height = preview.ysize * tile_size;
2642 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2643 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2644 int real_preview_width = real_preview_xsize * tile_size;
2645 int real_preview_height = real_preview_ysize * tile_size;
2646 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2647 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2650 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2653 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2655 dst_x += (preview_width - real_preview_width) / 2;
2656 dst_y += (preview_height - real_preview_height) / 2;
2658 for (x = 0; x < real_preview_xsize; x++)
2660 for (y = 0; y < real_preview_ysize; y++)
2662 int lx = from_x + x + (show_level_border ? -1 : 0);
2663 int ly = from_y + y + (show_level_border ? -1 : 0);
2664 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2665 getBorderElement(lx, ly));
2667 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2668 element, tile_size);
2672 redraw_mask |= REDRAW_FIELD;
2675 #define MICROLABEL_EMPTY 0
2676 #define MICROLABEL_LEVEL_NAME 1
2677 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2678 #define MICROLABEL_LEVEL_AUTHOR 3
2679 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2680 #define MICROLABEL_IMPORTED_FROM 5
2681 #define MICROLABEL_IMPORTED_BY_HEAD 6
2682 #define MICROLABEL_IMPORTED_BY 7
2684 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2686 int max_text_width = SXSIZE;
2687 int font_width = getFontWidth(font_nr);
2689 if (pos->align == ALIGN_CENTER)
2690 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2691 else if (pos->align == ALIGN_RIGHT)
2692 max_text_width = pos->x;
2694 max_text_width = SXSIZE - pos->x;
2696 return max_text_width / font_width;
2699 static void DrawPreviewLevelLabelExt(int mode)
2701 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2702 char label_text[MAX_OUTPUT_LINESIZE + 1];
2703 int max_len_label_text;
2704 int font_nr = pos->font;
2707 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2710 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2711 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2712 mode == MICROLABEL_IMPORTED_BY_HEAD)
2713 font_nr = pos->font_alt;
2715 max_len_label_text = getMaxTextLength(pos, font_nr);
2717 if (pos->size != -1)
2718 max_len_label_text = pos->size;
2720 for (i = 0; i < max_len_label_text; i++)
2721 label_text[i] = ' ';
2722 label_text[max_len_label_text] = '\0';
2724 if (strlen(label_text) > 0)
2725 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2728 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2729 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2730 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2731 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2732 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2733 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2734 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2735 max_len_label_text);
2736 label_text[max_len_label_text] = '\0';
2738 if (strlen(label_text) > 0)
2739 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2741 redraw_mask |= REDRAW_FIELD;
2744 static void DrawPreviewLevelExt(boolean restart)
2746 static unsigned int scroll_delay = 0;
2747 static unsigned int label_delay = 0;
2748 static int from_x, from_y, scroll_direction;
2749 static int label_state, label_counter;
2750 unsigned int scroll_delay_value = preview.step_delay;
2751 boolean show_level_border = (BorderElement != EL_EMPTY);
2752 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2753 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2754 int last_game_status = game_status; /* save current game status */
2761 if (preview.anim_mode == ANIM_CENTERED)
2763 if (level_xsize > preview.xsize)
2764 from_x = (level_xsize - preview.xsize) / 2;
2765 if (level_ysize > preview.ysize)
2766 from_y = (level_ysize - preview.ysize) / 2;
2769 from_x += preview.xoffset;
2770 from_y += preview.yoffset;
2772 scroll_direction = MV_RIGHT;
2776 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2777 DrawPreviewLevelLabelExt(label_state);
2779 /* initialize delay counters */
2780 DelayReached(&scroll_delay, 0);
2781 DelayReached(&label_delay, 0);
2783 if (leveldir_current->name)
2785 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2786 char label_text[MAX_OUTPUT_LINESIZE + 1];
2787 int font_nr = pos->font;
2788 int max_len_label_text = getMaxTextLength(pos, font_nr);
2790 if (pos->size != -1)
2791 max_len_label_text = pos->size;
2793 strncpy(label_text, leveldir_current->name, max_len_label_text);
2794 label_text[max_len_label_text] = '\0';
2796 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2797 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2800 SetGameStatus(last_game_status); /* restore current game status */
2805 /* scroll preview level, if needed */
2806 if (preview.anim_mode != ANIM_NONE &&
2807 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2808 DelayReached(&scroll_delay, scroll_delay_value))
2810 switch (scroll_direction)
2815 from_x -= preview.step_offset;
2816 from_x = (from_x < 0 ? 0 : from_x);
2819 scroll_direction = MV_UP;
2823 if (from_x < level_xsize - preview.xsize)
2825 from_x += preview.step_offset;
2826 from_x = (from_x > level_xsize - preview.xsize ?
2827 level_xsize - preview.xsize : from_x);
2830 scroll_direction = MV_DOWN;
2836 from_y -= preview.step_offset;
2837 from_y = (from_y < 0 ? 0 : from_y);
2840 scroll_direction = MV_RIGHT;
2844 if (from_y < level_ysize - preview.ysize)
2846 from_y += preview.step_offset;
2847 from_y = (from_y > level_ysize - preview.ysize ?
2848 level_ysize - preview.ysize : from_y);
2851 scroll_direction = MV_LEFT;
2858 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2861 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2862 /* redraw micro level label, if needed */
2863 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2864 !strEqual(level.author, ANONYMOUS_NAME) &&
2865 !strEqual(level.author, leveldir_current->name) &&
2866 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2868 int max_label_counter = 23;
2870 if (leveldir_current->imported_from != NULL &&
2871 strlen(leveldir_current->imported_from) > 0)
2872 max_label_counter += 14;
2873 if (leveldir_current->imported_by != NULL &&
2874 strlen(leveldir_current->imported_by) > 0)
2875 max_label_counter += 14;
2877 label_counter = (label_counter + 1) % max_label_counter;
2878 label_state = (label_counter >= 0 && label_counter <= 7 ?
2879 MICROLABEL_LEVEL_NAME :
2880 label_counter >= 9 && label_counter <= 12 ?
2881 MICROLABEL_LEVEL_AUTHOR_HEAD :
2882 label_counter >= 14 && label_counter <= 21 ?
2883 MICROLABEL_LEVEL_AUTHOR :
2884 label_counter >= 23 && label_counter <= 26 ?
2885 MICROLABEL_IMPORTED_FROM_HEAD :
2886 label_counter >= 28 && label_counter <= 35 ?
2887 MICROLABEL_IMPORTED_FROM :
2888 label_counter >= 37 && label_counter <= 40 ?
2889 MICROLABEL_IMPORTED_BY_HEAD :
2890 label_counter >= 42 && label_counter <= 49 ?
2891 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2893 if (leveldir_current->imported_from == NULL &&
2894 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2895 label_state == MICROLABEL_IMPORTED_FROM))
2896 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2897 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2899 DrawPreviewLevelLabelExt(label_state);
2902 SetGameStatus(last_game_status); /* restore current game status */
2905 void DrawPreviewLevelInitial()
2907 DrawPreviewLevelExt(TRUE);
2910 void DrawPreviewLevelAnimation()
2912 DrawPreviewLevelExt(FALSE);
2915 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2916 int graphic, int sync_frame,
2919 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2921 if (mask_mode == USE_MASKING)
2922 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2924 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2927 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2928 int graphic, int sync_frame, int mask_mode)
2930 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2932 if (mask_mode == USE_MASKING)
2933 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2935 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
2938 inline static void DrawGraphicAnimation(int x, int y, int graphic)
2940 int lx = LEVELX(x), ly = LEVELY(y);
2942 if (!IN_SCR_FIELD(x, y))
2945 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
2946 graphic, GfxFrame[lx][ly], NO_MASKING);
2948 MarkTileDirty(x, y);
2951 void DrawFixedGraphicAnimation(int x, int y, int graphic)
2953 int lx = LEVELX(x), ly = LEVELY(y);
2955 if (!IN_SCR_FIELD(x, y))
2958 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
2959 graphic, GfxFrame[lx][ly], NO_MASKING);
2960 MarkTileDirty(x, y);
2963 void DrawLevelGraphicAnimation(int x, int y, int graphic)
2965 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2968 void DrawLevelElementAnimation(int x, int y, int element)
2970 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2972 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2975 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
2977 int sx = SCREENX(x), sy = SCREENY(y);
2979 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2982 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2985 DrawGraphicAnimation(sx, sy, graphic);
2988 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
2989 DrawLevelFieldCrumbled(x, y);
2991 if (GFX_CRUMBLED(Feld[x][y]))
2992 DrawLevelFieldCrumbled(x, y);
2996 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
2998 int sx = SCREENX(x), sy = SCREENY(y);
3001 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3004 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3006 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3009 DrawGraphicAnimation(sx, sy, graphic);
3011 if (GFX_CRUMBLED(element))
3012 DrawLevelFieldCrumbled(x, y);
3015 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3017 if (player->use_murphy)
3019 /* this works only because currently only one player can be "murphy" ... */
3020 static int last_horizontal_dir = MV_LEFT;
3021 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3023 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3024 last_horizontal_dir = move_dir;
3026 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3028 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3030 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3036 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3039 static boolean equalGraphics(int graphic1, int graphic2)
3041 struct GraphicInfo *g1 = &graphic_info[graphic1];
3042 struct GraphicInfo *g2 = &graphic_info[graphic2];
3044 return (g1->bitmap == g2->bitmap &&
3045 g1->src_x == g2->src_x &&
3046 g1->src_y == g2->src_y &&
3047 g1->anim_frames == g2->anim_frames &&
3048 g1->anim_delay == g2->anim_delay &&
3049 g1->anim_mode == g2->anim_mode);
3052 void DrawAllPlayers()
3056 for (i = 0; i < MAX_PLAYERS; i++)
3057 if (stored_player[i].active)
3058 DrawPlayer(&stored_player[i]);
3061 void DrawPlayerField(int x, int y)
3063 if (!IS_PLAYER(x, y))
3066 DrawPlayer(PLAYERINFO(x, y));
3069 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3071 void DrawPlayer(struct PlayerInfo *player)
3073 int jx = player->jx;
3074 int jy = player->jy;
3075 int move_dir = player->MovDir;
3076 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3077 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3078 int last_jx = (player->is_moving ? jx - dx : jx);
3079 int last_jy = (player->is_moving ? jy - dy : jy);
3080 int next_jx = jx + dx;
3081 int next_jy = jy + dy;
3082 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3083 boolean player_is_opaque = FALSE;
3084 int sx = SCREENX(jx), sy = SCREENY(jy);
3085 int sxx = 0, syy = 0;
3086 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3088 int action = ACTION_DEFAULT;
3089 int last_player_graphic = getPlayerGraphic(player, move_dir);
3090 int last_player_frame = player->Frame;
3093 /* GfxElement[][] is set to the element the player is digging or collecting;
3094 remove also for off-screen player if the player is not moving anymore */
3095 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3096 GfxElement[jx][jy] = EL_UNDEFINED;
3098 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3102 if (!IN_LEV_FIELD(jx, jy))
3104 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3105 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3106 printf("DrawPlayerField(): This should never happen!\n");
3111 if (element == EL_EXPLOSION)
3114 action = (player->is_pushing ? ACTION_PUSHING :
3115 player->is_digging ? ACTION_DIGGING :
3116 player->is_collecting ? ACTION_COLLECTING :
3117 player->is_moving ? ACTION_MOVING :
3118 player->is_snapping ? ACTION_SNAPPING :
3119 player->is_dropping ? ACTION_DROPPING :
3120 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3122 if (player->is_waiting)
3123 move_dir = player->dir_waiting;
3125 InitPlayerGfxAnimation(player, action, move_dir);
3127 /* ----------------------------------------------------------------------- */
3128 /* draw things in the field the player is leaving, if needed */
3129 /* ----------------------------------------------------------------------- */
3131 if (player->is_moving)
3133 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3135 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3137 if (last_element == EL_DYNAMITE_ACTIVE ||
3138 last_element == EL_EM_DYNAMITE_ACTIVE ||
3139 last_element == EL_SP_DISK_RED_ACTIVE)
3140 DrawDynamite(last_jx, last_jy);
3142 DrawLevelFieldThruMask(last_jx, last_jy);
3144 else if (last_element == EL_DYNAMITE_ACTIVE ||
3145 last_element == EL_EM_DYNAMITE_ACTIVE ||
3146 last_element == EL_SP_DISK_RED_ACTIVE)
3147 DrawDynamite(last_jx, last_jy);
3149 /* !!! this is not enough to prevent flickering of players which are
3150 moving next to each others without a free tile between them -- this
3151 can only be solved by drawing all players layer by layer (first the
3152 background, then the foreground etc.) !!! => TODO */
3153 else if (!IS_PLAYER(last_jx, last_jy))
3154 DrawLevelField(last_jx, last_jy);
3157 DrawLevelField(last_jx, last_jy);
3160 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3161 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3164 if (!IN_SCR_FIELD(sx, sy))
3167 /* ----------------------------------------------------------------------- */
3168 /* draw things behind the player, if needed */
3169 /* ----------------------------------------------------------------------- */
3172 DrawLevelElement(jx, jy, Back[jx][jy]);
3173 else if (IS_ACTIVE_BOMB(element))
3174 DrawLevelElement(jx, jy, EL_EMPTY);
3177 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3179 int old_element = GfxElement[jx][jy];
3180 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3181 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3183 if (GFX_CRUMBLED(old_element))
3184 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3186 DrawGraphic(sx, sy, old_graphic, frame);
3188 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3189 player_is_opaque = TRUE;
3193 GfxElement[jx][jy] = EL_UNDEFINED;
3195 /* make sure that pushed elements are drawn with correct frame rate */
3196 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3198 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3199 GfxFrame[jx][jy] = player->StepFrame;
3201 DrawLevelField(jx, jy);
3205 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3206 /* ----------------------------------------------------------------------- */
3207 /* draw player himself */
3208 /* ----------------------------------------------------------------------- */
3210 graphic = getPlayerGraphic(player, move_dir);
3212 /* in the case of changed player action or direction, prevent the current
3213 animation frame from being restarted for identical animations */
3214 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3215 player->Frame = last_player_frame;
3217 frame = getGraphicAnimationFrame(graphic, player->Frame);
3221 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3222 sxx = player->GfxPos;
3224 syy = player->GfxPos;
3227 if (player_is_opaque)
3228 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3230 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3232 if (SHIELD_ON(player))
3234 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3235 IMG_SHIELD_NORMAL_ACTIVE);
3236 int frame = getGraphicAnimationFrame(graphic, -1);
3238 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3242 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3245 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3246 sxx = player->GfxPos;
3248 syy = player->GfxPos;
3252 /* ----------------------------------------------------------------------- */
3253 /* draw things the player is pushing, if needed */
3254 /* ----------------------------------------------------------------------- */
3256 if (player->is_pushing && player->is_moving)
3258 int px = SCREENX(jx), py = SCREENY(jy);
3259 int pxx = (TILEX - ABS(sxx)) * dx;
3260 int pyy = (TILEY - ABS(syy)) * dy;
3261 int gfx_frame = GfxFrame[jx][jy];
3267 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3269 element = Feld[next_jx][next_jy];
3270 gfx_frame = GfxFrame[next_jx][next_jy];
3273 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3275 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3276 frame = getGraphicAnimationFrame(graphic, sync_frame);
3278 /* draw background element under pushed element (like the Sokoban field) */
3279 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3281 /* this allows transparent pushing animation over non-black background */
3284 DrawLevelElement(jx, jy, Back[jx][jy]);
3286 DrawLevelElement(jx, jy, EL_EMPTY);
3288 if (Back[next_jx][next_jy])
3289 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3291 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3293 else if (Back[next_jx][next_jy])
3294 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3297 /* do not draw (EM style) pushing animation when pushing is finished */
3298 /* (two-tile animations usually do not contain start and end frame) */
3299 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3300 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3302 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3304 /* masked drawing is needed for EMC style (double) movement graphics */
3305 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3306 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3310 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3311 /* ----------------------------------------------------------------------- */
3312 /* draw player himself */
3313 /* ----------------------------------------------------------------------- */
3315 graphic = getPlayerGraphic(player, move_dir);
3317 /* in the case of changed player action or direction, prevent the current
3318 animation frame from being restarted for identical animations */
3319 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3320 player->Frame = last_player_frame;
3322 frame = getGraphicAnimationFrame(graphic, player->Frame);
3326 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3327 sxx = player->GfxPos;
3329 syy = player->GfxPos;
3332 if (player_is_opaque)
3333 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3335 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3337 if (SHIELD_ON(player))
3339 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3340 IMG_SHIELD_NORMAL_ACTIVE);
3341 int frame = getGraphicAnimationFrame(graphic, -1);
3343 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3347 /* ----------------------------------------------------------------------- */
3348 /* draw things in front of player (active dynamite or dynabombs) */
3349 /* ----------------------------------------------------------------------- */
3351 if (IS_ACTIVE_BOMB(element))
3353 graphic = el2img(element);
3354 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3356 if (game.emulation == EMU_SUPAPLEX)
3357 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3359 DrawGraphicThruMask(sx, sy, graphic, frame);
3362 if (player_is_moving && last_element == EL_EXPLOSION)
3364 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3365 GfxElement[last_jx][last_jy] : EL_EMPTY);
3366 int graphic = el_act2img(element, ACTION_EXPLODING);
3367 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3368 int phase = ExplodePhase[last_jx][last_jy] - 1;
3369 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3372 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3375 /* ----------------------------------------------------------------------- */
3376 /* draw elements the player is just walking/passing through/under */
3377 /* ----------------------------------------------------------------------- */
3379 if (player_is_moving)
3381 /* handle the field the player is leaving ... */
3382 if (IS_ACCESSIBLE_INSIDE(last_element))
3383 DrawLevelField(last_jx, last_jy);
3384 else if (IS_ACCESSIBLE_UNDER(last_element))
3385 DrawLevelFieldThruMask(last_jx, last_jy);
3388 /* do not redraw accessible elements if the player is just pushing them */
3389 if (!player_is_moving || !player->is_pushing)
3391 /* ... and the field the player is entering */
3392 if (IS_ACCESSIBLE_INSIDE(element))
3393 DrawLevelField(jx, jy);
3394 else if (IS_ACCESSIBLE_UNDER(element))
3395 DrawLevelFieldThruMask(jx, jy);
3398 MarkTileDirty(sx, sy);
3401 /* ------------------------------------------------------------------------- */
3403 void WaitForEventToContinue()
3405 boolean still_wait = TRUE;
3407 /* simulate releasing mouse button over last gadget, if still pressed */
3409 HandleGadgets(-1, -1, 0);
3411 button_status = MB_RELEASED;
3425 case EVENT_BUTTONPRESS:
3426 case EVENT_KEYPRESS:
3430 case EVENT_KEYRELEASE:
3431 ClearPlayerAction();
3435 HandleOtherEvents(&event);
3439 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3446 WaitUntilDelayReached(&sync_frame_delay, sync_frame_delay_value);
3450 #define MAX_REQUEST_LINES 13
3451 #define MAX_REQUEST_LINE_FONT1_LEN 7
3452 #define MAX_REQUEST_LINE_FONT2_LEN 10
3454 static int RequestHandleEvents(unsigned int req_state)
3456 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3457 local_player->LevelSolved_GameEnd);
3458 int width = request.width;
3459 int height = request.height;
3463 setRequestPosition(&sx, &sy, FALSE);
3465 button_status = MB_RELEASED;
3467 request_gadget_id = -1;
3474 SetDrawtoField(DRAW_FIELDBUFFER);
3476 HandleGameActions();
3478 SetDrawtoField(DRAW_BACKBUFFER);
3480 if (global.use_envelope_request)
3482 /* copy current state of request area to middle of playfield area */
3483 BlitBitmap(bitmap_db_cross, drawto, sx, sy, width, height, sx, sy);
3491 while (NextValidEvent(&event))
3495 case EVENT_BUTTONPRESS:
3496 case EVENT_BUTTONRELEASE:
3497 case EVENT_MOTIONNOTIFY:
3501 if (event.type == EVENT_MOTIONNOTIFY)
3506 motion_status = TRUE;
3507 mx = ((MotionEvent *) &event)->x;
3508 my = ((MotionEvent *) &event)->y;
3512 motion_status = FALSE;
3513 mx = ((ButtonEvent *) &event)->x;
3514 my = ((ButtonEvent *) &event)->y;
3515 if (event.type == EVENT_BUTTONPRESS)
3516 button_status = ((ButtonEvent *) &event)->button;
3518 button_status = MB_RELEASED;
3521 /* this sets 'request_gadget_id' */
3522 HandleGadgets(mx, my, button_status);
3524 switch (request_gadget_id)
3526 case TOOL_CTRL_ID_YES:
3529 case TOOL_CTRL_ID_NO:
3532 case TOOL_CTRL_ID_CONFIRM:
3533 result = TRUE | FALSE;
3536 case TOOL_CTRL_ID_PLAYER_1:
3539 case TOOL_CTRL_ID_PLAYER_2:
3542 case TOOL_CTRL_ID_PLAYER_3:
3545 case TOOL_CTRL_ID_PLAYER_4:
3556 case EVENT_KEYPRESS:
3557 switch (GetEventKey((KeyEvent *)&event, TRUE))
3560 if (req_state & REQ_CONFIRM)
3565 #if defined(TARGET_SDL2)
3572 #if defined(TARGET_SDL2)
3582 if (req_state & REQ_PLAYER)
3586 case EVENT_KEYRELEASE:
3587 ClearPlayerAction();
3591 HandleOtherEvents(&event);
3596 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3598 int joy = AnyJoystick();
3600 if (joy & JOY_BUTTON_1)
3602 else if (joy & JOY_BUTTON_2)
3608 if (global.use_envelope_request)
3610 /* copy back current state of pressed buttons inside request area */
3611 BlitBitmap(drawto, bitmap_db_cross, sx, sy, width, height, sx, sy);
3621 WaitUntilDelayReached(&sync_frame_delay, sync_frame_delay_value);
3627 static boolean RequestDoor(char *text, unsigned int req_state)
3629 unsigned int old_door_state;
3630 int last_game_status = game_status; /* save current game status */
3631 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3632 int font_nr = FONT_TEXT_2;
3637 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3639 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3640 font_nr = FONT_TEXT_1;
3643 if (game_status == GAME_MODE_PLAYING)
3644 BlitScreenToBitmap(backbuffer);
3646 /* disable deactivated drawing when quick-loading level tape recording */
3647 if (tape.playing && tape.deactivate_display)
3648 TapeDeactivateDisplayOff(TRUE);
3650 SetMouseCursor(CURSOR_DEFAULT);
3652 #if defined(NETWORK_AVALIABLE)
3653 /* pause network game while waiting for request to answer */
3654 if (options.network &&
3655 game_status == GAME_MODE_PLAYING &&
3656 req_state & REQUEST_WAIT_FOR_INPUT)
3657 SendToServer_PausePlaying();
3660 old_door_state = GetDoorState();
3662 /* simulate releasing mouse button over last gadget, if still pressed */
3664 HandleGadgets(-1, -1, 0);
3668 /* draw released gadget before proceeding */
3671 if (old_door_state & DOOR_OPEN_1)
3673 CloseDoor(DOOR_CLOSE_1);
3675 /* save old door content */
3676 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3677 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3680 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3681 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3683 /* clear door drawing field */
3684 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3686 /* force DOOR font inside door area */
3687 SetGameStatus(GAME_MODE_PSEUDO_DOOR);
3689 /* write text for request */
3690 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3692 char text_line[max_request_line_len + 1];
3698 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3700 tc = *(text_ptr + tx);
3701 // if (!tc || tc == ' ')
3702 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3706 if ((tc == '?' || tc == '!') && tl == 0)
3716 strncpy(text_line, text_ptr, tl);
3719 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3720 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3721 text_line, font_nr);
3723 text_ptr += tl + (tc == ' ' ? 1 : 0);
3724 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3727 SetGameStatus(last_game_status); /* restore current game status */
3729 if (req_state & REQ_ASK)
3731 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3732 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3734 else if (req_state & REQ_CONFIRM)
3736 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3738 else if (req_state & REQ_PLAYER)
3740 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3741 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3742 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3743 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3746 /* copy request gadgets to door backbuffer */
3747 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3749 OpenDoor(DOOR_OPEN_1);
3751 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3753 if (game_status == GAME_MODE_PLAYING)
3755 SetPanelBackground();
3756 SetDrawBackgroundMask(REDRAW_DOOR_1);
3760 SetDrawBackgroundMask(REDRAW_FIELD);
3766 if (game_status != GAME_MODE_MAIN)
3769 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3771 // ---------- handle request buttons ----------
3772 result = RequestHandleEvents(req_state);
3774 if (game_status != GAME_MODE_MAIN)
3779 if (!(req_state & REQ_STAY_OPEN))
3781 CloseDoor(DOOR_CLOSE_1);
3783 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3784 (req_state & REQ_REOPEN))
3785 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3790 if (game_status == GAME_MODE_PLAYING)
3792 SetPanelBackground();
3793 SetDrawBackgroundMask(REDRAW_DOOR_1);
3797 SetDrawBackgroundMask(REDRAW_FIELD);
3800 #if defined(NETWORK_AVALIABLE)
3801 /* continue network game after request */
3802 if (options.network &&
3803 game_status == GAME_MODE_PLAYING &&
3804 req_state & REQUEST_WAIT_FOR_INPUT)
3805 SendToServer_ContinuePlaying();
3808 /* restore deactivated drawing when quick-loading level tape recording */
3809 if (tape.playing && tape.deactivate_display)
3810 TapeDeactivateDisplayOn();
3815 static boolean RequestEnvelope(char *text, unsigned int req_state)
3819 if (game_status == GAME_MODE_PLAYING)
3820 BlitScreenToBitmap(backbuffer);
3822 /* disable deactivated drawing when quick-loading level tape recording */
3823 if (tape.playing && tape.deactivate_display)
3824 TapeDeactivateDisplayOff(TRUE);
3826 SetMouseCursor(CURSOR_DEFAULT);
3828 #if defined(NETWORK_AVALIABLE)
3829 /* pause network game while waiting for request to answer */
3830 if (options.network &&
3831 game_status == GAME_MODE_PLAYING &&
3832 req_state & REQUEST_WAIT_FOR_INPUT)
3833 SendToServer_PausePlaying();
3836 /* simulate releasing mouse button over last gadget, if still pressed */
3838 HandleGadgets(-1, -1, 0);
3842 // (replace with setting corresponding request background)
3843 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3844 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3846 /* clear door drawing field */
3847 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3849 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3851 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3853 if (game_status == GAME_MODE_PLAYING)
3855 SetPanelBackground();
3856 SetDrawBackgroundMask(REDRAW_DOOR_1);
3860 SetDrawBackgroundMask(REDRAW_FIELD);
3866 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3868 // ---------- handle request buttons ----------
3869 result = RequestHandleEvents(req_state);
3871 if (game_status != GAME_MODE_MAIN)
3876 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3880 if (game_status == GAME_MODE_PLAYING)
3882 SetPanelBackground();
3883 SetDrawBackgroundMask(REDRAW_DOOR_1);
3887 SetDrawBackgroundMask(REDRAW_FIELD);
3890 #if defined(NETWORK_AVALIABLE)
3891 /* continue network game after request */
3892 if (options.network &&
3893 game_status == GAME_MODE_PLAYING &&
3894 req_state & REQUEST_WAIT_FOR_INPUT)
3895 SendToServer_ContinuePlaying();
3898 /* restore deactivated drawing when quick-loading level tape recording */
3899 if (tape.playing && tape.deactivate_display)
3900 TapeDeactivateDisplayOn();
3905 boolean Request(char *text, unsigned int req_state)
3907 if (global.use_envelope_request)
3908 return RequestEnvelope(text, req_state);
3910 return RequestDoor(text, req_state);
3913 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3915 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3916 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3919 if (dpo1->sort_priority != dpo2->sort_priority)
3920 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3922 compare_result = dpo1->nr - dpo2->nr;
3924 return compare_result;
3927 void InitGraphicCompatibilityInfo_Doors()
3933 struct DoorInfo *door;
3937 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
3938 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
3940 { -1, -1, -1, NULL }
3942 struct Rect door_rect_list[] =
3944 { DX, DY, DXSIZE, DYSIZE },
3945 { VX, VY, VXSIZE, VYSIZE }
3949 for (i = 0; doors[i].door_token != -1; i++)
3951 int door_token = doors[i].door_token;
3952 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
3953 int part_1 = doors[i].part_1;
3954 int part_8 = doors[i].part_8;
3955 int part_2 = part_1 + 1;
3956 int part_3 = part_1 + 2;
3957 struct DoorInfo *door = doors[i].door;
3958 struct Rect *door_rect = &door_rect_list[door_index];
3959 boolean door_gfx_redefined = FALSE;
3961 /* check if any door part graphic definitions have been redefined */
3963 for (j = 0; door_part_controls[j].door_token != -1; j++)
3965 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3966 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
3968 if (dpc->door_token == door_token && fi->redefined)
3969 door_gfx_redefined = TRUE;
3972 /* check for old-style door graphic/animation modifications */
3974 if (!door_gfx_redefined)
3976 if (door->anim_mode & ANIM_STATIC_PANEL)
3978 door->panel.step_xoffset = 0;
3979 door->panel.step_yoffset = 0;
3982 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
3984 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
3985 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
3986 int num_door_steps, num_panel_steps;
3988 /* remove door part graphics other than the two default wings */
3990 for (j = 0; door_part_controls[j].door_token != -1; j++)
3992 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3993 struct GraphicInfo *g = &graphic_info[dpc->graphic];
3995 if (dpc->graphic >= part_3 &&
3996 dpc->graphic <= part_8)
4000 /* set graphics and screen positions of the default wings */
4002 g_part_1->width = door_rect->width;
4003 g_part_1->height = door_rect->height;
4004 g_part_2->width = door_rect->width;
4005 g_part_2->height = door_rect->height;
4006 g_part_2->src_x = door_rect->width;
4007 g_part_2->src_y = g_part_1->src_y;
4009 door->part_2.x = door->part_1.x;
4010 door->part_2.y = door->part_1.y;
4012 if (door->width != -1)
4014 g_part_1->width = door->width;
4015 g_part_2->width = door->width;
4017 // special treatment for graphics and screen position of right wing
4018 g_part_2->src_x += door_rect->width - door->width;
4019 door->part_2.x += door_rect->width - door->width;
4022 if (door->height != -1)
4024 g_part_1->height = door->height;
4025 g_part_2->height = door->height;
4027 // special treatment for graphics and screen position of bottom wing
4028 g_part_2->src_y += door_rect->height - door->height;
4029 door->part_2.y += door_rect->height - door->height;
4032 /* set animation delays for the default wings and panels */
4034 door->part_1.step_delay = door->step_delay;
4035 door->part_2.step_delay = door->step_delay;
4036 door->panel.step_delay = door->step_delay;
4038 /* set animation draw order for the default wings */
4040 door->part_1.sort_priority = 2; /* draw left wing over ... */
4041 door->part_2.sort_priority = 1; /* ... right wing */
4043 /* set animation draw offset for the default wings */
4045 if (door->anim_mode & ANIM_HORIZONTAL)
4047 door->part_1.step_xoffset = door->step_offset;
4048 door->part_1.step_yoffset = 0;
4049 door->part_2.step_xoffset = door->step_offset * -1;
4050 door->part_2.step_yoffset = 0;
4052 num_door_steps = g_part_1->width / door->step_offset;
4054 else // ANIM_VERTICAL
4056 door->part_1.step_xoffset = 0;
4057 door->part_1.step_yoffset = door->step_offset;
4058 door->part_2.step_xoffset = 0;
4059 door->part_2.step_yoffset = door->step_offset * -1;
4061 num_door_steps = g_part_1->height / door->step_offset;
4064 /* set animation draw offset for the default panels */
4066 if (door->step_offset > 1)
4068 num_panel_steps = 2 * door_rect->height / door->step_offset;
4069 door->panel.start_step = num_panel_steps - num_door_steps;
4070 door->panel.start_step_closing = door->panel.start_step;
4074 num_panel_steps = door_rect->height / door->step_offset;
4075 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4076 door->panel.start_step_closing = door->panel.start_step;
4077 door->panel.step_delay *= 2;
4088 for (i = 0; door_part_controls[i].door_token != -1; i++)
4090 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4091 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4093 /* initialize "start_step_opening" and "start_step_closing", if needed */
4094 if (dpc->pos->start_step_opening == 0 &&
4095 dpc->pos->start_step_closing == 0)
4097 // dpc->pos->start_step_opening = dpc->pos->start_step;
4098 dpc->pos->start_step_closing = dpc->pos->start_step;
4101 /* fill structure for door part draw order (sorted below) */
4103 dpo->sort_priority = dpc->pos->sort_priority;
4106 /* sort door part controls according to sort_priority and graphic number */
4107 qsort(door_part_order, MAX_DOOR_PARTS,
4108 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4111 unsigned int OpenDoor(unsigned int door_state)
4113 if (door_state & DOOR_COPY_BACK)
4115 if (door_state & DOOR_OPEN_1)
4116 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4117 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4119 if (door_state & DOOR_OPEN_2)
4120 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4121 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4123 door_state &= ~DOOR_COPY_BACK;
4126 return MoveDoor(door_state);
4129 unsigned int CloseDoor(unsigned int door_state)
4131 unsigned int old_door_state = GetDoorState();
4133 if (!(door_state & DOOR_NO_COPY_BACK))
4135 if (old_door_state & DOOR_OPEN_1)
4136 BlitBitmap(backbuffer, bitmap_db_door_1,
4137 DX, DY, DXSIZE, DYSIZE, 0, 0);
4139 if (old_door_state & DOOR_OPEN_2)
4140 BlitBitmap(backbuffer, bitmap_db_door_2,
4141 VX, VY, VXSIZE, VYSIZE, 0, 0);
4143 door_state &= ~DOOR_NO_COPY_BACK;
4146 return MoveDoor(door_state);
4149 unsigned int GetDoorState()
4151 return MoveDoor(DOOR_GET_STATE);
4154 unsigned int SetDoorState(unsigned int door_state)
4156 return MoveDoor(door_state | DOOR_SET_STATE);
4159 int euclid(int a, int b)
4161 return (b ? euclid(b, a % b) : a);
4164 unsigned int MoveDoor(unsigned int door_state)
4166 struct Rect door_rect_list[] =
4168 { DX, DY, DXSIZE, DYSIZE },
4169 { VX, VY, VXSIZE, VYSIZE }
4171 static int door1 = DOOR_OPEN_1;
4172 static int door2 = DOOR_CLOSE_2;
4173 unsigned int door_delay = 0;
4174 unsigned int door_delay_value;
4177 if (door_state == DOOR_GET_STATE)
4178 return (door1 | door2);
4180 if (door_state & DOOR_SET_STATE)
4182 if (door_state & DOOR_ACTION_1)
4183 door1 = door_state & DOOR_ACTION_1;
4184 if (door_state & DOOR_ACTION_2)
4185 door2 = door_state & DOOR_ACTION_2;
4187 return (door1 | door2);
4190 if (!(door_state & DOOR_FORCE_REDRAW))
4192 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4193 door_state &= ~DOOR_OPEN_1;
4194 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4195 door_state &= ~DOOR_CLOSE_1;
4196 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4197 door_state &= ~DOOR_OPEN_2;
4198 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4199 door_state &= ~DOOR_CLOSE_2;
4202 if (global.autoplay_leveldir)
4204 door_state |= DOOR_NO_DELAY;
4205 door_state &= ~DOOR_CLOSE_ALL;
4208 if (game_status == GAME_MODE_EDITOR)
4209 door_state |= DOOR_NO_DELAY;
4211 if (door_state & DOOR_ACTION)
4213 boolean door_panel_drawn[NUM_DOORS];
4214 boolean panel_has_doors[NUM_DOORS];
4215 boolean door_part_skip[MAX_DOOR_PARTS];
4216 boolean door_part_done[MAX_DOOR_PARTS];
4217 boolean door_part_done_all;
4218 int num_steps[MAX_DOOR_PARTS];
4219 int max_move_delay = 0; // delay for complete animations of all doors
4220 int max_step_delay = 0; // delay (ms) between two animation frames
4221 int num_move_steps = 0; // number of animation steps for all doors
4222 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4223 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4224 int current_move_delay = 0;
4228 for (i = 0; i < NUM_DOORS; i++)
4229 panel_has_doors[i] = FALSE;
4231 for (i = 0; i < MAX_DOOR_PARTS; i++)
4233 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4234 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4235 int door_token = dpc->door_token;
4237 door_part_done[i] = FALSE;
4238 door_part_skip[i] = (!(door_state & door_token) ||
4242 for (i = 0; i < MAX_DOOR_PARTS; i++)
4244 int nr = door_part_order[i].nr;
4245 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4246 struct DoorPartPosInfo *pos = dpc->pos;
4247 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4248 int door_token = dpc->door_token;
4249 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4250 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4251 int step_xoffset = ABS(pos->step_xoffset);
4252 int step_yoffset = ABS(pos->step_yoffset);
4253 int step_delay = pos->step_delay;
4254 int current_door_state = door_state & door_token;
4255 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4256 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4257 boolean part_opening = (is_panel ? door_closing : door_opening);
4258 int start_step = (part_opening ? pos->start_step_opening :
4259 pos->start_step_closing);
4260 float move_xsize = (step_xoffset ? g->width : 0);
4261 float move_ysize = (step_yoffset ? g->height : 0);
4262 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4263 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4264 int move_steps = (move_xsteps && move_ysteps ?
4265 MIN(move_xsteps, move_ysteps) :
4266 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4267 int move_delay = move_steps * step_delay;
4269 if (door_part_skip[nr])
4272 max_move_delay = MAX(max_move_delay, move_delay);
4273 max_step_delay = (max_step_delay == 0 ? step_delay :
4274 euclid(max_step_delay, step_delay));
4275 num_steps[nr] = move_steps;
4279 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4281 panel_has_doors[door_index] = TRUE;
4285 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4287 num_move_steps = max_move_delay / max_step_delay;
4288 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4290 door_delay_value = max_step_delay;
4292 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4294 start = num_move_steps - 1;
4298 /* opening door sound has priority over simultaneously closing door */
4299 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4300 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4301 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4302 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4305 for (k = start; k < num_move_steps; k++)
4307 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4309 door_part_done_all = TRUE;
4311 for (i = 0; i < NUM_DOORS; i++)
4312 door_panel_drawn[i] = FALSE;
4314 for (i = 0; i < MAX_DOOR_PARTS; i++)
4316 int nr = door_part_order[i].nr;
4317 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4318 struct DoorPartPosInfo *pos = dpc->pos;
4319 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4320 int door_token = dpc->door_token;
4321 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4322 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4323 boolean is_panel_and_door_has_closed = FALSE;
4324 struct Rect *door_rect = &door_rect_list[door_index];
4325 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4327 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4328 int current_door_state = door_state & door_token;
4329 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4330 boolean door_closing = !door_opening;
4331 boolean part_opening = (is_panel ? door_closing : door_opening);
4332 boolean part_closing = !part_opening;
4333 int start_step = (part_opening ? pos->start_step_opening :
4334 pos->start_step_closing);
4335 int step_delay = pos->step_delay;
4336 int step_factor = step_delay / max_step_delay;
4337 int k1 = (step_factor ? k / step_factor + 1 : k);
4338 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4339 int kk = MAX(0, k2);
4342 int src_x, src_y, src_xx, src_yy;
4343 int dst_x, dst_y, dst_xx, dst_yy;
4346 if (door_part_skip[nr])
4349 if (!(door_state & door_token))
4357 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4358 int kk_door = MAX(0, k2_door);
4359 int sync_frame = kk_door * door_delay_value;
4360 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4362 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4367 if (!door_panel_drawn[door_index])
4369 ClearRectangle(drawto, door_rect->x, door_rect->y,
4370 door_rect->width, door_rect->height);
4372 door_panel_drawn[door_index] = TRUE;
4375 // draw opening or closing door parts
4377 if (pos->step_xoffset < 0) // door part on right side
4380 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4383 if (dst_xx + width > door_rect->width)
4384 width = door_rect->width - dst_xx;
4386 else // door part on left side
4389 dst_xx = pos->x - kk * pos->step_xoffset;
4393 src_xx = ABS(dst_xx);
4397 width = g->width - src_xx;
4399 if (width > door_rect->width)
4400 width = door_rect->width;
4402 // printf("::: k == %d [%d] \n", k, start_step);
4405 if (pos->step_yoffset < 0) // door part on bottom side
4408 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4411 if (dst_yy + height > door_rect->height)
4412 height = door_rect->height - dst_yy;
4414 else // door part on top side
4417 dst_yy = pos->y - kk * pos->step_yoffset;
4421 src_yy = ABS(dst_yy);
4425 height = g->height - src_yy;
4428 src_x = g_src_x + src_xx;
4429 src_y = g_src_y + src_yy;
4431 dst_x = door_rect->x + dst_xx;
4432 dst_y = door_rect->y + dst_yy;
4434 is_panel_and_door_has_closed =
4437 panel_has_doors[door_index] &&
4438 k >= num_move_steps_doors_only - 1);
4440 if (width >= 0 && width <= g->width &&
4441 height >= 0 && height <= g->height &&
4442 !is_panel_and_door_has_closed)
4444 if (is_panel || !pos->draw_masked)
4445 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4448 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4452 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4454 if ((part_opening && (width < 0 || height < 0)) ||
4455 (part_closing && (width >= g->width && height >= g->height)))
4456 door_part_done[nr] = TRUE;
4458 // continue door part animations, but not panel after door has closed
4459 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4460 door_part_done_all = FALSE;
4463 if (!(door_state & DOOR_NO_DELAY))
4467 if (game_status == GAME_MODE_MAIN)
4470 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4472 current_move_delay += max_step_delay;
4475 if (door_part_done_all)
4480 if (door_state & DOOR_ACTION_1)
4481 door1 = door_state & DOOR_ACTION_1;
4482 if (door_state & DOOR_ACTION_2)
4483 door2 = door_state & DOOR_ACTION_2;
4485 // draw masked border over door area
4486 DrawMaskedBorder(REDRAW_DOOR_1);
4487 DrawMaskedBorder(REDRAW_DOOR_2);
4489 return (door1 | door2);
4492 static boolean useSpecialEditorDoor()
4494 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4495 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4497 // do not draw special editor door if editor border defined or redefined
4498 if (graphic_info[graphic].bitmap != NULL || redefined)
4501 // do not draw special editor door if global border defined to be empty
4502 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4505 // do not draw special editor door if viewport definitions do not match
4509 EY + EYSIZE != VY + VYSIZE)
4515 void DrawSpecialEditorDoor()
4517 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4518 int top_border_width = gfx1->width;
4519 int top_border_height = gfx1->height;
4520 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4521 int ex = EX - outer_border;
4522 int ey = EY - outer_border;
4523 int vy = VY - outer_border;
4524 int exsize = EXSIZE + 2 * outer_border;
4526 if (!useSpecialEditorDoor())
4529 /* draw bigger level editor toolbox window */
4530 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4531 top_border_width, top_border_height, ex, ey - top_border_height);
4532 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4533 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4535 redraw_mask |= REDRAW_ALL;
4538 void UndrawSpecialEditorDoor()
4540 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4541 int top_border_width = gfx1->width;
4542 int top_border_height = gfx1->height;
4543 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4544 int ex = EX - outer_border;
4545 int ey = EY - outer_border;
4546 int ey_top = ey - top_border_height;
4547 int exsize = EXSIZE + 2 * outer_border;
4548 int eysize = EYSIZE + 2 * outer_border;
4550 if (!useSpecialEditorDoor())
4553 /* draw normal tape recorder window */
4554 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4556 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4557 ex, ey_top, top_border_width, top_border_height,
4559 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4560 ex, ey, exsize, eysize, ex, ey);
4564 // if screen background is set to "[NONE]", clear editor toolbox window
4565 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4566 ClearRectangle(drawto, ex, ey, exsize, eysize);
4569 redraw_mask |= REDRAW_ALL;
4573 /* ---------- new tool button stuff ---------------------------------------- */
4578 struct TextPosInfo *pos;
4581 } toolbutton_info[NUM_TOOL_BUTTONS] =
4584 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4585 TOOL_CTRL_ID_YES, "yes"
4588 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4589 TOOL_CTRL_ID_NO, "no"
4592 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4593 TOOL_CTRL_ID_CONFIRM, "confirm"
4596 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4597 TOOL_CTRL_ID_PLAYER_1, "player 1"
4600 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4601 TOOL_CTRL_ID_PLAYER_2, "player 2"
4604 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4605 TOOL_CTRL_ID_PLAYER_3, "player 3"
4608 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4609 TOOL_CTRL_ID_PLAYER_4, "player 4"
4613 void CreateToolButtons()
4617 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4619 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4620 struct TextPosInfo *pos = toolbutton_info[i].pos;
4621 struct GadgetInfo *gi;
4622 Bitmap *deco_bitmap = None;
4623 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4624 unsigned int event_mask = GD_EVENT_RELEASED;
4627 int gd_x = gfx->src_x;
4628 int gd_y = gfx->src_y;
4629 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4630 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4633 if (global.use_envelope_request)
4634 setRequestPosition(&dx, &dy, TRUE);
4636 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4638 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4640 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4641 pos->size, &deco_bitmap, &deco_x, &deco_y);
4642 deco_xpos = (gfx->width - pos->size) / 2;
4643 deco_ypos = (gfx->height - pos->size) / 2;
4646 gi = CreateGadget(GDI_CUSTOM_ID, id,
4647 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4648 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4649 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4650 GDI_WIDTH, gfx->width,
4651 GDI_HEIGHT, gfx->height,
4652 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4653 GDI_STATE, GD_BUTTON_UNPRESSED,
4654 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4655 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4656 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4657 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4658 GDI_DECORATION_SIZE, pos->size, pos->size,
4659 GDI_DECORATION_SHIFTING, 1, 1,
4660 GDI_DIRECT_DRAW, FALSE,
4661 GDI_EVENT_MASK, event_mask,
4662 GDI_CALLBACK_ACTION, HandleToolButtons,
4666 Error(ERR_EXIT, "cannot create gadget");
4668 tool_gadget[id] = gi;
4672 void FreeToolButtons()
4676 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4677 FreeGadget(tool_gadget[i]);
4680 static void UnmapToolButtons()
4684 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4685 UnmapGadget(tool_gadget[i]);
4688 static void HandleToolButtons(struct GadgetInfo *gi)
4690 request_gadget_id = gi->custom_id;
4693 static struct Mapping_EM_to_RND_object
4696 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4697 boolean is_backside; /* backside of moving element */
4703 em_object_mapping_list[] =
4706 Xblank, TRUE, FALSE,
4710 Yacid_splash_eB, FALSE, FALSE,
4711 EL_ACID_SPLASH_RIGHT, -1, -1
4714 Yacid_splash_wB, FALSE, FALSE,
4715 EL_ACID_SPLASH_LEFT, -1, -1
4718 #ifdef EM_ENGINE_BAD_ROLL
4720 Xstone_force_e, FALSE, FALSE,
4721 EL_ROCK, -1, MV_BIT_RIGHT
4724 Xstone_force_w, FALSE, FALSE,
4725 EL_ROCK, -1, MV_BIT_LEFT
4728 Xnut_force_e, FALSE, FALSE,
4729 EL_NUT, -1, MV_BIT_RIGHT
4732 Xnut_force_w, FALSE, FALSE,
4733 EL_NUT, -1, MV_BIT_LEFT
4736 Xspring_force_e, FALSE, FALSE,
4737 EL_SPRING, -1, MV_BIT_RIGHT
4740 Xspring_force_w, FALSE, FALSE,
4741 EL_SPRING, -1, MV_BIT_LEFT
4744 Xemerald_force_e, FALSE, FALSE,
4745 EL_EMERALD, -1, MV_BIT_RIGHT
4748 Xemerald_force_w, FALSE, FALSE,
4749 EL_EMERALD, -1, MV_BIT_LEFT
4752 Xdiamond_force_e, FALSE, FALSE,
4753 EL_DIAMOND, -1, MV_BIT_RIGHT
4756 Xdiamond_force_w, FALSE, FALSE,
4757 EL_DIAMOND, -1, MV_BIT_LEFT
4760 Xbomb_force_e, FALSE, FALSE,
4761 EL_BOMB, -1, MV_BIT_RIGHT
4764 Xbomb_force_w, FALSE, FALSE,
4765 EL_BOMB, -1, MV_BIT_LEFT
4767 #endif /* EM_ENGINE_BAD_ROLL */
4770 Xstone, TRUE, FALSE,
4774 Xstone_pause, FALSE, FALSE,
4778 Xstone_fall, FALSE, FALSE,
4782 Ystone_s, FALSE, FALSE,
4783 EL_ROCK, ACTION_FALLING, -1
4786 Ystone_sB, FALSE, TRUE,
4787 EL_ROCK, ACTION_FALLING, -1
4790 Ystone_e, FALSE, FALSE,
4791 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4794 Ystone_eB, FALSE, TRUE,
4795 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4798 Ystone_w, FALSE, FALSE,
4799 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4802 Ystone_wB, FALSE, TRUE,
4803 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4810 Xnut_pause, FALSE, FALSE,
4814 Xnut_fall, FALSE, FALSE,
4818 Ynut_s, FALSE, FALSE,
4819 EL_NUT, ACTION_FALLING, -1
4822 Ynut_sB, FALSE, TRUE,
4823 EL_NUT, ACTION_FALLING, -1
4826 Ynut_e, FALSE, FALSE,
4827 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4830 Ynut_eB, FALSE, TRUE,
4831 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4834 Ynut_w, FALSE, FALSE,
4835 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4838 Ynut_wB, FALSE, TRUE,
4839 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4842 Xbug_n, TRUE, FALSE,
4846 Xbug_e, TRUE, FALSE,
4847 EL_BUG_RIGHT, -1, -1
4850 Xbug_s, TRUE, FALSE,
4854 Xbug_w, TRUE, FALSE,
4858 Xbug_gon, FALSE, FALSE,
4862 Xbug_goe, FALSE, FALSE,
4863 EL_BUG_RIGHT, -1, -1
4866 Xbug_gos, FALSE, FALSE,
4870 Xbug_gow, FALSE, FALSE,
4874 Ybug_n, FALSE, FALSE,
4875 EL_BUG, ACTION_MOVING, MV_BIT_UP
4878 Ybug_nB, FALSE, TRUE,
4879 EL_BUG, ACTION_MOVING, MV_BIT_UP
4882 Ybug_e, FALSE, FALSE,
4883 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4886 Ybug_eB, FALSE, TRUE,
4887 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4890 Ybug_s, FALSE, FALSE,
4891 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4894 Ybug_sB, FALSE, TRUE,
4895 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4898 Ybug_w, FALSE, FALSE,
4899 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4902 Ybug_wB, FALSE, TRUE,
4903 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4906 Ybug_w_n, FALSE, FALSE,
4907 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4910 Ybug_n_e, FALSE, FALSE,
4911 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4914 Ybug_e_s, FALSE, FALSE,
4915 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4918 Ybug_s_w, FALSE, FALSE,
4919 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4922 Ybug_e_n, FALSE, FALSE,
4923 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4926 Ybug_s_e, FALSE, FALSE,
4927 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4930 Ybug_w_s, FALSE, FALSE,
4931 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4934 Ybug_n_w, FALSE, FALSE,
4935 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4938 Ybug_stone, FALSE, FALSE,
4939 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
4942 Ybug_spring, FALSE, FALSE,
4943 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
4946 Xtank_n, TRUE, FALSE,
4947 EL_SPACESHIP_UP, -1, -1
4950 Xtank_e, TRUE, FALSE,
4951 EL_SPACESHIP_RIGHT, -1, -1
4954 Xtank_s, TRUE, FALSE,
4955 EL_SPACESHIP_DOWN, -1, -1
4958 Xtank_w, TRUE, FALSE,
4959 EL_SPACESHIP_LEFT, -1, -1
4962 Xtank_gon, FALSE, FALSE,
4963 EL_SPACESHIP_UP, -1, -1
4966 Xtank_goe, FALSE, FALSE,
4967 EL_SPACESHIP_RIGHT, -1, -1
4970 Xtank_gos, FALSE, FALSE,
4971 EL_SPACESHIP_DOWN, -1, -1
4974 Xtank_gow, FALSE, FALSE,
4975 EL_SPACESHIP_LEFT, -1, -1
4978 Ytank_n, FALSE, FALSE,
4979 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4982 Ytank_nB, FALSE, TRUE,
4983 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4986 Ytank_e, FALSE, FALSE,
4987 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4990 Ytank_eB, FALSE, TRUE,
4991 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4994 Ytank_s, FALSE, FALSE,
4995 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4998 Ytank_sB, FALSE, TRUE,
4999 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
5002 Ytank_w, FALSE, FALSE,
5003 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5006 Ytank_wB, FALSE, TRUE,
5007 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5010 Ytank_w_n, FALSE, FALSE,
5011 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5014 Ytank_n_e, FALSE, FALSE,
5015 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5018 Ytank_e_s, FALSE, FALSE,
5019 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5022 Ytank_s_w, FALSE, FALSE,
5023 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5026 Ytank_e_n, FALSE, FALSE,
5027 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5030 Ytank_s_e, FALSE, FALSE,
5031 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5034 Ytank_w_s, FALSE, FALSE,
5035 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5038 Ytank_n_w, FALSE, FALSE,
5039 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5042 Ytank_stone, FALSE, FALSE,
5043 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5046 Ytank_spring, FALSE, FALSE,
5047 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5050 Xandroid, TRUE, FALSE,
5051 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5054 Xandroid_1_n, FALSE, FALSE,
5055 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5058 Xandroid_2_n, FALSE, FALSE,
5059 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5062 Xandroid_1_e, FALSE, FALSE,
5063 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5066 Xandroid_2_e, FALSE, FALSE,
5067 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5070 Xandroid_1_w, FALSE, FALSE,
5071 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5074 Xandroid_2_w, FALSE, FALSE,
5075 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5078 Xandroid_1_s, FALSE, FALSE,
5079 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5082 Xandroid_2_s, FALSE, FALSE,
5083 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5086 Yandroid_n, FALSE, FALSE,
5087 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5090 Yandroid_nB, FALSE, TRUE,
5091 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5094 Yandroid_ne, FALSE, FALSE,
5095 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5098 Yandroid_neB, FALSE, TRUE,
5099 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5102 Yandroid_e, FALSE, FALSE,
5103 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5106 Yandroid_eB, FALSE, TRUE,
5107 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5110 Yandroid_se, FALSE, FALSE,
5111 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5114 Yandroid_seB, FALSE, TRUE,
5115 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5118 Yandroid_s, FALSE, FALSE,
5119 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5122 Yandroid_sB, FALSE, TRUE,
5123 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5126 Yandroid_sw, FALSE, FALSE,
5127 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5130 Yandroid_swB, FALSE, TRUE,
5131 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5134 Yandroid_w, FALSE, FALSE,
5135 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5138 Yandroid_wB, FALSE, TRUE,
5139 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5142 Yandroid_nw, FALSE, FALSE,
5143 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5146 Yandroid_nwB, FALSE, TRUE,
5147 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5150 Xspring, TRUE, FALSE,
5154 Xspring_pause, FALSE, FALSE,
5158 Xspring_e, FALSE, FALSE,
5162 Xspring_w, FALSE, FALSE,
5166 Xspring_fall, FALSE, FALSE,
5170 Yspring_s, FALSE, FALSE,
5171 EL_SPRING, ACTION_FALLING, -1
5174 Yspring_sB, FALSE, TRUE,
5175 EL_SPRING, ACTION_FALLING, -1
5178 Yspring_e, FALSE, FALSE,
5179 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5182 Yspring_eB, FALSE, TRUE,
5183 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5186 Yspring_w, FALSE, FALSE,
5187 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5190 Yspring_wB, FALSE, TRUE,
5191 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5194 Yspring_kill_e, FALSE, FALSE,
5195 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5198 Yspring_kill_eB, FALSE, TRUE,
5199 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5202 Yspring_kill_w, FALSE, FALSE,
5203 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5206 Yspring_kill_wB, FALSE, TRUE,
5207 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5210 Xeater_n, TRUE, FALSE,
5211 EL_YAMYAM_UP, -1, -1
5214 Xeater_e, TRUE, FALSE,
5215 EL_YAMYAM_RIGHT, -1, -1
5218 Xeater_w, TRUE, FALSE,
5219 EL_YAMYAM_LEFT, -1, -1
5222 Xeater_s, TRUE, FALSE,
5223 EL_YAMYAM_DOWN, -1, -1
5226 Yeater_n, FALSE, FALSE,
5227 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5230 Yeater_nB, FALSE, TRUE,
5231 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5234 Yeater_e, FALSE, FALSE,
5235 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5238 Yeater_eB, FALSE, TRUE,
5239 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5242 Yeater_s, FALSE, FALSE,
5243 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5246 Yeater_sB, FALSE, TRUE,
5247 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5250 Yeater_w, FALSE, FALSE,
5251 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5254 Yeater_wB, FALSE, TRUE,
5255 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5258 Yeater_stone, FALSE, FALSE,
5259 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5262 Yeater_spring, FALSE, FALSE,
5263 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5266 Xalien, TRUE, FALSE,
5270 Xalien_pause, FALSE, FALSE,
5274 Yalien_n, FALSE, FALSE,
5275 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5278 Yalien_nB, FALSE, TRUE,
5279 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5282 Yalien_e, FALSE, FALSE,
5283 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5286 Yalien_eB, FALSE, TRUE,
5287 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5290 Yalien_s, FALSE, FALSE,
5291 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5294 Yalien_sB, FALSE, TRUE,
5295 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5298 Yalien_w, FALSE, FALSE,
5299 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5302 Yalien_wB, FALSE, TRUE,
5303 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5306 Yalien_stone, FALSE, FALSE,
5307 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5310 Yalien_spring, FALSE, FALSE,
5311 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5314 Xemerald, TRUE, FALSE,
5318 Xemerald_pause, FALSE, FALSE,
5322 Xemerald_fall, FALSE, FALSE,
5326 Xemerald_shine, FALSE, FALSE,
5327 EL_EMERALD, ACTION_TWINKLING, -1
5330 Yemerald_s, FALSE, FALSE,
5331 EL_EMERALD, ACTION_FALLING, -1
5334 Yemerald_sB, FALSE, TRUE,
5335 EL_EMERALD, ACTION_FALLING, -1
5338 Yemerald_e, FALSE, FALSE,
5339 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5342 Yemerald_eB, FALSE, TRUE,
5343 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5346 Yemerald_w, FALSE, FALSE,
5347 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5350 Yemerald_wB, FALSE, TRUE,
5351 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5354 Yemerald_eat, FALSE, FALSE,
5355 EL_EMERALD, ACTION_COLLECTING, -1
5358 Yemerald_stone, FALSE, FALSE,
5359 EL_NUT, ACTION_BREAKING, -1
5362 Xdiamond, TRUE, FALSE,
5366 Xdiamond_pause, FALSE, FALSE,
5370 Xdiamond_fall, FALSE, FALSE,
5374 Xdiamond_shine, FALSE, FALSE,
5375 EL_DIAMOND, ACTION_TWINKLING, -1
5378 Ydiamond_s, FALSE, FALSE,
5379 EL_DIAMOND, ACTION_FALLING, -1
5382 Ydiamond_sB, FALSE, TRUE,
5383 EL_DIAMOND, ACTION_FALLING, -1
5386 Ydiamond_e, FALSE, FALSE,
5387 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5390 Ydiamond_eB, FALSE, TRUE,
5391 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5394 Ydiamond_w, FALSE, FALSE,
5395 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5398 Ydiamond_wB, FALSE, TRUE,
5399 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5402 Ydiamond_eat, FALSE, FALSE,
5403 EL_DIAMOND, ACTION_COLLECTING, -1
5406 Ydiamond_stone, FALSE, FALSE,
5407 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5410 Xdrip_fall, TRUE, FALSE,
5411 EL_AMOEBA_DROP, -1, -1
5414 Xdrip_stretch, FALSE, FALSE,
5415 EL_AMOEBA_DROP, ACTION_FALLING, -1
5418 Xdrip_stretchB, FALSE, TRUE,
5419 EL_AMOEBA_DROP, ACTION_FALLING, -1
5422 Xdrip_eat, FALSE, FALSE,
5423 EL_AMOEBA_DROP, ACTION_GROWING, -1
5426 Ydrip_s1, FALSE, FALSE,
5427 EL_AMOEBA_DROP, ACTION_FALLING, -1
5430 Ydrip_s1B, FALSE, TRUE,
5431 EL_AMOEBA_DROP, ACTION_FALLING, -1
5434 Ydrip_s2, FALSE, FALSE,
5435 EL_AMOEBA_DROP, ACTION_FALLING, -1
5438 Ydrip_s2B, FALSE, TRUE,
5439 EL_AMOEBA_DROP, ACTION_FALLING, -1
5446 Xbomb_pause, FALSE, FALSE,
5450 Xbomb_fall, FALSE, FALSE,
5454 Ybomb_s, FALSE, FALSE,
5455 EL_BOMB, ACTION_FALLING, -1
5458 Ybomb_sB, FALSE, TRUE,
5459 EL_BOMB, ACTION_FALLING, -1
5462 Ybomb_e, FALSE, FALSE,
5463 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5466 Ybomb_eB, FALSE, TRUE,
5467 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5470 Ybomb_w, FALSE, FALSE,
5471 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5474 Ybomb_wB, FALSE, TRUE,
5475 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5478 Ybomb_eat, FALSE, FALSE,
5479 EL_BOMB, ACTION_ACTIVATING, -1
5482 Xballoon, TRUE, FALSE,
5486 Yballoon_n, FALSE, FALSE,
5487 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5490 Yballoon_nB, FALSE, TRUE,
5491 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5494 Yballoon_e, FALSE, FALSE,
5495 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5498 Yballoon_eB, FALSE, TRUE,
5499 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5502 Yballoon_s, FALSE, FALSE,
5503 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5506 Yballoon_sB, FALSE, TRUE,
5507 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5510 Yballoon_w, FALSE, FALSE,
5511 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5514 Yballoon_wB, FALSE, TRUE,
5515 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5518 Xgrass, TRUE, FALSE,
5519 EL_EMC_GRASS, -1, -1
5522 Ygrass_nB, FALSE, FALSE,
5523 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5526 Ygrass_eB, FALSE, FALSE,
5527 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5530 Ygrass_sB, FALSE, FALSE,
5531 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5534 Ygrass_wB, FALSE, FALSE,
5535 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5542 Ydirt_nB, FALSE, FALSE,
5543 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5546 Ydirt_eB, FALSE, FALSE,
5547 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5550 Ydirt_sB, FALSE, FALSE,
5551 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5554 Ydirt_wB, FALSE, FALSE,
5555 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5558 Xacid_ne, TRUE, FALSE,
5559 EL_ACID_POOL_TOPRIGHT, -1, -1
5562 Xacid_se, TRUE, FALSE,
5563 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5566 Xacid_s, TRUE, FALSE,
5567 EL_ACID_POOL_BOTTOM, -1, -1
5570 Xacid_sw, TRUE, FALSE,
5571 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5574 Xacid_nw, TRUE, FALSE,
5575 EL_ACID_POOL_TOPLEFT, -1, -1
5578 Xacid_1, TRUE, FALSE,
5582 Xacid_2, FALSE, FALSE,
5586 Xacid_3, FALSE, FALSE,
5590 Xacid_4, FALSE, FALSE,
5594 Xacid_5, FALSE, FALSE,
5598 Xacid_6, FALSE, FALSE,
5602 Xacid_7, FALSE, FALSE,
5606 Xacid_8, FALSE, FALSE,
5610 Xball_1, TRUE, FALSE,
5611 EL_EMC_MAGIC_BALL, -1, -1
5614 Xball_1B, FALSE, FALSE,
5615 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5618 Xball_2, FALSE, FALSE,
5619 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5622 Xball_2B, FALSE, FALSE,
5623 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5626 Yball_eat, FALSE, FALSE,
5627 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5630 Ykey_1_eat, FALSE, FALSE,
5631 EL_EM_KEY_1, ACTION_COLLECTING, -1
5634 Ykey_2_eat, FALSE, FALSE,
5635 EL_EM_KEY_2, ACTION_COLLECTING, -1
5638 Ykey_3_eat, FALSE, FALSE,
5639 EL_EM_KEY_3, ACTION_COLLECTING, -1
5642 Ykey_4_eat, FALSE, FALSE,
5643 EL_EM_KEY_4, ACTION_COLLECTING, -1
5646 Ykey_5_eat, FALSE, FALSE,
5647 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5650 Ykey_6_eat, FALSE, FALSE,
5651 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5654 Ykey_7_eat, FALSE, FALSE,
5655 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5658 Ykey_8_eat, FALSE, FALSE,
5659 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5662 Ylenses_eat, FALSE, FALSE,
5663 EL_EMC_LENSES, ACTION_COLLECTING, -1
5666 Ymagnify_eat, FALSE, FALSE,
5667 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5670 Ygrass_eat, FALSE, FALSE,
5671 EL_EMC_GRASS, ACTION_SNAPPING, -1
5674 Ydirt_eat, FALSE, FALSE,
5675 EL_SAND, ACTION_SNAPPING, -1
5678 Xgrow_ns, TRUE, FALSE,
5679 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5682 Ygrow_ns_eat, FALSE, FALSE,
5683 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5686 Xgrow_ew, TRUE, FALSE,
5687 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5690 Ygrow_ew_eat, FALSE, FALSE,
5691 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5694 Xwonderwall, TRUE, FALSE,
5695 EL_MAGIC_WALL, -1, -1
5698 XwonderwallB, FALSE, FALSE,
5699 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5702 Xamoeba_1, TRUE, FALSE,
5703 EL_AMOEBA_DRY, ACTION_OTHER, -1
5706 Xamoeba_2, FALSE, FALSE,
5707 EL_AMOEBA_DRY, ACTION_OTHER, -1
5710 Xamoeba_3, FALSE, FALSE,
5711 EL_AMOEBA_DRY, ACTION_OTHER, -1
5714 Xamoeba_4, FALSE, FALSE,
5715 EL_AMOEBA_DRY, ACTION_OTHER, -1
5718 Xamoeba_5, TRUE, FALSE,
5719 EL_AMOEBA_WET, ACTION_OTHER, -1
5722 Xamoeba_6, FALSE, FALSE,
5723 EL_AMOEBA_WET, ACTION_OTHER, -1
5726 Xamoeba_7, FALSE, FALSE,
5727 EL_AMOEBA_WET, ACTION_OTHER, -1
5730 Xamoeba_8, FALSE, FALSE,
5731 EL_AMOEBA_WET, ACTION_OTHER, -1
5734 Xdoor_1, TRUE, FALSE,
5735 EL_EM_GATE_1, -1, -1
5738 Xdoor_2, TRUE, FALSE,
5739 EL_EM_GATE_2, -1, -1
5742 Xdoor_3, TRUE, FALSE,
5743 EL_EM_GATE_3, -1, -1
5746 Xdoor_4, TRUE, FALSE,
5747 EL_EM_GATE_4, -1, -1
5750 Xdoor_5, TRUE, FALSE,
5751 EL_EMC_GATE_5, -1, -1
5754 Xdoor_6, TRUE, FALSE,
5755 EL_EMC_GATE_6, -1, -1
5758 Xdoor_7, TRUE, FALSE,
5759 EL_EMC_GATE_7, -1, -1
5762 Xdoor_8, TRUE, FALSE,
5763 EL_EMC_GATE_8, -1, -1
5766 Xkey_1, TRUE, FALSE,
5770 Xkey_2, TRUE, FALSE,
5774 Xkey_3, TRUE, FALSE,
5778 Xkey_4, TRUE, FALSE,
5782 Xkey_5, TRUE, FALSE,
5783 EL_EMC_KEY_5, -1, -1
5786 Xkey_6, TRUE, FALSE,
5787 EL_EMC_KEY_6, -1, -1
5790 Xkey_7, TRUE, FALSE,
5791 EL_EMC_KEY_7, -1, -1
5794 Xkey_8, TRUE, FALSE,
5795 EL_EMC_KEY_8, -1, -1
5798 Xwind_n, TRUE, FALSE,
5799 EL_BALLOON_SWITCH_UP, -1, -1
5802 Xwind_e, TRUE, FALSE,
5803 EL_BALLOON_SWITCH_RIGHT, -1, -1
5806 Xwind_s, TRUE, FALSE,
5807 EL_BALLOON_SWITCH_DOWN, -1, -1
5810 Xwind_w, TRUE, FALSE,
5811 EL_BALLOON_SWITCH_LEFT, -1, -1
5814 Xwind_nesw, TRUE, FALSE,
5815 EL_BALLOON_SWITCH_ANY, -1, -1
5818 Xwind_stop, TRUE, FALSE,
5819 EL_BALLOON_SWITCH_NONE, -1, -1
5823 EL_EM_EXIT_CLOSED, -1, -1
5826 Xexit_1, TRUE, FALSE,
5827 EL_EM_EXIT_OPEN, -1, -1
5830 Xexit_2, FALSE, FALSE,
5831 EL_EM_EXIT_OPEN, -1, -1
5834 Xexit_3, FALSE, FALSE,
5835 EL_EM_EXIT_OPEN, -1, -1
5838 Xdynamite, TRUE, FALSE,
5839 EL_EM_DYNAMITE, -1, -1
5842 Ydynamite_eat, FALSE, FALSE,
5843 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5846 Xdynamite_1, TRUE, FALSE,
5847 EL_EM_DYNAMITE_ACTIVE, -1, -1
5850 Xdynamite_2, FALSE, FALSE,
5851 EL_EM_DYNAMITE_ACTIVE, -1, -1
5854 Xdynamite_3, FALSE, FALSE,
5855 EL_EM_DYNAMITE_ACTIVE, -1, -1
5858 Xdynamite_4, FALSE, FALSE,
5859 EL_EM_DYNAMITE_ACTIVE, -1, -1
5862 Xbumper, TRUE, FALSE,
5863 EL_EMC_SPRING_BUMPER, -1, -1
5866 XbumperB, FALSE, FALSE,
5867 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5870 Xwheel, TRUE, FALSE,
5871 EL_ROBOT_WHEEL, -1, -1
5874 XwheelB, FALSE, FALSE,
5875 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5878 Xswitch, TRUE, FALSE,
5879 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5882 XswitchB, FALSE, FALSE,
5883 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5887 EL_QUICKSAND_EMPTY, -1, -1
5890 Xsand_stone, TRUE, FALSE,
5891 EL_QUICKSAND_FULL, -1, -1
5894 Xsand_stonein_1, FALSE, TRUE,
5895 EL_ROCK, ACTION_FILLING, -1
5898 Xsand_stonein_2, FALSE, TRUE,
5899 EL_ROCK, ACTION_FILLING, -1
5902 Xsand_stonein_3, FALSE, TRUE,
5903 EL_ROCK, ACTION_FILLING, -1
5906 Xsand_stonein_4, FALSE, TRUE,
5907 EL_ROCK, ACTION_FILLING, -1
5910 Xsand_stonesand_1, FALSE, FALSE,
5911 EL_QUICKSAND_EMPTYING, -1, -1
5914 Xsand_stonesand_2, FALSE, FALSE,
5915 EL_QUICKSAND_EMPTYING, -1, -1
5918 Xsand_stonesand_3, FALSE, FALSE,
5919 EL_QUICKSAND_EMPTYING, -1, -1
5922 Xsand_stonesand_4, FALSE, FALSE,
5923 EL_QUICKSAND_EMPTYING, -1, -1
5926 Xsand_stonesand_quickout_1, FALSE, FALSE,
5927 EL_QUICKSAND_EMPTYING, -1, -1
5930 Xsand_stonesand_quickout_2, FALSE, FALSE,
5931 EL_QUICKSAND_EMPTYING, -1, -1
5934 Xsand_stoneout_1, FALSE, FALSE,
5935 EL_ROCK, ACTION_EMPTYING, -1
5938 Xsand_stoneout_2, FALSE, FALSE,
5939 EL_ROCK, ACTION_EMPTYING, -1
5942 Xsand_sandstone_1, FALSE, FALSE,
5943 EL_QUICKSAND_FILLING, -1, -1
5946 Xsand_sandstone_2, FALSE, FALSE,
5947 EL_QUICKSAND_FILLING, -1, -1
5950 Xsand_sandstone_3, FALSE, FALSE,
5951 EL_QUICKSAND_FILLING, -1, -1
5954 Xsand_sandstone_4, FALSE, FALSE,
5955 EL_QUICKSAND_FILLING, -1, -1
5958 Xplant, TRUE, FALSE,
5959 EL_EMC_PLANT, -1, -1
5962 Yplant, FALSE, FALSE,
5963 EL_EMC_PLANT, -1, -1
5966 Xlenses, TRUE, FALSE,
5967 EL_EMC_LENSES, -1, -1
5970 Xmagnify, TRUE, FALSE,
5971 EL_EMC_MAGNIFIER, -1, -1
5974 Xdripper, TRUE, FALSE,
5975 EL_EMC_DRIPPER, -1, -1
5978 XdripperB, FALSE, FALSE,
5979 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
5982 Xfake_blank, TRUE, FALSE,
5983 EL_INVISIBLE_WALL, -1, -1
5986 Xfake_blankB, FALSE, FALSE,
5987 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
5990 Xfake_grass, TRUE, FALSE,
5991 EL_EMC_FAKE_GRASS, -1, -1
5994 Xfake_grassB, FALSE, FALSE,
5995 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
5998 Xfake_door_1, TRUE, FALSE,
5999 EL_EM_GATE_1_GRAY, -1, -1
6002 Xfake_door_2, TRUE, FALSE,
6003 EL_EM_GATE_2_GRAY, -1, -1
6006 Xfake_door_3, TRUE, FALSE,
6007 EL_EM_GATE_3_GRAY, -1, -1
6010 Xfake_door_4, TRUE, FALSE,
6011 EL_EM_GATE_4_GRAY, -1, -1
6014 Xfake_door_5, TRUE, FALSE,
6015 EL_EMC_GATE_5_GRAY, -1, -1
6018 Xfake_door_6, TRUE, FALSE,
6019 EL_EMC_GATE_6_GRAY, -1, -1
6022 Xfake_door_7, TRUE, FALSE,
6023 EL_EMC_GATE_7_GRAY, -1, -1
6026 Xfake_door_8, TRUE, FALSE,
6027 EL_EMC_GATE_8_GRAY, -1, -1
6030 Xfake_acid_1, TRUE, FALSE,
6031 EL_EMC_FAKE_ACID, -1, -1
6034 Xfake_acid_2, FALSE, FALSE,
6035 EL_EMC_FAKE_ACID, -1, -1
6038 Xfake_acid_3, FALSE, FALSE,
6039 EL_EMC_FAKE_ACID, -1, -1
6042 Xfake_acid_4, FALSE, FALSE,
6043 EL_EMC_FAKE_ACID, -1, -1
6046 Xfake_acid_5, FALSE, FALSE,
6047 EL_EMC_FAKE_ACID, -1, -1
6050 Xfake_acid_6, FALSE, FALSE,
6051 EL_EMC_FAKE_ACID, -1, -1
6054 Xfake_acid_7, FALSE, FALSE,
6055 EL_EMC_FAKE_ACID, -1, -1
6058 Xfake_acid_8, FALSE, FALSE,
6059 EL_EMC_FAKE_ACID, -1, -1
6062 Xsteel_1, TRUE, FALSE,
6063 EL_STEELWALL, -1, -1
6066 Xsteel_2, TRUE, FALSE,
6067 EL_EMC_STEELWALL_2, -1, -1
6070 Xsteel_3, TRUE, FALSE,
6071 EL_EMC_STEELWALL_3, -1, -1
6074 Xsteel_4, TRUE, FALSE,
6075 EL_EMC_STEELWALL_4, -1, -1
6078 Xwall_1, TRUE, FALSE,
6082 Xwall_2, TRUE, FALSE,
6083 EL_EMC_WALL_14, -1, -1
6086 Xwall_3, TRUE, FALSE,
6087 EL_EMC_WALL_15, -1, -1
6090 Xwall_4, TRUE, FALSE,
6091 EL_EMC_WALL_16, -1, -1
6094 Xround_wall_1, TRUE, FALSE,
6095 EL_WALL_SLIPPERY, -1, -1
6098 Xround_wall_2, TRUE, FALSE,
6099 EL_EMC_WALL_SLIPPERY_2, -1, -1
6102 Xround_wall_3, TRUE, FALSE,
6103 EL_EMC_WALL_SLIPPERY_3, -1, -1
6106 Xround_wall_4, TRUE, FALSE,
6107 EL_EMC_WALL_SLIPPERY_4, -1, -1
6110 Xdecor_1, TRUE, FALSE,
6111 EL_EMC_WALL_8, -1, -1
6114 Xdecor_2, TRUE, FALSE,
6115 EL_EMC_WALL_6, -1, -1
6118 Xdecor_3, TRUE, FALSE,
6119 EL_EMC_WALL_4, -1, -1
6122 Xdecor_4, TRUE, FALSE,
6123 EL_EMC_WALL_7, -1, -1
6126 Xdecor_5, TRUE, FALSE,
6127 EL_EMC_WALL_5, -1, -1
6130 Xdecor_6, TRUE, FALSE,
6131 EL_EMC_WALL_9, -1, -1
6134 Xdecor_7, TRUE, FALSE,
6135 EL_EMC_WALL_10, -1, -1
6138 Xdecor_8, TRUE, FALSE,
6139 EL_EMC_WALL_1, -1, -1
6142 Xdecor_9, TRUE, FALSE,
6143 EL_EMC_WALL_2, -1, -1
6146 Xdecor_10, TRUE, FALSE,
6147 EL_EMC_WALL_3, -1, -1
6150 Xdecor_11, TRUE, FALSE,
6151 EL_EMC_WALL_11, -1, -1
6154 Xdecor_12, TRUE, FALSE,
6155 EL_EMC_WALL_12, -1, -1
6158 Xalpha_0, TRUE, FALSE,
6159 EL_CHAR('0'), -1, -1
6162 Xalpha_1, TRUE, FALSE,
6163 EL_CHAR('1'), -1, -1
6166 Xalpha_2, TRUE, FALSE,
6167 EL_CHAR('2'), -1, -1
6170 Xalpha_3, TRUE, FALSE,
6171 EL_CHAR('3'), -1, -1
6174 Xalpha_4, TRUE, FALSE,
6175 EL_CHAR('4'), -1, -1
6178 Xalpha_5, TRUE, FALSE,
6179 EL_CHAR('5'), -1, -1
6182 Xalpha_6, TRUE, FALSE,
6183 EL_CHAR('6'), -1, -1
6186 Xalpha_7, TRUE, FALSE,
6187 EL_CHAR('7'), -1, -1
6190 Xalpha_8, TRUE, FALSE,
6191 EL_CHAR('8'), -1, -1
6194 Xalpha_9, TRUE, FALSE,
6195 EL_CHAR('9'), -1, -1
6198 Xalpha_excla, TRUE, FALSE,
6199 EL_CHAR('!'), -1, -1
6202 Xalpha_quote, TRUE, FALSE,
6203 EL_CHAR('"'), -1, -1
6206 Xalpha_comma, TRUE, FALSE,
6207 EL_CHAR(','), -1, -1
6210 Xalpha_minus, TRUE, FALSE,
6211 EL_CHAR('-'), -1, -1
6214 Xalpha_perio, TRUE, FALSE,
6215 EL_CHAR('.'), -1, -1
6218 Xalpha_colon, TRUE, FALSE,
6219 EL_CHAR(':'), -1, -1
6222 Xalpha_quest, TRUE, FALSE,
6223 EL_CHAR('?'), -1, -1
6226 Xalpha_a, TRUE, FALSE,
6227 EL_CHAR('A'), -1, -1
6230 Xalpha_b, TRUE, FALSE,
6231 EL_CHAR('B'), -1, -1
6234 Xalpha_c, TRUE, FALSE,
6235 EL_CHAR('C'), -1, -1
6238 Xalpha_d, TRUE, FALSE,
6239 EL_CHAR('D'), -1, -1
6242 Xalpha_e, TRUE, FALSE,
6243 EL_CHAR('E'), -1, -1
6246 Xalpha_f, TRUE, FALSE,
6247 EL_CHAR('F'), -1, -1
6250 Xalpha_g, TRUE, FALSE,
6251 EL_CHAR('G'), -1, -1
6254 Xalpha_h, TRUE, FALSE,
6255 EL_CHAR('H'), -1, -1
6258 Xalpha_i, TRUE, FALSE,
6259 EL_CHAR('I'), -1, -1
6262 Xalpha_j, TRUE, FALSE,
6263 EL_CHAR('J'), -1, -1
6266 Xalpha_k, TRUE, FALSE,
6267 EL_CHAR('K'), -1, -1
6270 Xalpha_l, TRUE, FALSE,
6271 EL_CHAR('L'), -1, -1
6274 Xalpha_m, TRUE, FALSE,
6275 EL_CHAR('M'), -1, -1
6278 Xalpha_n, TRUE, FALSE,
6279 EL_CHAR('N'), -1, -1
6282 Xalpha_o, TRUE, FALSE,
6283 EL_CHAR('O'), -1, -1
6286 Xalpha_p, TRUE, FALSE,
6287 EL_CHAR('P'), -1, -1
6290 Xalpha_q, TRUE, FALSE,
6291 EL_CHAR('Q'), -1, -1
6294 Xalpha_r, TRUE, FALSE,
6295 EL_CHAR('R'), -1, -1
6298 Xalpha_s, TRUE, FALSE,
6299 EL_CHAR('S'), -1, -1
6302 Xalpha_t, TRUE, FALSE,
6303 EL_CHAR('T'), -1, -1
6306 Xalpha_u, TRUE, FALSE,
6307 EL_CHAR('U'), -1, -1
6310 Xalpha_v, TRUE, FALSE,
6311 EL_CHAR('V'), -1, -1
6314 Xalpha_w, TRUE, FALSE,
6315 EL_CHAR('W'), -1, -1
6318 Xalpha_x, TRUE, FALSE,
6319 EL_CHAR('X'), -1, -1
6322 Xalpha_y, TRUE, FALSE,
6323 EL_CHAR('Y'), -1, -1
6326 Xalpha_z, TRUE, FALSE,
6327 EL_CHAR('Z'), -1, -1
6330 Xalpha_arrow_e, TRUE, FALSE,
6331 EL_CHAR('>'), -1, -1
6334 Xalpha_arrow_w, TRUE, FALSE,
6335 EL_CHAR('<'), -1, -1
6338 Xalpha_copyr, TRUE, FALSE,
6339 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6343 Xboom_bug, FALSE, FALSE,
6344 EL_BUG, ACTION_EXPLODING, -1
6347 Xboom_bomb, FALSE, FALSE,
6348 EL_BOMB, ACTION_EXPLODING, -1
6351 Xboom_android, FALSE, FALSE,
6352 EL_EMC_ANDROID, ACTION_OTHER, -1
6355 Xboom_1, FALSE, FALSE,
6356 EL_DEFAULT, ACTION_EXPLODING, -1
6359 Xboom_2, FALSE, FALSE,
6360 EL_DEFAULT, ACTION_EXPLODING, -1
6363 Znormal, FALSE, FALSE,
6367 Zdynamite, FALSE, FALSE,
6371 Zplayer, FALSE, FALSE,
6375 ZBORDER, FALSE, FALSE,
6385 static struct Mapping_EM_to_RND_player
6394 em_player_mapping_list[] =
6398 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6402 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6406 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6410 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6414 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6418 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6422 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6426 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6430 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6434 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6438 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6442 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6446 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6450 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6454 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6458 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6462 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6466 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6470 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6474 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6478 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6482 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6486 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6490 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6494 EL_PLAYER_1, ACTION_DEFAULT, -1,
6498 EL_PLAYER_2, ACTION_DEFAULT, -1,
6502 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6506 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6510 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6514 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6518 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6522 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6526 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6530 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6534 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6538 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6542 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6546 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6550 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6554 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6558 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6562 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6566 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6570 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6574 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6578 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6582 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6586 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6590 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6594 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6598 EL_PLAYER_3, ACTION_DEFAULT, -1,
6602 EL_PLAYER_4, ACTION_DEFAULT, -1,
6611 int map_element_RND_to_EM(int element_rnd)
6613 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6614 static boolean mapping_initialized = FALSE;
6616 if (!mapping_initialized)
6620 /* return "Xalpha_quest" for all undefined elements in mapping array */
6621 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6622 mapping_RND_to_EM[i] = Xalpha_quest;
6624 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6625 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6626 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6627 em_object_mapping_list[i].element_em;
6629 mapping_initialized = TRUE;
6632 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6633 return mapping_RND_to_EM[element_rnd];
6635 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6640 int map_element_EM_to_RND(int element_em)
6642 static unsigned short mapping_EM_to_RND[TILE_MAX];
6643 static boolean mapping_initialized = FALSE;
6645 if (!mapping_initialized)
6649 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6650 for (i = 0; i < TILE_MAX; i++)
6651 mapping_EM_to_RND[i] = EL_UNKNOWN;
6653 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6654 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6655 em_object_mapping_list[i].element_rnd;
6657 mapping_initialized = TRUE;
6660 if (element_em >= 0 && element_em < TILE_MAX)
6661 return mapping_EM_to_RND[element_em];
6663 Error(ERR_WARN, "invalid EM level element %d", element_em);
6668 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6670 struct LevelInfo_EM *level_em = level->native_em_level;
6671 struct LEVEL *lev = level_em->lev;
6674 for (i = 0; i < TILE_MAX; i++)
6675 lev->android_array[i] = Xblank;
6677 for (i = 0; i < level->num_android_clone_elements; i++)
6679 int element_rnd = level->android_clone_element[i];
6680 int element_em = map_element_RND_to_EM(element_rnd);
6682 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6683 if (em_object_mapping_list[j].element_rnd == element_rnd)
6684 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6688 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6690 struct LevelInfo_EM *level_em = level->native_em_level;
6691 struct LEVEL *lev = level_em->lev;
6694 level->num_android_clone_elements = 0;
6696 for (i = 0; i < TILE_MAX; i++)
6698 int element_em = lev->android_array[i];
6700 boolean element_found = FALSE;
6702 if (element_em == Xblank)
6705 element_rnd = map_element_EM_to_RND(element_em);
6707 for (j = 0; j < level->num_android_clone_elements; j++)
6708 if (level->android_clone_element[j] == element_rnd)
6709 element_found = TRUE;
6713 level->android_clone_element[level->num_android_clone_elements++] =
6716 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6721 if (level->num_android_clone_elements == 0)
6723 level->num_android_clone_elements = 1;
6724 level->android_clone_element[0] = EL_EMPTY;
6728 int map_direction_RND_to_EM(int direction)
6730 return (direction == MV_UP ? 0 :
6731 direction == MV_RIGHT ? 1 :
6732 direction == MV_DOWN ? 2 :
6733 direction == MV_LEFT ? 3 :
6737 int map_direction_EM_to_RND(int direction)
6739 return (direction == 0 ? MV_UP :
6740 direction == 1 ? MV_RIGHT :
6741 direction == 2 ? MV_DOWN :
6742 direction == 3 ? MV_LEFT :
6746 int map_element_RND_to_SP(int element_rnd)
6748 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6750 if (element_rnd >= EL_SP_START &&
6751 element_rnd <= EL_SP_END)
6752 element_sp = element_rnd - EL_SP_START;
6753 else if (element_rnd == EL_EMPTY_SPACE)
6755 else if (element_rnd == EL_INVISIBLE_WALL)
6761 int map_element_SP_to_RND(int element_sp)
6763 int element_rnd = EL_UNKNOWN;
6765 if (element_sp >= 0x00 &&
6767 element_rnd = EL_SP_START + element_sp;
6768 else if (element_sp == 0x28)
6769 element_rnd = EL_INVISIBLE_WALL;
6774 int map_action_SP_to_RND(int action_sp)
6778 case actActive: return ACTION_ACTIVE;
6779 case actImpact: return ACTION_IMPACT;
6780 case actExploding: return ACTION_EXPLODING;
6781 case actDigging: return ACTION_DIGGING;
6782 case actSnapping: return ACTION_SNAPPING;
6783 case actCollecting: return ACTION_COLLECTING;
6784 case actPassing: return ACTION_PASSING;
6785 case actPushing: return ACTION_PUSHING;
6786 case actDropping: return ACTION_DROPPING;
6788 default: return ACTION_DEFAULT;
6792 int get_next_element(int element)
6796 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6797 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6798 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6799 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6800 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6801 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6802 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6803 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6804 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6805 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6806 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6808 default: return element;
6812 int el_act_dir2img(int element, int action, int direction)
6814 element = GFX_ELEMENT(element);
6815 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6817 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6818 return element_info[element].direction_graphic[action][direction];
6821 static int el_act_dir2crm(int element, int action, int direction)
6823 element = GFX_ELEMENT(element);
6824 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6826 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6827 return element_info[element].direction_crumbled[action][direction];
6830 int el_act2img(int element, int action)
6832 element = GFX_ELEMENT(element);
6834 return element_info[element].graphic[action];
6837 int el_act2crm(int element, int action)
6839 element = GFX_ELEMENT(element);
6841 return element_info[element].crumbled[action];
6844 int el_dir2img(int element, int direction)
6846 element = GFX_ELEMENT(element);
6848 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6851 int el2baseimg(int element)
6853 return element_info[element].graphic[ACTION_DEFAULT];
6856 int el2img(int element)
6858 element = GFX_ELEMENT(element);
6860 return element_info[element].graphic[ACTION_DEFAULT];
6863 int el2edimg(int element)
6865 element = GFX_ELEMENT(element);
6867 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6870 int el2preimg(int element)
6872 element = GFX_ELEMENT(element);
6874 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6877 int el2panelimg(int element)
6879 element = GFX_ELEMENT(element);
6881 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6884 int font2baseimg(int font_nr)
6886 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6889 int getBeltNrFromBeltElement(int element)
6891 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6892 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6893 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6896 int getBeltNrFromBeltActiveElement(int element)
6898 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6899 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6900 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6903 int getBeltNrFromBeltSwitchElement(int element)
6905 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6906 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6907 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6910 int getBeltDirNrFromBeltElement(int element)
6912 static int belt_base_element[4] =
6914 EL_CONVEYOR_BELT_1_LEFT,
6915 EL_CONVEYOR_BELT_2_LEFT,
6916 EL_CONVEYOR_BELT_3_LEFT,
6917 EL_CONVEYOR_BELT_4_LEFT
6920 int belt_nr = getBeltNrFromBeltElement(element);
6921 int belt_dir_nr = element - belt_base_element[belt_nr];
6923 return (belt_dir_nr % 3);
6926 int getBeltDirNrFromBeltSwitchElement(int element)
6928 static int belt_base_element[4] =
6930 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6931 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6932 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6933 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6936 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6937 int belt_dir_nr = element - belt_base_element[belt_nr];
6939 return (belt_dir_nr % 3);
6942 int getBeltDirFromBeltElement(int element)
6944 static int belt_move_dir[3] =
6951 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6953 return belt_move_dir[belt_dir_nr];
6956 int getBeltDirFromBeltSwitchElement(int element)
6958 static int belt_move_dir[3] =
6965 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6967 return belt_move_dir[belt_dir_nr];
6970 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6972 static int belt_base_element[4] =
6974 EL_CONVEYOR_BELT_1_LEFT,
6975 EL_CONVEYOR_BELT_2_LEFT,
6976 EL_CONVEYOR_BELT_3_LEFT,
6977 EL_CONVEYOR_BELT_4_LEFT
6980 return belt_base_element[belt_nr] + belt_dir_nr;
6983 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6985 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6987 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6990 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6992 static int belt_base_element[4] =
6994 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6995 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6996 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6997 EL_CONVEYOR_BELT_4_SWITCH_LEFT
7000 return belt_base_element[belt_nr] + belt_dir_nr;
7003 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7005 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7007 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7010 boolean getTeamMode_EM()
7012 return game.team_mode;
7015 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7017 int game_frame_delay_value;
7019 game_frame_delay_value =
7020 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7021 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7024 if (tape.playing && tape.warp_forward && !tape.pausing)
7025 game_frame_delay_value = 0;
7027 return game_frame_delay_value;
7030 unsigned int InitRND(int seed)
7032 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7033 return InitEngineRandom_EM(seed);
7034 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7035 return InitEngineRandom_SP(seed);
7037 return InitEngineRandom_RND(seed);
7040 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7041 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7043 inline static int get_effective_element_EM(int tile, int frame_em)
7045 int element = object_mapping[tile].element_rnd;
7046 int action = object_mapping[tile].action;
7047 boolean is_backside = object_mapping[tile].is_backside;
7048 boolean action_removing = (action == ACTION_DIGGING ||
7049 action == ACTION_SNAPPING ||
7050 action == ACTION_COLLECTING);
7056 case Yacid_splash_eB:
7057 case Yacid_splash_wB:
7058 return (frame_em > 5 ? EL_EMPTY : element);
7064 else /* frame_em == 7 */
7068 case Yacid_splash_eB:
7069 case Yacid_splash_wB:
7072 case Yemerald_stone:
7075 case Ydiamond_stone:
7079 case Xdrip_stretchB:
7098 case Xsand_stonein_1:
7099 case Xsand_stonein_2:
7100 case Xsand_stonein_3:
7101 case Xsand_stonein_4:
7105 return (is_backside || action_removing ? EL_EMPTY : element);
7110 inline static boolean check_linear_animation_EM(int tile)
7114 case Xsand_stonesand_1:
7115 case Xsand_stonesand_quickout_1:
7116 case Xsand_sandstone_1:
7117 case Xsand_stonein_1:
7118 case Xsand_stoneout_1:
7137 case Yacid_splash_eB:
7138 case Yacid_splash_wB:
7139 case Yemerald_stone:
7146 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7147 boolean has_crumbled_graphics,
7148 int crumbled, int sync_frame)
7150 /* if element can be crumbled, but certain action graphics are just empty
7151 space (like instantly snapping sand to empty space in 1 frame), do not
7152 treat these empty space graphics as crumbled graphics in EMC engine */
7153 if (crumbled == IMG_EMPTY_SPACE)
7154 has_crumbled_graphics = FALSE;
7156 if (has_crumbled_graphics)
7158 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7159 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7160 g_crumbled->anim_delay,
7161 g_crumbled->anim_mode,
7162 g_crumbled->anim_start_frame,
7165 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7166 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7168 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7170 g_em->has_crumbled_graphics = TRUE;
7174 g_em->crumbled_bitmap = NULL;
7175 g_em->crumbled_src_x = 0;
7176 g_em->crumbled_src_y = 0;
7177 g_em->crumbled_border_size = 0;
7179 g_em->has_crumbled_graphics = FALSE;
7183 void ResetGfxAnimation_EM(int x, int y, int tile)
7188 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7189 int tile, int frame_em, int x, int y)
7191 int action = object_mapping[tile].action;
7192 int direction = object_mapping[tile].direction;
7193 int effective_element = get_effective_element_EM(tile, frame_em);
7194 int graphic = (direction == MV_NONE ?
7195 el_act2img(effective_element, action) :
7196 el_act_dir2img(effective_element, action, direction));
7197 struct GraphicInfo *g = &graphic_info[graphic];
7199 boolean action_removing = (action == ACTION_DIGGING ||
7200 action == ACTION_SNAPPING ||
7201 action == ACTION_COLLECTING);
7202 boolean action_moving = (action == ACTION_FALLING ||
7203 action == ACTION_MOVING ||
7204 action == ACTION_PUSHING ||
7205 action == ACTION_EATING ||
7206 action == ACTION_FILLING ||
7207 action == ACTION_EMPTYING);
7208 boolean action_falling = (action == ACTION_FALLING ||
7209 action == ACTION_FILLING ||
7210 action == ACTION_EMPTYING);
7212 /* special case: graphic uses "2nd movement tile" and has defined
7213 7 frames for movement animation (or less) => use default graphic
7214 for last (8th) frame which ends the movement animation */
7215 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7217 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7218 graphic = (direction == MV_NONE ?
7219 el_act2img(effective_element, action) :
7220 el_act_dir2img(effective_element, action, direction));
7222 g = &graphic_info[graphic];
7225 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7229 else if (action_moving)
7231 boolean is_backside = object_mapping[tile].is_backside;
7235 int direction = object_mapping[tile].direction;
7236 int move_dir = (action_falling ? MV_DOWN : direction);
7241 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7242 if (g->double_movement && frame_em == 0)
7246 if (move_dir == MV_LEFT)
7247 GfxFrame[x - 1][y] = GfxFrame[x][y];
7248 else if (move_dir == MV_RIGHT)
7249 GfxFrame[x + 1][y] = GfxFrame[x][y];
7250 else if (move_dir == MV_UP)
7251 GfxFrame[x][y - 1] = GfxFrame[x][y];
7252 else if (move_dir == MV_DOWN)
7253 GfxFrame[x][y + 1] = GfxFrame[x][y];
7260 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7261 if (tile == Xsand_stonesand_quickout_1 ||
7262 tile == Xsand_stonesand_quickout_2)
7266 if (graphic_info[graphic].anim_global_sync)
7267 sync_frame = FrameCounter;
7268 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7269 sync_frame = GfxFrame[x][y];
7271 sync_frame = 0; /* playfield border (pseudo steel) */
7273 SetRandomAnimationValue(x, y);
7275 int frame = getAnimationFrame(g->anim_frames,
7278 g->anim_start_frame,
7281 g_em->unique_identifier =
7282 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7285 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7286 int tile, int frame_em, int x, int y)
7288 int action = object_mapping[tile].action;
7289 int direction = object_mapping[tile].direction;
7290 boolean is_backside = object_mapping[tile].is_backside;
7291 int effective_element = get_effective_element_EM(tile, frame_em);
7292 int effective_action = action;
7293 int graphic = (direction == MV_NONE ?
7294 el_act2img(effective_element, effective_action) :
7295 el_act_dir2img(effective_element, effective_action,
7297 int crumbled = (direction == MV_NONE ?
7298 el_act2crm(effective_element, effective_action) :
7299 el_act_dir2crm(effective_element, effective_action,
7301 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7302 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7303 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7304 struct GraphicInfo *g = &graphic_info[graphic];
7307 /* special case: graphic uses "2nd movement tile" and has defined
7308 7 frames for movement animation (or less) => use default graphic
7309 for last (8th) frame which ends the movement animation */
7310 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7312 effective_action = ACTION_DEFAULT;
7313 graphic = (direction == MV_NONE ?
7314 el_act2img(effective_element, effective_action) :
7315 el_act_dir2img(effective_element, effective_action,
7317 crumbled = (direction == MV_NONE ?
7318 el_act2crm(effective_element, effective_action) :
7319 el_act_dir2crm(effective_element, effective_action,
7322 g = &graphic_info[graphic];
7325 if (graphic_info[graphic].anim_global_sync)
7326 sync_frame = FrameCounter;
7327 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7328 sync_frame = GfxFrame[x][y];
7330 sync_frame = 0; /* playfield border (pseudo steel) */
7332 SetRandomAnimationValue(x, y);
7334 int frame = getAnimationFrame(g->anim_frames,
7337 g->anim_start_frame,
7340 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7341 g->double_movement && is_backside);
7343 /* (updating the "crumbled" graphic definitions is probably not really needed,
7344 as animations for crumbled graphics can't be longer than one EMC cycle) */
7345 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7349 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7350 int player_nr, int anim, int frame_em)
7352 int element = player_mapping[player_nr][anim].element_rnd;
7353 int action = player_mapping[player_nr][anim].action;
7354 int direction = player_mapping[player_nr][anim].direction;
7355 int graphic = (direction == MV_NONE ?
7356 el_act2img(element, action) :
7357 el_act_dir2img(element, action, direction));
7358 struct GraphicInfo *g = &graphic_info[graphic];
7361 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7363 stored_player[player_nr].StepFrame = frame_em;
7365 sync_frame = stored_player[player_nr].Frame;
7367 int frame = getAnimationFrame(g->anim_frames,
7370 g->anim_start_frame,
7373 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7374 &g_em->src_x, &g_em->src_y, FALSE);
7377 void InitGraphicInfo_EM(void)
7382 int num_em_gfx_errors = 0;
7384 if (graphic_info_em_object[0][0].bitmap == NULL)
7386 /* EM graphics not yet initialized in em_open_all() */
7391 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7394 /* always start with reliable default values */
7395 for (i = 0; i < TILE_MAX; i++)
7397 object_mapping[i].element_rnd = EL_UNKNOWN;
7398 object_mapping[i].is_backside = FALSE;
7399 object_mapping[i].action = ACTION_DEFAULT;
7400 object_mapping[i].direction = MV_NONE;
7403 /* always start with reliable default values */
7404 for (p = 0; p < MAX_PLAYERS; p++)
7406 for (i = 0; i < SPR_MAX; i++)
7408 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7409 player_mapping[p][i].action = ACTION_DEFAULT;
7410 player_mapping[p][i].direction = MV_NONE;
7414 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7416 int e = em_object_mapping_list[i].element_em;
7418 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7419 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7421 if (em_object_mapping_list[i].action != -1)
7422 object_mapping[e].action = em_object_mapping_list[i].action;
7424 if (em_object_mapping_list[i].direction != -1)
7425 object_mapping[e].direction =
7426 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7429 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7431 int a = em_player_mapping_list[i].action_em;
7432 int p = em_player_mapping_list[i].player_nr;
7434 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7436 if (em_player_mapping_list[i].action != -1)
7437 player_mapping[p][a].action = em_player_mapping_list[i].action;
7439 if (em_player_mapping_list[i].direction != -1)
7440 player_mapping[p][a].direction =
7441 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7444 for (i = 0; i < TILE_MAX; i++)
7446 int element = object_mapping[i].element_rnd;
7447 int action = object_mapping[i].action;
7448 int direction = object_mapping[i].direction;
7449 boolean is_backside = object_mapping[i].is_backside;
7450 boolean action_exploding = ((action == ACTION_EXPLODING ||
7451 action == ACTION_SMASHED_BY_ROCK ||
7452 action == ACTION_SMASHED_BY_SPRING) &&
7453 element != EL_DIAMOND);
7454 boolean action_active = (action == ACTION_ACTIVE);
7455 boolean action_other = (action == ACTION_OTHER);
7457 for (j = 0; j < 8; j++)
7459 int effective_element = get_effective_element_EM(i, j);
7460 int effective_action = (j < 7 ? action :
7461 i == Xdrip_stretch ? action :
7462 i == Xdrip_stretchB ? action :
7463 i == Ydrip_s1 ? action :
7464 i == Ydrip_s1B ? action :
7465 i == Xball_1B ? action :
7466 i == Xball_2 ? action :
7467 i == Xball_2B ? action :
7468 i == Yball_eat ? action :
7469 i == Ykey_1_eat ? action :
7470 i == Ykey_2_eat ? action :
7471 i == Ykey_3_eat ? action :
7472 i == Ykey_4_eat ? action :
7473 i == Ykey_5_eat ? action :
7474 i == Ykey_6_eat ? action :
7475 i == Ykey_7_eat ? action :
7476 i == Ykey_8_eat ? action :
7477 i == Ylenses_eat ? action :
7478 i == Ymagnify_eat ? action :
7479 i == Ygrass_eat ? action :
7480 i == Ydirt_eat ? action :
7481 i == Xsand_stonein_1 ? action :
7482 i == Xsand_stonein_2 ? action :
7483 i == Xsand_stonein_3 ? action :
7484 i == Xsand_stonein_4 ? action :
7485 i == Xsand_stoneout_1 ? action :
7486 i == Xsand_stoneout_2 ? action :
7487 i == Xboom_android ? ACTION_EXPLODING :
7488 action_exploding ? ACTION_EXPLODING :
7489 action_active ? action :
7490 action_other ? action :
7492 int graphic = (el_act_dir2img(effective_element, effective_action,
7494 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7496 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7497 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7498 boolean has_action_graphics = (graphic != base_graphic);
7499 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7500 struct GraphicInfo *g = &graphic_info[graphic];
7501 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7504 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7505 boolean special_animation = (action != ACTION_DEFAULT &&
7506 g->anim_frames == 3 &&
7507 g->anim_delay == 2 &&
7508 g->anim_mode & ANIM_LINEAR);
7509 int sync_frame = (i == Xdrip_stretch ? 7 :
7510 i == Xdrip_stretchB ? 7 :
7511 i == Ydrip_s2 ? j + 8 :
7512 i == Ydrip_s2B ? j + 8 :
7521 i == Xfake_acid_1 ? 0 :
7522 i == Xfake_acid_2 ? 10 :
7523 i == Xfake_acid_3 ? 20 :
7524 i == Xfake_acid_4 ? 30 :
7525 i == Xfake_acid_5 ? 40 :
7526 i == Xfake_acid_6 ? 50 :
7527 i == Xfake_acid_7 ? 60 :
7528 i == Xfake_acid_8 ? 70 :
7530 i == Xball_2B ? j + 8 :
7531 i == Yball_eat ? j + 1 :
7532 i == Ykey_1_eat ? j + 1 :
7533 i == Ykey_2_eat ? j + 1 :
7534 i == Ykey_3_eat ? j + 1 :
7535 i == Ykey_4_eat ? j + 1 :
7536 i == Ykey_5_eat ? j + 1 :
7537 i == Ykey_6_eat ? j + 1 :
7538 i == Ykey_7_eat ? j + 1 :
7539 i == Ykey_8_eat ? j + 1 :
7540 i == Ylenses_eat ? j + 1 :
7541 i == Ymagnify_eat ? j + 1 :
7542 i == Ygrass_eat ? j + 1 :
7543 i == Ydirt_eat ? j + 1 :
7544 i == Xamoeba_1 ? 0 :
7545 i == Xamoeba_2 ? 1 :
7546 i == Xamoeba_3 ? 2 :
7547 i == Xamoeba_4 ? 3 :
7548 i == Xamoeba_5 ? 0 :
7549 i == Xamoeba_6 ? 1 :
7550 i == Xamoeba_7 ? 2 :
7551 i == Xamoeba_8 ? 3 :
7552 i == Xexit_2 ? j + 8 :
7553 i == Xexit_3 ? j + 16 :
7554 i == Xdynamite_1 ? 0 :
7555 i == Xdynamite_2 ? 8 :
7556 i == Xdynamite_3 ? 16 :
7557 i == Xdynamite_4 ? 24 :
7558 i == Xsand_stonein_1 ? j + 1 :
7559 i == Xsand_stonein_2 ? j + 9 :
7560 i == Xsand_stonein_3 ? j + 17 :
7561 i == Xsand_stonein_4 ? j + 25 :
7562 i == Xsand_stoneout_1 && j == 0 ? 0 :
7563 i == Xsand_stoneout_1 && j == 1 ? 0 :
7564 i == Xsand_stoneout_1 && j == 2 ? 1 :
7565 i == Xsand_stoneout_1 && j == 3 ? 2 :
7566 i == Xsand_stoneout_1 && j == 4 ? 2 :
7567 i == Xsand_stoneout_1 && j == 5 ? 3 :
7568 i == Xsand_stoneout_1 && j == 6 ? 4 :
7569 i == Xsand_stoneout_1 && j == 7 ? 4 :
7570 i == Xsand_stoneout_2 && j == 0 ? 5 :
7571 i == Xsand_stoneout_2 && j == 1 ? 6 :
7572 i == Xsand_stoneout_2 && j == 2 ? 7 :
7573 i == Xsand_stoneout_2 && j == 3 ? 8 :
7574 i == Xsand_stoneout_2 && j == 4 ? 9 :
7575 i == Xsand_stoneout_2 && j == 5 ? 11 :
7576 i == Xsand_stoneout_2 && j == 6 ? 13 :
7577 i == Xsand_stoneout_2 && j == 7 ? 15 :
7578 i == Xboom_bug && j == 1 ? 2 :
7579 i == Xboom_bug && j == 2 ? 2 :
7580 i == Xboom_bug && j == 3 ? 4 :
7581 i == Xboom_bug && j == 4 ? 4 :
7582 i == Xboom_bug && j == 5 ? 2 :
7583 i == Xboom_bug && j == 6 ? 2 :
7584 i == Xboom_bug && j == 7 ? 0 :
7585 i == Xboom_bomb && j == 1 ? 2 :
7586 i == Xboom_bomb && j == 2 ? 2 :
7587 i == Xboom_bomb && j == 3 ? 4 :
7588 i == Xboom_bomb && j == 4 ? 4 :
7589 i == Xboom_bomb && j == 5 ? 2 :
7590 i == Xboom_bomb && j == 6 ? 2 :
7591 i == Xboom_bomb && j == 7 ? 0 :
7592 i == Xboom_android && j == 7 ? 6 :
7593 i == Xboom_1 && j == 1 ? 2 :
7594 i == Xboom_1 && j == 2 ? 2 :
7595 i == Xboom_1 && j == 3 ? 4 :
7596 i == Xboom_1 && j == 4 ? 4 :
7597 i == Xboom_1 && j == 5 ? 6 :
7598 i == Xboom_1 && j == 6 ? 6 :
7599 i == Xboom_1 && j == 7 ? 8 :
7600 i == Xboom_2 && j == 0 ? 8 :
7601 i == Xboom_2 && j == 1 ? 8 :
7602 i == Xboom_2 && j == 2 ? 10 :
7603 i == Xboom_2 && j == 3 ? 10 :
7604 i == Xboom_2 && j == 4 ? 10 :
7605 i == Xboom_2 && j == 5 ? 12 :
7606 i == Xboom_2 && j == 6 ? 12 :
7607 i == Xboom_2 && j == 7 ? 12 :
7608 special_animation && j == 4 ? 3 :
7609 effective_action != action ? 0 :
7613 Bitmap *debug_bitmap = g_em->bitmap;
7614 int debug_src_x = g_em->src_x;
7615 int debug_src_y = g_em->src_y;
7618 int frame = getAnimationFrame(g->anim_frames,
7621 g->anim_start_frame,
7624 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7625 g->double_movement && is_backside);
7627 g_em->bitmap = src_bitmap;
7628 g_em->src_x = src_x;
7629 g_em->src_y = src_y;
7630 g_em->src_offset_x = 0;
7631 g_em->src_offset_y = 0;
7632 g_em->dst_offset_x = 0;
7633 g_em->dst_offset_y = 0;
7634 g_em->width = TILEX;
7635 g_em->height = TILEY;
7637 g_em->preserve_background = FALSE;
7639 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7642 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7643 effective_action == ACTION_MOVING ||
7644 effective_action == ACTION_PUSHING ||
7645 effective_action == ACTION_EATING)) ||
7646 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7647 effective_action == ACTION_EMPTYING)))
7650 (effective_action == ACTION_FALLING ||
7651 effective_action == ACTION_FILLING ||
7652 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7653 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7654 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7655 int num_steps = (i == Ydrip_s1 ? 16 :
7656 i == Ydrip_s1B ? 16 :
7657 i == Ydrip_s2 ? 16 :
7658 i == Ydrip_s2B ? 16 :
7659 i == Xsand_stonein_1 ? 32 :
7660 i == Xsand_stonein_2 ? 32 :
7661 i == Xsand_stonein_3 ? 32 :
7662 i == Xsand_stonein_4 ? 32 :
7663 i == Xsand_stoneout_1 ? 16 :
7664 i == Xsand_stoneout_2 ? 16 : 8);
7665 int cx = ABS(dx) * (TILEX / num_steps);
7666 int cy = ABS(dy) * (TILEY / num_steps);
7667 int step_frame = (i == Ydrip_s2 ? j + 8 :
7668 i == Ydrip_s2B ? j + 8 :
7669 i == Xsand_stonein_2 ? j + 8 :
7670 i == Xsand_stonein_3 ? j + 16 :
7671 i == Xsand_stonein_4 ? j + 24 :
7672 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7673 int step = (is_backside ? step_frame : num_steps - step_frame);
7675 if (is_backside) /* tile where movement starts */
7677 if (dx < 0 || dy < 0)
7679 g_em->src_offset_x = cx * step;
7680 g_em->src_offset_y = cy * step;
7684 g_em->dst_offset_x = cx * step;
7685 g_em->dst_offset_y = cy * step;
7688 else /* tile where movement ends */
7690 if (dx < 0 || dy < 0)
7692 g_em->dst_offset_x = cx * step;
7693 g_em->dst_offset_y = cy * step;
7697 g_em->src_offset_x = cx * step;
7698 g_em->src_offset_y = cy * step;
7702 g_em->width = TILEX - cx * step;
7703 g_em->height = TILEY - cy * step;
7706 /* create unique graphic identifier to decide if tile must be redrawn */
7707 /* bit 31 - 16 (16 bit): EM style graphic
7708 bit 15 - 12 ( 4 bit): EM style frame
7709 bit 11 - 6 ( 6 bit): graphic width
7710 bit 5 - 0 ( 6 bit): graphic height */
7711 g_em->unique_identifier =
7712 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7716 /* skip check for EMC elements not contained in original EMC artwork */
7717 if (element == EL_EMC_FAKE_ACID)
7720 if (g_em->bitmap != debug_bitmap ||
7721 g_em->src_x != debug_src_x ||
7722 g_em->src_y != debug_src_y ||
7723 g_em->src_offset_x != 0 ||
7724 g_em->src_offset_y != 0 ||
7725 g_em->dst_offset_x != 0 ||
7726 g_em->dst_offset_y != 0 ||
7727 g_em->width != TILEX ||
7728 g_em->height != TILEY)
7730 static int last_i = -1;
7738 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7739 i, element, element_info[element].token_name,
7740 element_action_info[effective_action].suffix, direction);
7742 if (element != effective_element)
7743 printf(" [%d ('%s')]",
7745 element_info[effective_element].token_name);
7749 if (g_em->bitmap != debug_bitmap)
7750 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7751 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7753 if (g_em->src_x != debug_src_x ||
7754 g_em->src_y != debug_src_y)
7755 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7756 j, (is_backside ? 'B' : 'F'),
7757 g_em->src_x, g_em->src_y,
7758 g_em->src_x / 32, g_em->src_y / 32,
7759 debug_src_x, debug_src_y,
7760 debug_src_x / 32, debug_src_y / 32);
7762 if (g_em->src_offset_x != 0 ||
7763 g_em->src_offset_y != 0 ||
7764 g_em->dst_offset_x != 0 ||
7765 g_em->dst_offset_y != 0)
7766 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7768 g_em->src_offset_x, g_em->src_offset_y,
7769 g_em->dst_offset_x, g_em->dst_offset_y);
7771 if (g_em->width != TILEX ||
7772 g_em->height != TILEY)
7773 printf(" %d (%d): size %d,%d should be %d,%d\n",
7775 g_em->width, g_em->height, TILEX, TILEY);
7777 num_em_gfx_errors++;
7784 for (i = 0; i < TILE_MAX; i++)
7786 for (j = 0; j < 8; j++)
7788 int element = object_mapping[i].element_rnd;
7789 int action = object_mapping[i].action;
7790 int direction = object_mapping[i].direction;
7791 boolean is_backside = object_mapping[i].is_backside;
7792 int graphic_action = el_act_dir2img(element, action, direction);
7793 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7795 if ((action == ACTION_SMASHED_BY_ROCK ||
7796 action == ACTION_SMASHED_BY_SPRING ||
7797 action == ACTION_EATING) &&
7798 graphic_action == graphic_default)
7800 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7801 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7802 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7803 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7806 /* no separate animation for "smashed by rock" -- use rock instead */
7807 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7808 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7810 g_em->bitmap = g_xx->bitmap;
7811 g_em->src_x = g_xx->src_x;
7812 g_em->src_y = g_xx->src_y;
7813 g_em->src_offset_x = g_xx->src_offset_x;
7814 g_em->src_offset_y = g_xx->src_offset_y;
7815 g_em->dst_offset_x = g_xx->dst_offset_x;
7816 g_em->dst_offset_y = g_xx->dst_offset_y;
7817 g_em->width = g_xx->width;
7818 g_em->height = g_xx->height;
7819 g_em->unique_identifier = g_xx->unique_identifier;
7822 g_em->preserve_background = TRUE;
7827 for (p = 0; p < MAX_PLAYERS; p++)
7829 for (i = 0; i < SPR_MAX; i++)
7831 int element = player_mapping[p][i].element_rnd;
7832 int action = player_mapping[p][i].action;
7833 int direction = player_mapping[p][i].direction;
7835 for (j = 0; j < 8; j++)
7837 int effective_element = element;
7838 int effective_action = action;
7839 int graphic = (direction == MV_NONE ?
7840 el_act2img(effective_element, effective_action) :
7841 el_act_dir2img(effective_element, effective_action,
7843 struct GraphicInfo *g = &graphic_info[graphic];
7844 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7850 Bitmap *debug_bitmap = g_em->bitmap;
7851 int debug_src_x = g_em->src_x;
7852 int debug_src_y = g_em->src_y;
7855 int frame = getAnimationFrame(g->anim_frames,
7858 g->anim_start_frame,
7861 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7863 g_em->bitmap = src_bitmap;
7864 g_em->src_x = src_x;
7865 g_em->src_y = src_y;
7866 g_em->src_offset_x = 0;
7867 g_em->src_offset_y = 0;
7868 g_em->dst_offset_x = 0;
7869 g_em->dst_offset_y = 0;
7870 g_em->width = TILEX;
7871 g_em->height = TILEY;
7875 /* skip check for EMC elements not contained in original EMC artwork */
7876 if (element == EL_PLAYER_3 ||
7877 element == EL_PLAYER_4)
7880 if (g_em->bitmap != debug_bitmap ||
7881 g_em->src_x != debug_src_x ||
7882 g_em->src_y != debug_src_y)
7884 static int last_i = -1;
7892 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7893 p, i, element, element_info[element].token_name,
7894 element_action_info[effective_action].suffix, direction);
7896 if (element != effective_element)
7897 printf(" [%d ('%s')]",
7899 element_info[effective_element].token_name);
7903 if (g_em->bitmap != debug_bitmap)
7904 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7905 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7907 if (g_em->src_x != debug_src_x ||
7908 g_em->src_y != debug_src_y)
7909 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7911 g_em->src_x, g_em->src_y,
7912 g_em->src_x / 32, g_em->src_y / 32,
7913 debug_src_x, debug_src_y,
7914 debug_src_x / 32, debug_src_y / 32);
7916 num_em_gfx_errors++;
7926 printf("::: [%d errors found]\n", num_em_gfx_errors);
7932 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
7933 boolean any_player_moving,
7934 boolean any_player_snapping,
7935 boolean any_player_dropping)
7937 static boolean player_was_waiting = TRUE;
7939 if (frame == 0 && !any_player_dropping)
7941 if (!player_was_waiting)
7943 if (!SaveEngineSnapshotToList())
7946 player_was_waiting = TRUE;
7949 else if (any_player_moving || any_player_snapping || any_player_dropping)
7951 player_was_waiting = FALSE;
7955 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
7956 boolean murphy_is_dropping)
7958 static boolean player_was_waiting = TRUE;
7960 if (murphy_is_waiting)
7962 if (!player_was_waiting)
7964 if (!SaveEngineSnapshotToList())
7967 player_was_waiting = TRUE;
7972 player_was_waiting = FALSE;
7976 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7977 boolean any_player_moving,
7978 boolean any_player_snapping,
7979 boolean any_player_dropping)
7981 if (tape.single_step && tape.recording && !tape.pausing)
7982 if (frame == 0 && !any_player_dropping)
7983 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7985 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
7986 any_player_snapping, any_player_dropping);
7989 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
7990 boolean murphy_is_dropping)
7992 if (tape.single_step && tape.recording && !tape.pausing)
7993 if (murphy_is_waiting)
7994 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7996 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
7999 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
8000 int graphic, int sync_frame, int x, int y)
8002 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8004 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8007 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8009 return (IS_NEXT_FRAME(sync_frame, graphic));
8012 int getGraphicInfo_Delay(int graphic)
8014 return graphic_info[graphic].anim_delay;
8017 void PlayMenuSoundExt(int sound)
8019 if (sound == SND_UNDEFINED)
8022 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8023 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8026 if (IS_LOOP_SOUND(sound))
8027 PlaySoundLoop(sound);
8032 void PlayMenuSound()
8034 PlayMenuSoundExt(menu.sound[game_status]);
8037 void PlayMenuSoundStereo(int sound, int stereo_position)
8039 if (sound == SND_UNDEFINED)
8042 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8043 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8046 if (IS_LOOP_SOUND(sound))
8047 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8049 PlaySoundStereo(sound, stereo_position);
8052 void PlayMenuSoundIfLoopExt(int sound)
8054 if (sound == SND_UNDEFINED)
8057 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8058 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8061 if (IS_LOOP_SOUND(sound))
8062 PlaySoundLoop(sound);
8065 void PlayMenuSoundIfLoop()
8067 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8070 void PlayMenuMusicExt(int music)
8072 if (music == MUS_UNDEFINED)
8075 if (!setup.sound_music)
8081 void PlayMenuMusic()
8083 PlayMenuMusicExt(menu.music[game_status]);
8086 void PlaySoundActivating()
8089 PlaySound(SND_MENU_ITEM_ACTIVATING);
8093 void PlaySoundSelecting()
8096 PlaySound(SND_MENU_ITEM_SELECTING);
8100 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8102 boolean change_fullscreen = (setup.fullscreen !=
8103 video.fullscreen_enabled);
8104 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
8105 !strEqual(setup.fullscreen_mode,
8106 video.fullscreen_mode_current));
8107 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8108 setup.window_scaling_percent !=
8109 video.window_scaling_percent);
8111 if (change_window_scaling_percent && video.fullscreen_enabled)
8114 if (!change_window_scaling_percent && !video.fullscreen_available)
8117 #if defined(TARGET_SDL2)
8118 if (change_window_scaling_percent)
8120 SDLSetWindowScaling(setup.window_scaling_percent);
8124 else if (change_fullscreen)
8126 SDLSetWindowFullscreen(setup.fullscreen);
8128 /* set setup value according to successfully changed fullscreen mode */
8129 setup.fullscreen = video.fullscreen_enabled;
8135 if (change_fullscreen ||
8136 change_fullscreen_mode ||
8137 change_window_scaling_percent)
8139 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8141 /* save backbuffer content which gets lost when toggling fullscreen mode */
8142 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8144 if (change_fullscreen_mode)
8146 /* keep fullscreen, but change fullscreen mode (screen resolution) */
8147 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
8150 if (change_window_scaling_percent)
8152 /* keep window mode, but change window scaling */
8153 video.fullscreen_enabled = TRUE; /* force new window scaling */
8156 /* toggle fullscreen */
8157 ChangeVideoModeIfNeeded(setup.fullscreen);
8159 /* set setup value according to successfully changed fullscreen mode */
8160 setup.fullscreen = video.fullscreen_enabled;
8162 /* restore backbuffer content from temporary backbuffer backup bitmap */
8163 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8165 FreeBitmap(tmp_backbuffer);
8167 /* update visible window/screen */
8168 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8172 void JoinRectangles(int *x, int *y, int *width, int *height,
8173 int x2, int y2, int width2, int height2)
8175 // do not join with "off-screen" rectangle
8176 if (x2 == -1 || y2 == -1)
8181 *width = MAX(*width, width2);
8182 *height = MAX(*height, height2);
8185 void SetGameStatus(int game_status_new)
8187 game_status = game_status_new;
8189 global.anim_status_next = game_status;
8192 void ChangeViewportPropertiesIfNeeded()
8194 int gfx_game_mode = game_status;
8195 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8197 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
8198 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8199 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8200 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8201 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8202 int new_win_xsize = vp_window->width;
8203 int new_win_ysize = vp_window->height;
8204 int border_size = vp_playfield->border_size;
8205 int new_sx = vp_playfield->x + border_size;
8206 int new_sy = vp_playfield->y + border_size;
8207 int new_sxsize = vp_playfield->width - 2 * border_size;
8208 int new_sysize = vp_playfield->height - 2 * border_size;
8209 int new_real_sx = vp_playfield->x;
8210 int new_real_sy = vp_playfield->y;
8211 int new_full_sxsize = vp_playfield->width;
8212 int new_full_sysize = vp_playfield->height;
8213 int new_dx = vp_door_1->x;
8214 int new_dy = vp_door_1->y;
8215 int new_dxsize = vp_door_1->width;
8216 int new_dysize = vp_door_1->height;
8217 int new_vx = vp_door_2->x;
8218 int new_vy = vp_door_2->y;
8219 int new_vxsize = vp_door_2->width;
8220 int new_vysize = vp_door_2->height;
8221 int new_ex = vp_door_3->x;
8222 int new_ey = vp_door_3->y;
8223 int new_exsize = vp_door_3->width;
8224 int new_eysize = vp_door_3->height;
8225 int new_tilesize_var =
8226 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8228 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8229 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8230 int new_scr_fieldx = new_sxsize / tilesize;
8231 int new_scr_fieldy = new_sysize / tilesize;
8232 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8233 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8234 boolean init_gfx_buffers = FALSE;
8235 boolean init_video_buffer = FALSE;
8236 boolean init_gadgets_and_toons = FALSE;
8237 boolean init_em_graphics = FALSE;
8239 if (new_win_xsize != WIN_XSIZE ||
8240 new_win_ysize != WIN_YSIZE)
8242 WIN_XSIZE = new_win_xsize;
8243 WIN_YSIZE = new_win_ysize;
8245 init_video_buffer = TRUE;
8246 init_gfx_buffers = TRUE;
8247 init_gadgets_and_toons = TRUE;
8249 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8252 if (new_scr_fieldx != SCR_FIELDX ||
8253 new_scr_fieldy != SCR_FIELDY)
8255 /* this always toggles between MAIN and GAME when using small tile size */
8257 SCR_FIELDX = new_scr_fieldx;
8258 SCR_FIELDY = new_scr_fieldy;
8260 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8271 new_sxsize != SXSIZE ||
8272 new_sysize != SYSIZE ||
8273 new_dxsize != DXSIZE ||
8274 new_dysize != DYSIZE ||
8275 new_vxsize != VXSIZE ||
8276 new_vysize != VYSIZE ||
8277 new_exsize != EXSIZE ||
8278 new_eysize != EYSIZE ||
8279 new_real_sx != REAL_SX ||
8280 new_real_sy != REAL_SY ||
8281 new_full_sxsize != FULL_SXSIZE ||
8282 new_full_sysize != FULL_SYSIZE ||
8283 new_tilesize_var != TILESIZE_VAR
8286 // ------------------------------------------------------------------------
8287 // determine next fading area for changed viewport definitions
8288 // ------------------------------------------------------------------------
8290 // start with current playfield area (default fading area)
8293 FADE_SXSIZE = FULL_SXSIZE;
8294 FADE_SYSIZE = FULL_SYSIZE;
8296 // add new playfield area if position or size has changed
8297 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8298 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8300 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8301 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8304 // add current and new door 1 area if position or size has changed
8305 if (new_dx != DX || new_dy != DY ||
8306 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8308 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8309 DX, DY, DXSIZE, DYSIZE);
8310 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8311 new_dx, new_dy, new_dxsize, new_dysize);
8314 // add current and new door 2 area if position or size has changed
8315 if (new_dx != VX || new_dy != VY ||
8316 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8318 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8319 VX, VY, VXSIZE, VYSIZE);
8320 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8321 new_vx, new_vy, new_vxsize, new_vysize);
8324 // ------------------------------------------------------------------------
8325 // handle changed tile size
8326 // ------------------------------------------------------------------------
8328 if (new_tilesize_var != TILESIZE_VAR)
8330 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8332 // changing tile size invalidates scroll values of engine snapshots
8333 FreeEngineSnapshotSingle();
8335 // changing tile size requires update of graphic mapping for EM engine
8336 init_em_graphics = TRUE;
8347 SXSIZE = new_sxsize;
8348 SYSIZE = new_sysize;
8349 DXSIZE = new_dxsize;
8350 DYSIZE = new_dysize;
8351 VXSIZE = new_vxsize;
8352 VYSIZE = new_vysize;
8353 EXSIZE = new_exsize;
8354 EYSIZE = new_eysize;
8355 REAL_SX = new_real_sx;
8356 REAL_SY = new_real_sy;
8357 FULL_SXSIZE = new_full_sxsize;
8358 FULL_SYSIZE = new_full_sysize;
8359 TILESIZE_VAR = new_tilesize_var;
8361 init_gfx_buffers = TRUE;
8362 init_gadgets_and_toons = TRUE;
8364 // printf("::: viewports: init_gfx_buffers\n");
8365 // printf("::: viewports: init_gadgets_and_toons\n");
8368 if (init_gfx_buffers)
8370 // printf("::: init_gfx_buffers\n");
8372 SCR_FIELDX = new_scr_fieldx_buffers;
8373 SCR_FIELDY = new_scr_fieldy_buffers;
8377 SCR_FIELDX = new_scr_fieldx;
8378 SCR_FIELDY = new_scr_fieldy;
8380 SetDrawDeactivationMask(REDRAW_NONE);
8381 SetDrawBackgroundMask(REDRAW_FIELD);
8384 if (init_video_buffer)
8386 // printf("::: init_video_buffer\n");
8388 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8389 InitImageTextures();
8392 if (init_gadgets_and_toons)
8394 // printf("::: init_gadgets_and_toons\n");
8398 InitGlobalAnimations();
8401 if (init_em_graphics)
8403 InitGraphicInfo_EM();