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;
648 void FadeOut(int fade_mask)
651 DrawMaskedBorder(REDRAW_ALL);
654 if (fading.fade_mode & FADE_TYPE_TRANSFORM)
655 FadeExt(fade_mask, fading.fade_mode, FADE_TYPE_FADE_OUT);
657 FadeExt(fade_mask, FADE_MODE_FADE_OUT, FADE_TYPE_FADE_OUT);
659 global.border_status = game_status;
662 static void FadeSetLeaveNext(struct TitleFadingInfo fading_leave, boolean set)
664 static struct TitleFadingInfo fading_leave_stored;
667 fading_leave_stored = fading_leave;
669 fading = fading_leave_stored;
672 void FadeSetEnterMenu()
674 fading = menu.enter_menu;
676 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
679 void FadeSetLeaveMenu()
681 fading = menu.leave_menu;
683 FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
686 void FadeSetEnterScreen()
688 fading = menu.enter_screen[game_status];
690 FadeSetLeaveNext(menu.leave_screen[game_status], TRUE); /* store */
693 void FadeSetNextScreen()
695 fading = menu.next_screen[game_status];
697 // (do not overwrite fade mode set by FadeSetEnterScreen)
698 // FadeSetLeaveNext(fading, TRUE); /* (keep same fade mode) */
701 void FadeSetLeaveScreen()
703 FadeSetLeaveNext(menu.leave_screen[game_status], FALSE); /* recall */
706 void FadeSetFromType(int type)
708 if (type & TYPE_ENTER_SCREEN)
709 FadeSetEnterScreen();
710 else if (type & TYPE_ENTER)
712 else if (type & TYPE_LEAVE)
716 void FadeSetDisabled()
718 static struct TitleFadingInfo fading_none = { FADE_MODE_NONE, -1, -1, -1 };
720 fading = fading_none;
723 void FadeSkipNextFadeIn()
725 FadeExt(0, FADE_MODE_SKIP_FADE_IN, FADE_TYPE_SKIP);
728 void FadeSkipNextFadeOut()
730 FadeExt(0, FADE_MODE_SKIP_FADE_OUT, FADE_TYPE_SKIP);
733 Bitmap *getBitmapFromGraphicOrDefault(int graphic, int default_graphic)
735 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
737 return (graphic == IMG_UNDEFINED ? NULL :
738 graphic_info[graphic].bitmap != NULL || redefined ?
739 graphic_info[graphic].bitmap :
740 graphic_info[default_graphic].bitmap);
743 Bitmap *getBackgroundBitmap(int graphic)
745 return getBitmapFromGraphicOrDefault(graphic, IMG_BACKGROUND);
748 Bitmap *getGlobalBorderBitmap(int graphic)
750 return getBitmapFromGraphicOrDefault(graphic, IMG_GLOBAL_BORDER);
753 Bitmap *getGlobalBorderBitmapFromGameStatus()
756 (game_status == GAME_MODE_MAIN ||
757 game_status == GAME_MODE_PSEUDO_TYPENAME ? IMG_GLOBAL_BORDER_MAIN :
758 game_status == GAME_MODE_SCORES ? IMG_GLOBAL_BORDER_SCORES :
759 game_status == GAME_MODE_EDITOR ? IMG_GLOBAL_BORDER_EDITOR :
760 game_status == GAME_MODE_PLAYING ? IMG_GLOBAL_BORDER_PLAYING :
763 return getGlobalBorderBitmap(graphic);
766 void SetWindowBackgroundImageIfDefined(int graphic)
768 if (graphic_info[graphic].bitmap)
769 SetWindowBackgroundBitmap(graphic_info[graphic].bitmap);
772 void SetMainBackgroundImageIfDefined(int graphic)
774 if (graphic_info[graphic].bitmap)
775 SetMainBackgroundBitmap(graphic_info[graphic].bitmap);
778 void SetDoorBackgroundImageIfDefined(int graphic)
780 if (graphic_info[graphic].bitmap)
781 SetDoorBackgroundBitmap(graphic_info[graphic].bitmap);
784 void SetWindowBackgroundImage(int graphic)
786 SetWindowBackgroundBitmap(getBackgroundBitmap(graphic));
789 void SetMainBackgroundImage(int graphic)
791 SetMainBackgroundBitmap(getBackgroundBitmap(graphic));
794 void SetDoorBackgroundImage(int graphic)
796 SetDoorBackgroundBitmap(getBackgroundBitmap(graphic));
799 void SetPanelBackground()
801 struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
803 BlitBitmapTiled(gfx->bitmap, bitmap_db_panel, gfx->src_x, gfx->src_y,
804 gfx->width, gfx->height, 0, 0, DXSIZE, DYSIZE);
806 SetDoorBackgroundBitmap(bitmap_db_panel);
809 void DrawBackground(int x, int y, int width, int height)
811 /* "drawto" might still point to playfield buffer here (hall of fame) */
812 ClearRectangleOnBackground(backbuffer, x, y, width, height);
814 if (IN_GFX_FIELD_FULL(x, y))
815 redraw_mask |= REDRAW_FIELD;
816 else if (IN_GFX_DOOR_1(x, y))
817 redraw_mask |= REDRAW_DOOR_1;
818 else if (IN_GFX_DOOR_2(x, y))
819 redraw_mask |= REDRAW_DOOR_2;
820 else if (IN_GFX_DOOR_3(x, y))
821 redraw_mask |= REDRAW_DOOR_3;
824 void DrawBackgroundForFont(int x, int y, int width, int height, int font_nr)
826 struct FontBitmapInfo *font = getFontBitmapInfo(font_nr);
828 if (font->bitmap == NULL)
831 DrawBackground(x, y, width, height);
834 void DrawBackgroundForGraphic(int x, int y, int width, int height, int graphic)
836 struct GraphicInfo *g = &graphic_info[graphic];
838 if (g->bitmap == NULL)
841 DrawBackground(x, y, width, height);
844 static int game_status_last = -1;
845 static Bitmap *global_border_bitmap_last = NULL;
846 static Bitmap *global_border_bitmap = NULL;
847 static int real_sx_last = -1, real_sy_last = -1;
848 static int full_sxsize_last = -1, full_sysize_last = -1;
849 static int dx_last = -1, dy_last = -1;
850 static int dxsize_last = -1, dysize_last = -1;
851 static int vx_last = -1, vy_last = -1;
852 static int vxsize_last = -1, vysize_last = -1;
854 boolean CheckIfGlobalBorderHasChanged()
856 // if game status has not changed, global border has not changed either
857 if (game_status == game_status_last)
860 // determine and store new global border bitmap for current game status
861 global_border_bitmap = getGlobalBorderBitmapFromGameStatus();
863 return (global_border_bitmap_last != global_border_bitmap);
866 boolean CheckIfGlobalBorderRedrawIsNeeded()
868 // if game status has not changed, nothing has to be redrawn
869 if (game_status == game_status_last)
872 // redraw if last screen was title screen
873 if (game_status_last == GAME_MODE_TITLE)
876 // redraw if global screen border has changed
877 if (CheckIfGlobalBorderHasChanged())
880 // redraw if position or size of playfield area has changed
881 if (real_sx_last != REAL_SX || real_sy_last != REAL_SY ||
882 full_sxsize_last != FULL_SXSIZE || full_sysize_last != FULL_SYSIZE)
885 // redraw if position or size of door area has changed
886 if (dx_last != DX || dy_last != DY ||
887 dxsize_last != DXSIZE || dysize_last != DYSIZE)
890 // redraw if position or size of tape area has changed
891 if (vx_last != VX || vy_last != VY ||
892 vxsize_last != VXSIZE || vysize_last != VYSIZE)
898 void RedrawGlobalBorderFromBitmap(Bitmap *bitmap)
901 BlitBitmap(bitmap, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
903 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
906 void RedrawGlobalBorder()
908 Bitmap *bitmap = getGlobalBorderBitmapFromGameStatus();
910 RedrawGlobalBorderFromBitmap(bitmap);
912 redraw_mask = REDRAW_ALL;
915 static void RedrawGlobalBorderIfNeeded()
917 if (game_status == game_status_last)
920 // copy current draw buffer to later copy back areas that have not changed
921 if (game_status_last != GAME_MODE_TITLE)
922 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
924 if (CheckIfGlobalBorderRedrawIsNeeded())
926 // redraw global screen border (or clear, if defined to be empty)
927 RedrawGlobalBorderFromBitmap(global_border_bitmap);
929 // copy previous playfield and door areas, if they are defined on both
930 // previous and current screen and if they still have the same size
932 if (real_sx_last != -1 && real_sy_last != -1 &&
933 REAL_SX != -1 && REAL_SY != -1 &&
934 full_sxsize_last == FULL_SXSIZE && full_sysize_last == FULL_SYSIZE)
935 BlitBitmap(bitmap_db_store, backbuffer,
936 real_sx_last, real_sy_last, FULL_SXSIZE, FULL_SYSIZE,
939 if (dx_last != -1 && dy_last != -1 &&
940 DX != -1 && DY != -1 &&
941 dxsize_last == DXSIZE && dysize_last == DYSIZE)
942 BlitBitmap(bitmap_db_store, backbuffer,
943 dx_last, dy_last, DXSIZE, DYSIZE, DX, DY);
945 if (vx_last != -1 && vy_last != -1 &&
946 VX != -1 && VY != -1 &&
947 vxsize_last == VXSIZE && vysize_last == VYSIZE)
948 BlitBitmap(bitmap_db_store, backbuffer,
949 vx_last, vy_last, VXSIZE, VYSIZE, VX, VY);
951 redraw_mask = REDRAW_ALL;
954 game_status_last = game_status;
956 global_border_bitmap_last = global_border_bitmap;
958 real_sx_last = REAL_SX;
959 real_sy_last = REAL_SY;
960 full_sxsize_last = FULL_SXSIZE;
961 full_sysize_last = FULL_SYSIZE;
964 dxsize_last = DXSIZE;
965 dysize_last = DYSIZE;
968 vxsize_last = VXSIZE;
969 vysize_last = VYSIZE;
974 RedrawGlobalBorderIfNeeded();
976 /* !!! "drawto" might still point to playfield buffer here (see above) !!! */
977 /* (when entering hall of fame after playing) */
978 DrawBackground(REAL_SX, REAL_SY, FULL_SXSIZE, FULL_SYSIZE);
980 /* !!! maybe this should be done before clearing the background !!! */
981 if (game_status == GAME_MODE_PLAYING)
983 ClearRectangle(fieldbuffer, 0, 0, FXSIZE, FYSIZE);
984 SetDrawtoField(DRAW_FIELDBUFFER);
988 SetDrawtoField(DRAW_BACKBUFFER);
992 void MarkTileDirty(int x, int y)
994 redraw_mask |= REDRAW_FIELD;
997 void SetBorderElement()
1001 BorderElement = EL_EMPTY;
1003 for (y = 0; y < lev_fieldy && BorderElement == EL_EMPTY; y++)
1005 for (x = 0; x < lev_fieldx; x++)
1007 if (!IS_INDESTRUCTIBLE(Feld[x][y]))
1008 BorderElement = EL_STEELWALL;
1010 if (y != 0 && y != lev_fieldy - 1 && x != lev_fieldx - 1)
1016 void FloodFillLevel(int from_x, int from_y, int fill_element,
1017 short field[MAX_LEV_FIELDX][MAX_LEV_FIELDY],
1018 int max_fieldx, int max_fieldy)
1022 static int check[4][2] = { { -1, 0 }, { 0, -1 }, { 1, 0 }, { 0, 1 } };
1023 static int safety = 0;
1025 /* check if starting field still has the desired content */
1026 if (field[from_x][from_y] == fill_element)
1031 if (safety > max_fieldx * max_fieldy)
1032 Error(ERR_EXIT, "Something went wrong in 'FloodFill()'. Please debug.");
1034 old_element = field[from_x][from_y];
1035 field[from_x][from_y] = fill_element;
1037 for (i = 0; i < 4; i++)
1039 x = from_x + check[i][0];
1040 y = from_y + check[i][1];
1042 if (IN_FIELD(x, y, max_fieldx, max_fieldy) && field[x][y] == old_element)
1043 FloodFillLevel(x, y, fill_element, field, max_fieldx, max_fieldy);
1049 void SetRandomAnimationValue(int x, int y)
1051 gfx.anim_random_frame = GfxRandom[x][y];
1054 int getGraphicAnimationFrame(int graphic, int sync_frame)
1056 /* animation synchronized with global frame counter, not move position */
1057 if (graphic_info[graphic].anim_global_sync || sync_frame < 0)
1058 sync_frame = FrameCounter;
1060 return getAnimationFrame(graphic_info[graphic].anim_frames,
1061 graphic_info[graphic].anim_delay,
1062 graphic_info[graphic].anim_mode,
1063 graphic_info[graphic].anim_start_frame,
1067 void getSizedGraphicSourceExt(int graphic, int frame, int tilesize,
1068 Bitmap **bitmap, int *x, int *y,
1069 boolean get_backside)
1071 struct GraphicInfo *g = &graphic_info[graphic];
1072 Bitmap *src_bitmap = g->bitmap;
1073 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1074 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1075 int tilesize_capped = MIN(MAX(1, tilesize), TILESIZE);
1077 // if no in-game graphics defined, always use standard graphic size
1078 if (g->bitmaps[IMG_BITMAP_GAME] == NULL)
1079 tilesize = TILESIZE;
1081 if (tilesize == gfx.standard_tile_size)
1082 src_bitmap = g->bitmaps[IMG_BITMAP_STANDARD];
1083 else if (tilesize == game.tile_size)
1084 src_bitmap = g->bitmaps[IMG_BITMAP_GAME];
1086 src_bitmap = g->bitmaps[IMG_BITMAP_1x1 - log_2(tilesize_capped)];
1088 if (g->offset_y == 0) /* frames are ordered horizontally */
1090 int max_width = g->anim_frames_per_line * g->width;
1091 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1093 src_x = pos % max_width;
1094 src_y = src_y % g->height + pos / max_width * g->height;
1096 else if (g->offset_x == 0) /* frames are ordered vertically */
1098 int max_height = g->anim_frames_per_line * g->height;
1099 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1101 src_x = src_x % g->width + pos / max_height * g->width;
1102 src_y = pos % max_height;
1104 else /* frames are ordered diagonally */
1106 src_x = src_x + frame * g->offset_x;
1107 src_y = src_y + frame * g->offset_y;
1110 *bitmap = src_bitmap;
1111 *x = src_x * tilesize / g->tile_size;
1112 *y = src_y * tilesize / g->tile_size;
1115 void getFixedGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1116 int *x, int *y, boolean get_backside)
1118 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y,
1122 void getSizedGraphicSource(int graphic, int frame, int tilesize,
1123 Bitmap **bitmap, int *x, int *y)
1125 getSizedGraphicSourceExt(graphic, frame, tilesize, bitmap, x, y, FALSE);
1128 void getFixedGraphicSource(int graphic, int frame,
1129 Bitmap **bitmap, int *x, int *y)
1131 getSizedGraphicSourceExt(graphic, frame, TILESIZE, bitmap, x, y, FALSE);
1134 void getMiniGraphicSource(int graphic, Bitmap **bitmap, int *x, int *y)
1136 getSizedGraphicSource(graphic, 0, MINI_TILESIZE, bitmap, x, y);
1139 inline static void getGraphicSourceExt(int graphic, int frame, Bitmap **bitmap,
1140 int *x, int *y, boolean get_backside)
1142 struct GraphicInfo *g = &graphic_info[graphic];
1143 int src_x = g->src_x + (get_backside ? g->offset2_x : 0);
1144 int src_y = g->src_y + (get_backside ? g->offset2_y : 0);
1146 if (TILESIZE_VAR != TILESIZE)
1147 return getSizedGraphicSourceExt(graphic, frame, TILESIZE_VAR, bitmap, x, y,
1150 *bitmap = g->bitmap;
1152 if (g->offset_y == 0) /* frames are ordered horizontally */
1154 int max_width = g->anim_frames_per_line * g->width;
1155 int pos = (src_y / g->height) * max_width + src_x + frame * g->offset_x;
1157 *x = pos % max_width;
1158 *y = src_y % g->height + pos / max_width * g->height;
1160 else if (g->offset_x == 0) /* frames are ordered vertically */
1162 int max_height = g->anim_frames_per_line * g->height;
1163 int pos = (src_x / g->width) * max_height + src_y + frame * g->offset_y;
1165 *x = src_x % g->width + pos / max_height * g->width;
1166 *y = pos % max_height;
1168 else /* frames are ordered diagonally */
1170 *x = src_x + frame * g->offset_x;
1171 *y = src_y + frame * g->offset_y;
1174 *x = *x * TILESIZE_VAR / g->tile_size;
1175 *y = *y * TILESIZE_VAR / g->tile_size;
1178 void getGraphicSource(int graphic, int frame, Bitmap **bitmap, int *x, int *y)
1180 getGraphicSourceExt(graphic, frame, bitmap, x, y, FALSE);
1183 void DrawGraphic(int x, int y, int graphic, int frame)
1186 if (!IN_SCR_FIELD(x, y))
1188 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1189 printf("DrawGraphic(): This should never happen!\n");
1194 DrawGraphicExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR, graphic,
1197 MarkTileDirty(x, y);
1200 void DrawFixedGraphic(int x, int y, int graphic, int frame)
1203 if (!IN_SCR_FIELD(x, y))
1205 printf("DrawGraphic(): x = %d, y = %d, graphic = %d\n", x, y, graphic);
1206 printf("DrawGraphic(): This should never happen!\n");
1211 DrawFixedGraphicExt(drawto_field, FX + x * TILEX, FY + y * TILEY, graphic,
1213 MarkTileDirty(x, y);
1216 void DrawGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1222 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1224 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX_VAR, TILEY_VAR, x, y);
1227 void DrawFixedGraphicExt(DrawBuffer *dst_bitmap, int x, int y, int graphic,
1233 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1234 BlitBitmap(src_bitmap, dst_bitmap, src_x, src_y, TILEX, TILEY, x, y);
1237 void DrawGraphicThruMask(int x, int y, int graphic, int frame)
1240 if (!IN_SCR_FIELD(x, y))
1242 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1243 printf("DrawGraphicThruMask(): This should never happen!\n");
1248 DrawGraphicThruMaskExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
1251 MarkTileDirty(x, y);
1254 void DrawFixedGraphicThruMask(int x, int y, int graphic, int frame)
1257 if (!IN_SCR_FIELD(x, y))
1259 printf("DrawGraphicThruMask(): x = %d,y = %d, graphic = %d\n",x,y,graphic);
1260 printf("DrawGraphicThruMask(): This should never happen!\n");
1265 DrawFixedGraphicThruMaskExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
1267 MarkTileDirty(x, y);
1270 void DrawGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y, int graphic,
1276 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1278 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX_VAR, TILEY_VAR,
1282 void DrawFixedGraphicThruMaskExt(DrawBuffer *d, int dst_x, int dst_y,
1283 int graphic, int frame)
1288 getFixedGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1290 BlitBitmapMasked(src_bitmap, d, src_x, src_y, TILEX, TILEY,
1294 void DrawSizedGraphic(int x, int y, int graphic, int frame, int tilesize)
1296 DrawSizedGraphicExt(drawto, SX + x * tilesize, SY + y * tilesize, graphic,
1298 MarkTileDirty(x / tilesize, y / tilesize);
1301 void DrawSizedGraphicExt(DrawBuffer *d, int x, int y, int graphic, int frame,
1307 getSizedGraphicSource(graphic, frame, tilesize, &src_bitmap, &src_x, &src_y);
1308 BlitBitmap(src_bitmap, d, src_x, src_y, tilesize, tilesize, x, y);
1311 void DrawMiniGraphic(int x, int y, int graphic)
1313 DrawMiniGraphicExt(drawto, SX + x * MINI_TILEX,SY + y * MINI_TILEY, graphic);
1314 MarkTileDirty(x / 2, y / 2);
1317 void DrawMiniGraphicExt(DrawBuffer *d, int x, int y, int graphic)
1322 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
1323 BlitBitmap(src_bitmap, d, src_x, src_y, MINI_TILEX, MINI_TILEY, x, y);
1326 inline static void DrawGraphicShiftedNormal(int x, int y, int dx, int dy,
1327 int graphic, int frame,
1328 int cut_mode, int mask_mode)
1333 int width = TILEX, height = TILEY;
1336 if (dx || dy) /* shifted graphic */
1338 if (x < BX1) /* object enters playfield from the left */
1345 else if (x > BX2) /* object enters playfield from the right */
1351 else if (x == BX1 && dx < 0) /* object leaves playfield to the left */
1357 else if (x == BX2 && dx > 0) /* object leaves playfield to the right */
1359 else if (dx) /* general horizontal movement */
1360 MarkTileDirty(x + SIGN(dx), y);
1362 if (y < BY1) /* object enters playfield from the top */
1364 if (cut_mode == CUT_BELOW) /* object completely above top border */
1372 else if (y > BY2) /* object enters playfield from the bottom */
1378 else if (y == BY1 && dy < 0) /* object leaves playfield to the top */
1384 else if (dy > 0 && cut_mode == CUT_ABOVE)
1386 if (y == BY2) /* object completely above bottom border */
1392 MarkTileDirty(x, y + 1);
1393 } /* object leaves playfield to the bottom */
1394 else if (dy > 0 && (y == BY2 || cut_mode == CUT_BELOW))
1396 else if (dy) /* general vertical movement */
1397 MarkTileDirty(x, y + SIGN(dy));
1401 if (!IN_SCR_FIELD(x, y))
1403 printf("DrawGraphicShifted(): x = %d, y = %d, graphic = %d\n",x,y,graphic);
1404 printf("DrawGraphicShifted(): This should never happen!\n");
1409 width = width * TILESIZE_VAR / TILESIZE;
1410 height = height * TILESIZE_VAR / TILESIZE;
1411 cx = cx * TILESIZE_VAR / TILESIZE;
1412 cy = cy * TILESIZE_VAR / TILESIZE;
1413 dx = dx * TILESIZE_VAR / TILESIZE;
1414 dy = dy * TILESIZE_VAR / TILESIZE;
1416 if (width > 0 && height > 0)
1418 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1423 dst_x = FX + x * TILEX_VAR + dx;
1424 dst_y = FY + y * TILEY_VAR + dy;
1426 if (mask_mode == USE_MASKING)
1427 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1430 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1433 MarkTileDirty(x, y);
1437 inline static void DrawGraphicShiftedDouble(int x, int y, int dx, int dy,
1438 int graphic, int frame,
1439 int cut_mode, int mask_mode)
1444 int width = TILEX_VAR, height = TILEY_VAR;
1447 int x2 = x + SIGN(dx);
1448 int y2 = y + SIGN(dy);
1450 /* movement with two-tile animations must be sync'ed with movement position,
1451 not with current GfxFrame (which can be higher when using slow movement) */
1452 int anim_pos = (dx ? ABS(dx) : ABS(dy));
1453 int anim_frames = graphic_info[graphic].anim_frames;
1455 /* (we also need anim_delay here for movement animations with less frames) */
1456 int anim_delay = graphic_info[graphic].anim_delay;
1457 int sync_frame = anim_pos * anim_frames * anim_delay / TILESIZE;
1459 boolean draw_start_tile = (cut_mode != CUT_ABOVE); /* only for falling! */
1460 boolean draw_end_tile = (cut_mode != CUT_BELOW); /* only for falling! */
1462 /* re-calculate animation frame for two-tile movement animation */
1463 frame = getGraphicAnimationFrame(graphic, sync_frame);
1465 /* check if movement start graphic inside screen area and should be drawn */
1466 if (draw_start_tile && IN_SCR_FIELD(x1, y1))
1468 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, TRUE);
1470 dst_x = FX + x1 * TILEX_VAR;
1471 dst_y = FY + y1 * TILEY_VAR;
1473 if (mask_mode == USE_MASKING)
1474 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1477 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1480 MarkTileDirty(x1, y1);
1483 /* check if movement end graphic inside screen area and should be drawn */
1484 if (draw_end_tile && IN_SCR_FIELD(x2, y2))
1486 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
1488 dst_x = FX + x2 * TILEX_VAR;
1489 dst_y = FY + y2 * TILEY_VAR;
1491 if (mask_mode == USE_MASKING)
1492 BlitBitmapMasked(src_bitmap, drawto_field, src_x, src_y, width, height,
1495 BlitBitmap(src_bitmap, drawto_field, src_x, src_y, width, height,
1498 MarkTileDirty(x2, y2);
1502 static void DrawGraphicShifted(int x, int y, int dx, int dy,
1503 int graphic, int frame,
1504 int cut_mode, int mask_mode)
1508 DrawGraphic(x, y, graphic, frame);
1513 if (graphic_info[graphic].double_movement) /* EM style movement images */
1514 DrawGraphicShiftedDouble(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1516 DrawGraphicShiftedNormal(x, y, dx, dy, graphic, frame, cut_mode,mask_mode);
1519 void DrawGraphicShiftedThruMask(int x, int y, int dx, int dy, int graphic,
1520 int frame, int cut_mode)
1522 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, USE_MASKING);
1525 void DrawScreenElementExt(int x, int y, int dx, int dy, int element,
1526 int cut_mode, int mask_mode)
1528 int lx = LEVELX(x), ly = LEVELY(y);
1532 if (IN_LEV_FIELD(lx, ly))
1534 SetRandomAnimationValue(lx, ly);
1536 graphic = el_act_dir2img(element, GfxAction[lx][ly], GfxDir[lx][ly]);
1537 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1539 /* do not use double (EM style) movement graphic when not moving */
1540 if (graphic_info[graphic].double_movement && !dx && !dy)
1542 graphic = el_act_dir2img(element, ACTION_DEFAULT, GfxDir[lx][ly]);
1543 frame = getGraphicAnimationFrame(graphic, GfxFrame[lx][ly]);
1546 else /* border element */
1548 graphic = el2img(element);
1549 frame = getGraphicAnimationFrame(graphic, -1);
1552 if (element == EL_EXPANDABLE_WALL)
1554 boolean left_stopped = FALSE, right_stopped = FALSE;
1556 if (!IN_LEV_FIELD(lx - 1, ly) || IS_WALL(Feld[lx - 1][ly]))
1557 left_stopped = TRUE;
1558 if (!IN_LEV_FIELD(lx + 1, ly) || IS_WALL(Feld[lx + 1][ly]))
1559 right_stopped = TRUE;
1561 if (left_stopped && right_stopped)
1563 else if (left_stopped)
1565 graphic = IMG_EXPANDABLE_WALL_GROWING_RIGHT;
1566 frame = graphic_info[graphic].anim_frames - 1;
1568 else if (right_stopped)
1570 graphic = IMG_EXPANDABLE_WALL_GROWING_LEFT;
1571 frame = graphic_info[graphic].anim_frames - 1;
1576 DrawGraphicShifted(x, y, dx, dy, graphic, frame, cut_mode, mask_mode);
1577 else if (mask_mode == USE_MASKING)
1578 DrawGraphicThruMask(x, y, graphic, frame);
1580 DrawGraphic(x, y, graphic, frame);
1583 void DrawLevelElementExt(int x, int y, int dx, int dy, int element,
1584 int cut_mode, int mask_mode)
1586 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1587 DrawScreenElementExt(SCREENX(x), SCREENY(y), dx, dy, element,
1588 cut_mode, mask_mode);
1591 void DrawScreenElementShifted(int x, int y, int dx, int dy, int element,
1594 DrawScreenElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1597 void DrawLevelElementShifted(int x, int y, int dx, int dy, int element,
1600 DrawLevelElementExt(x, y, dx, dy, element, cut_mode, NO_MASKING);
1603 void DrawLevelElementThruMask(int x, int y, int element)
1605 DrawLevelElementExt(x, y, 0, 0, element, NO_CUTTING, USE_MASKING);
1608 void DrawLevelFieldThruMask(int x, int y)
1610 DrawLevelElementExt(x, y, 0, 0, Feld[x][y], NO_CUTTING, USE_MASKING);
1613 /* !!! implementation of quicksand is totally broken !!! */
1614 #define IS_CRUMBLED_TILE(x, y, e) \
1615 (GFX_CRUMBLED(e) && (!IN_LEV_FIELD(x, y) || \
1616 !IS_MOVING(x, y) || \
1617 (e) == EL_QUICKSAND_EMPTYING || \
1618 (e) == EL_QUICKSAND_FAST_EMPTYING))
1620 static void DrawLevelFieldCrumbledInnerCorners(int x, int y, int dx, int dy,
1625 int width, height, cx, cy;
1626 int sx = SCREENX(x), sy = SCREENY(y);
1627 int crumbled_border_size = graphic_info[graphic].border_size;
1630 getGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
1632 for (i = 1; i < 4; i++)
1634 int dxx = (i & 1 ? dx : 0);
1635 int dyy = (i & 2 ? dy : 0);
1638 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1641 /* check if neighbour field is of same crumble type */
1642 boolean same = (IS_CRUMBLED_TILE(xx, yy, element) &&
1643 graphic_info[graphic].class ==
1644 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class);
1646 /* return if check prevents inner corner */
1647 if (same == (dxx == dx && dyy == dy))
1651 /* if we reach this point, we have an inner corner */
1653 getGraphicSource(graphic, 1, &src_bitmap, &src_x, &src_y);
1655 width = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1656 height = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1657 cx = (dx > 0 ? TILESIZE_VAR - width : 0);
1658 cy = (dy > 0 ? TILESIZE_VAR - height : 0);
1660 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy,
1661 width, height, FX + sx * TILEX_VAR + cx, FY + sy * TILEY_VAR + cy);
1664 static void DrawLevelFieldCrumbledBorders(int x, int y, int graphic, int frame,
1669 int width, height, bx, by, cx, cy;
1670 int sx = SCREENX(x), sy = SCREENY(y);
1671 int crumbled_border_size = graphic_info[graphic].border_size;
1672 int crumbled_border_size_var = crumbled_border_size * TILESIZE_VAR / TILESIZE;
1673 int crumbled_border_pos_var = TILESIZE_VAR - crumbled_border_size_var;
1676 getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
1678 /* draw simple, sloppy, non-corner-accurate crumbled border */
1680 width = (dir == 1 || dir == 2 ? crumbled_border_size_var : TILESIZE_VAR);
1681 height = (dir == 0 || dir == 3 ? crumbled_border_size_var : TILESIZE_VAR);
1682 cx = (dir == 2 ? crumbled_border_pos_var : 0);
1683 cy = (dir == 3 ? crumbled_border_pos_var : 0);
1685 BlitBitmap(src_bitmap, drawto_field, src_x + cx, src_y + cy, width, height,
1686 FX + sx * TILEX_VAR + cx,
1687 FY + sy * TILEY_VAR + cy);
1689 /* (remaining middle border part must be at least as big as corner part) */
1690 if (!(graphic_info[graphic].style & STYLE_ACCURATE_BORDERS) ||
1691 crumbled_border_size >= TILESIZE / 3)
1694 /* correct corners of crumbled border, if needed */
1696 for (i = -1; i <= 1; i += 2)
1698 int xx = x + (dir == 0 || dir == 3 ? i : 0);
1699 int yy = y + (dir == 1 || dir == 2 ? i : 0);
1700 int element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1703 /* check if neighbour field is of same crumble type */
1704 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1705 graphic_info[graphic].class ==
1706 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1708 /* no crumbled corner, but continued crumbled border */
1710 int c1 = (dir == 2 || dir == 3 ? crumbled_border_pos_var : 0);
1711 int c2 = (i == 1 ? crumbled_border_pos_var : 0);
1712 int b1 = (i == 1 ? crumbled_border_size_var :
1713 TILESIZE_VAR - 2 * crumbled_border_size_var);
1715 width = crumbled_border_size_var;
1716 height = crumbled_border_size_var;
1718 if (dir == 1 || dir == 2)
1733 BlitBitmap(src_bitmap, drawto_field, src_x + bx, src_y + by,
1735 FX + sx * TILEX_VAR + cx,
1736 FY + sy * TILEY_VAR + cy);
1741 static void DrawLevelFieldCrumbledExt(int x, int y, int graphic, int frame)
1743 int sx = SCREENX(x), sy = SCREENY(y);
1746 static int xy[4][2] =
1754 if (!IN_LEV_FIELD(x, y))
1757 element = TILE_GFX_ELEMENT(x, y);
1759 /* crumble field itself */
1760 if (IS_CRUMBLED_TILE(x, y, element))
1762 if (!IN_SCR_FIELD(sx, sy))
1765 for (i = 0; i < 4; i++)
1767 int xx = x + xy[i][0];
1768 int yy = y + xy[i][1];
1770 element = (IN_LEV_FIELD(xx, yy) ? TILE_GFX_ELEMENT(xx, yy) :
1773 /* check if neighbour field is of same crumble type */
1774 if (IS_CRUMBLED_TILE(xx, yy, element) &&
1775 graphic_info[graphic].class ==
1776 graphic_info[el_act2crm(element, ACTION_DEFAULT)].class)
1779 DrawLevelFieldCrumbledBorders(x, y, graphic, frame, i);
1782 if ((graphic_info[graphic].style & STYLE_INNER_CORNERS) &&
1783 graphic_info[graphic].anim_frames == 2)
1785 for (i = 0; i < 4; i++)
1787 int dx = (i & 1 ? +1 : -1);
1788 int dy = (i & 2 ? +1 : -1);
1790 DrawLevelFieldCrumbledInnerCorners(x, y, dx, dy, graphic);
1794 MarkTileDirty(sx, sy);
1796 else /* center field not crumbled -- crumble neighbour fields */
1798 for (i = 0; i < 4; i++)
1800 int xx = x + xy[i][0];
1801 int yy = y + xy[i][1];
1802 int sxx = sx + xy[i][0];
1803 int syy = sy + xy[i][1];
1805 if (!IN_LEV_FIELD(xx, yy) ||
1806 !IN_SCR_FIELD(sxx, syy))
1809 if (Feld[xx][yy] == EL_ELEMENT_SNAPPING)
1812 element = TILE_GFX_ELEMENT(xx, yy);
1814 if (!IS_CRUMBLED_TILE(xx, yy, element))
1817 graphic = el_act2crm(element, ACTION_DEFAULT);
1819 DrawLevelFieldCrumbledBorders(xx, yy, graphic, 0, 3 - i);
1821 MarkTileDirty(sxx, syy);
1826 void DrawLevelFieldCrumbled(int x, int y)
1830 if (!IN_LEV_FIELD(x, y))
1833 if (Feld[x][y] == EL_ELEMENT_SNAPPING &&
1834 GfxElement[x][y] != EL_UNDEFINED &&
1835 GFX_CRUMBLED(GfxElement[x][y]))
1837 DrawLevelFieldCrumbledDigging(x, y, GfxDir[x][y], GfxFrame[x][y]);
1842 graphic = el_act2crm(TILE_GFX_ELEMENT(x, y), ACTION_DEFAULT);
1844 DrawLevelFieldCrumbledExt(x, y, graphic, 0);
1847 void DrawLevelFieldCrumbledDigging(int x, int y, int direction,
1850 int graphic1 = el_act_dir2img(GfxElement[x][y], ACTION_DIGGING, direction);
1851 int graphic2 = el_act_dir2crm(GfxElement[x][y], ACTION_DIGGING, direction);
1852 int frame1 = getGraphicAnimationFrame(graphic1, step_frame);
1853 int frame2 = getGraphicAnimationFrame(graphic2, step_frame);
1854 int sx = SCREENX(x), sy = SCREENY(y);
1856 DrawGraphic(sx, sy, graphic1, frame1);
1857 DrawLevelFieldCrumbledExt(x, y, graphic2, frame2);
1860 void DrawLevelFieldCrumbledNeighbours(int x, int y)
1862 int sx = SCREENX(x), sy = SCREENY(y);
1863 static int xy[4][2] =
1872 for (i = 0; i < 4; i++)
1874 int xx = x + xy[i][0];
1875 int yy = y + xy[i][1];
1876 int sxx = sx + xy[i][0];
1877 int syy = sy + xy[i][1];
1879 if (!IN_LEV_FIELD(xx, yy) ||
1880 !IN_SCR_FIELD(sxx, syy) ||
1881 !GFX_CRUMBLED(Feld[xx][yy]) ||
1885 DrawLevelField(xx, yy);
1889 static int getBorderElement(int x, int y)
1893 { EL_STEELWALL_TOPLEFT, EL_INVISIBLE_STEELWALL_TOPLEFT },
1894 { EL_STEELWALL_TOPRIGHT, EL_INVISIBLE_STEELWALL_TOPRIGHT },
1895 { EL_STEELWALL_BOTTOMLEFT, EL_INVISIBLE_STEELWALL_BOTTOMLEFT },
1896 { EL_STEELWALL_BOTTOMRIGHT, EL_INVISIBLE_STEELWALL_BOTTOMRIGHT },
1897 { EL_STEELWALL_VERTICAL, EL_INVISIBLE_STEELWALL_VERTICAL },
1898 { EL_STEELWALL_HORIZONTAL, EL_INVISIBLE_STEELWALL_HORIZONTAL },
1899 { EL_STEELWALL, EL_INVISIBLE_STEELWALL }
1901 int steel_type = (BorderElement == EL_STEELWALL ? 0 : 1);
1902 int steel_position = (x == -1 && y == -1 ? 0 :
1903 x == lev_fieldx && y == -1 ? 1 :
1904 x == -1 && y == lev_fieldy ? 2 :
1905 x == lev_fieldx && y == lev_fieldy ? 3 :
1906 x == -1 || x == lev_fieldx ? 4 :
1907 y == -1 || y == lev_fieldy ? 5 : 6);
1909 return border[steel_position][steel_type];
1912 void DrawScreenElement(int x, int y, int element)
1914 DrawScreenElementExt(x, y, 0, 0, element, NO_CUTTING, NO_MASKING);
1915 DrawLevelFieldCrumbled(LEVELX(x), LEVELY(y));
1918 void DrawLevelElement(int x, int y, int element)
1920 if (IN_LEV_FIELD(x, y) && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
1921 DrawScreenElement(SCREENX(x), SCREENY(y), element);
1924 void DrawScreenField(int x, int y)
1926 int lx = LEVELX(x), ly = LEVELY(y);
1927 int element, content;
1929 if (!IN_LEV_FIELD(lx, ly))
1931 if (lx < -1 || lx > lev_fieldx || ly < -1 || ly > lev_fieldy)
1934 element = getBorderElement(lx, ly);
1936 DrawScreenElement(x, y, element);
1941 element = Feld[lx][ly];
1942 content = Store[lx][ly];
1944 if (IS_MOVING(lx, ly))
1946 int horiz_move = (MovDir[lx][ly] == MV_LEFT || MovDir[lx][ly] == MV_RIGHT);
1947 boolean cut_mode = NO_CUTTING;
1949 if (element == EL_QUICKSAND_EMPTYING ||
1950 element == EL_QUICKSAND_FAST_EMPTYING ||
1951 element == EL_MAGIC_WALL_EMPTYING ||
1952 element == EL_BD_MAGIC_WALL_EMPTYING ||
1953 element == EL_DC_MAGIC_WALL_EMPTYING ||
1954 element == EL_AMOEBA_DROPPING)
1955 cut_mode = CUT_ABOVE;
1956 else if (element == EL_QUICKSAND_FILLING ||
1957 element == EL_QUICKSAND_FAST_FILLING ||
1958 element == EL_MAGIC_WALL_FILLING ||
1959 element == EL_BD_MAGIC_WALL_FILLING ||
1960 element == EL_DC_MAGIC_WALL_FILLING)
1961 cut_mode = CUT_BELOW;
1963 if (cut_mode == CUT_ABOVE)
1964 DrawScreenElement(x, y, element);
1966 DrawScreenElement(x, y, EL_EMPTY);
1969 DrawScreenElementShifted(x, y, MovPos[lx][ly], 0, element, NO_CUTTING);
1970 else if (cut_mode == NO_CUTTING)
1971 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], element, cut_mode);
1974 DrawScreenElementShifted(x, y, 0, MovPos[lx][ly], content, cut_mode);
1976 if (cut_mode == CUT_BELOW &&
1977 IN_LEV_FIELD(lx, ly + 1) && IN_SCR_FIELD(x, y + 1))
1978 DrawLevelElement(lx, ly + 1, element);
1981 if (content == EL_ACID)
1983 int dir = MovDir[lx][ly];
1984 int newlx = lx + (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
1985 int newly = ly + (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
1987 DrawLevelElementThruMask(newlx, newly, EL_ACID);
1990 else if (IS_BLOCKED(lx, ly))
1995 boolean cut_mode = NO_CUTTING;
1996 int element_old, content_old;
1998 Blocked2Moving(lx, ly, &oldx, &oldy);
2001 horiz_move = (MovDir[oldx][oldy] == MV_LEFT ||
2002 MovDir[oldx][oldy] == MV_RIGHT);
2004 element_old = Feld[oldx][oldy];
2005 content_old = Store[oldx][oldy];
2007 if (element_old == EL_QUICKSAND_EMPTYING ||
2008 element_old == EL_QUICKSAND_FAST_EMPTYING ||
2009 element_old == EL_MAGIC_WALL_EMPTYING ||
2010 element_old == EL_BD_MAGIC_WALL_EMPTYING ||
2011 element_old == EL_DC_MAGIC_WALL_EMPTYING ||
2012 element_old == EL_AMOEBA_DROPPING)
2013 cut_mode = CUT_ABOVE;
2015 DrawScreenElement(x, y, EL_EMPTY);
2018 DrawScreenElementShifted(sx, sy, MovPos[oldx][oldy], 0, element_old,
2020 else if (cut_mode == NO_CUTTING)
2021 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], element_old,
2024 DrawScreenElementShifted(sx, sy, 0, MovPos[oldx][oldy], content_old,
2027 else if (IS_DRAWABLE(element))
2028 DrawScreenElement(x, y, element);
2030 DrawScreenElement(x, y, EL_EMPTY);
2033 void DrawLevelField(int x, int y)
2035 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
2036 DrawScreenField(SCREENX(x), SCREENY(y));
2037 else if (IS_MOVING(x, y))
2041 Moving2Blocked(x, y, &newx, &newy);
2042 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
2043 DrawScreenField(SCREENX(newx), SCREENY(newy));
2045 else if (IS_BLOCKED(x, y))
2049 Blocked2Moving(x, y, &oldx, &oldy);
2050 if (IN_SCR_FIELD(SCREENX(oldx), SCREENY(oldy)))
2051 DrawScreenField(SCREENX(oldx), SCREENY(oldy));
2055 void DrawSizedElement(int x, int y, int element, int tilesize)
2059 graphic = el2edimg(element);
2060 DrawSizedGraphic(x, y, graphic, 0, tilesize);
2063 void DrawMiniElement(int x, int y, int element)
2067 graphic = el2edimg(element);
2068 DrawMiniGraphic(x, y, graphic);
2071 void DrawSizedElementOrWall(int sx, int sy, int scroll_x, int scroll_y,
2074 int x = sx + scroll_x, y = sy + scroll_y;
2076 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2077 DrawSizedElement(sx, sy, EL_EMPTY, tilesize);
2078 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2079 DrawSizedElement(sx, sy, Feld[x][y], tilesize);
2081 DrawSizedGraphic(sx, sy, el2edimg(getBorderElement(x, y)), 0, tilesize);
2084 void DrawMiniElementOrWall(int sx, int sy, int scroll_x, int scroll_y)
2086 int x = sx + scroll_x, y = sy + scroll_y;
2088 if (x < -1 || x > lev_fieldx || y < -1 || y > lev_fieldy)
2089 DrawMiniElement(sx, sy, EL_EMPTY);
2090 else if (x > -1 && x < lev_fieldx && y > -1 && y < lev_fieldy)
2091 DrawMiniElement(sx, sy, Feld[x][y]);
2093 DrawMiniGraphic(sx, sy, el2edimg(getBorderElement(x, y)));
2096 void DrawEnvelopeBackgroundTiles(int graphic, int startx, int starty,
2097 int x, int y, int xsize, int ysize,
2098 int tile_width, int tile_height)
2102 int dst_x = startx + x * tile_width;
2103 int dst_y = starty + y * tile_height;
2104 int width = graphic_info[graphic].width;
2105 int height = graphic_info[graphic].height;
2106 int inner_width_raw = MAX(width - 2 * tile_width, tile_width);
2107 int inner_height_raw = MAX(height - 2 * tile_height, tile_height);
2108 int inner_width = inner_width_raw - (inner_width_raw % tile_width);
2109 int inner_height = inner_height_raw - (inner_height_raw % tile_height);
2110 int inner_sx = (width >= 3 * tile_width ? tile_width : 0);
2111 int inner_sy = (height >= 3 * tile_height ? tile_height : 0);
2112 boolean draw_masked = graphic_info[graphic].draw_masked;
2114 getFixedGraphicSource(graphic, 0, &src_bitmap, &src_x, &src_y);
2116 if (src_bitmap == NULL || width < tile_width || height < tile_height)
2118 ClearRectangle(drawto, dst_x, dst_y, tile_width, tile_height);
2122 src_x += (x == 0 ? 0 : x == xsize - 1 ? width - tile_width :
2123 inner_sx + (x - 1) * tile_width % inner_width);
2124 src_y += (y == 0 ? 0 : y == ysize - 1 ? height - tile_height :
2125 inner_sy + (y - 1) * tile_height % inner_height);
2128 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2131 BlitBitmap(src_bitmap, drawto, src_x, src_y, tile_width, tile_height,
2135 void DrawEnvelopeBackground(int graphic, int startx, int starty,
2136 int x, int y, int xsize, int ysize, int font_nr)
2138 int font_width = getFontWidth(font_nr);
2139 int font_height = getFontHeight(font_nr);
2141 DrawEnvelopeBackgroundTiles(graphic, startx, starty, x, y, xsize, ysize,
2142 font_width, font_height);
2145 void AnimateEnvelope(int envelope_nr, int anim_mode, int action)
2147 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2148 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2149 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2150 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2151 boolean no_delay = (tape.warp_forward);
2152 unsigned int anim_delay = 0;
2153 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
2154 int anim_delay_value = (no_delay ? 0 : frame_delay_value) / 2;
2155 int font_nr = FONT_ENVELOPE_1 + envelope_nr;
2156 int font_width = getFontWidth(font_nr);
2157 int font_height = getFontHeight(font_nr);
2158 int max_xsize = level.envelope[envelope_nr].xsize;
2159 int max_ysize = level.envelope[envelope_nr].ysize;
2160 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize : 0);
2161 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize : 0);
2162 int xend = max_xsize;
2163 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize : 0);
2164 int xstep = (xstart < xend ? 1 : 0);
2165 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2167 int end = MAX(xend - xstart, yend - ystart);
2170 for (i = start; i <= end; i++)
2172 int last_frame = end; // last frame of this "for" loop
2173 int x = xstart + i * xstep;
2174 int y = ystart + i * ystep;
2175 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2176 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2177 int sx = SX + (SXSIZE - xsize * font_width) / 2;
2178 int sy = SY + (SYSIZE - ysize * font_height) / 2;
2181 SetDrawtoField(DRAW_FIELDBUFFER);
2183 BlitScreenToBitmap(backbuffer);
2185 SetDrawtoField(DRAW_BACKBUFFER);
2187 for (yy = 0; yy < ysize; yy++)
2188 for (xx = 0; xx < xsize; xx++)
2189 DrawEnvelopeBackground(graphic, sx, sy, xx, yy, xsize, ysize, font_nr);
2191 DrawTextBuffer(sx + font_width, sy + font_height,
2192 level.envelope[envelope_nr].text, font_nr, max_xsize,
2193 xsize - 2, ysize - 2, 0, mask_mode,
2194 level.envelope[envelope_nr].autowrap,
2195 level.envelope[envelope_nr].centered, FALSE);
2197 redraw_mask |= REDRAW_FIELD;
2200 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2204 void ShowEnvelope(int envelope_nr)
2206 int element = EL_ENVELOPE_1 + envelope_nr;
2207 int graphic = IMG_BACKGROUND_ENVELOPE_1 + envelope_nr;
2208 int sound_opening = element_info[element].sound[ACTION_OPENING];
2209 int sound_closing = element_info[element].sound[ACTION_CLOSING];
2210 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2211 boolean no_delay = (tape.warp_forward);
2212 int normal_delay_value = ONE_SECOND_DELAY / (ffwd_delay ? 2 : 1);
2213 int wait_delay_value = (no_delay ? 0 : normal_delay_value);
2214 int anim_mode = graphic_info[graphic].anim_mode;
2215 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2216 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2218 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2220 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2222 if (anim_mode == ANIM_DEFAULT)
2223 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_OPENING);
2225 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_OPENING);
2228 Delay(wait_delay_value);
2230 WaitForEventToContinue();
2232 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2234 if (anim_mode != ANIM_NONE)
2235 AnimateEnvelope(envelope_nr, main_anim_mode, ACTION_CLOSING);
2237 if (anim_mode == ANIM_DEFAULT)
2238 AnimateEnvelope(envelope_nr, ANIM_DEFAULT, ACTION_CLOSING);
2240 game.envelope_active = FALSE;
2242 SetDrawtoField(DRAW_FIELDBUFFER);
2244 redraw_mask |= REDRAW_FIELD;
2248 static void setRequestBasePosition(int *x, int *y)
2250 int sx_base, sy_base;
2252 if (request.x != -1)
2253 sx_base = request.x;
2254 else if (request.align == ALIGN_LEFT)
2256 else if (request.align == ALIGN_RIGHT)
2257 sx_base = SX + SXSIZE;
2259 sx_base = SX + SXSIZE / 2;
2261 if (request.y != -1)
2262 sy_base = request.y;
2263 else if (request.valign == VALIGN_TOP)
2265 else if (request.valign == VALIGN_BOTTOM)
2266 sy_base = SY + SYSIZE;
2268 sy_base = SY + SYSIZE / 2;
2274 static void setRequestPositionExt(int *x, int *y, int width, int height,
2275 boolean add_border_size)
2277 int border_size = request.border_size;
2278 int sx_base, sy_base;
2281 setRequestBasePosition(&sx_base, &sy_base);
2283 if (request.align == ALIGN_LEFT)
2285 else if (request.align == ALIGN_RIGHT)
2286 sx = sx_base - width;
2288 sx = sx_base - width / 2;
2290 if (request.valign == VALIGN_TOP)
2292 else if (request.valign == VALIGN_BOTTOM)
2293 sy = sy_base - height;
2295 sy = sy_base - height / 2;
2297 sx = MAX(0, MIN(sx, WIN_XSIZE - width));
2298 sy = MAX(0, MIN(sy, WIN_YSIZE - height));
2300 if (add_border_size)
2310 static void setRequestPosition(int *x, int *y, boolean add_border_size)
2312 setRequestPositionExt(x, y, request.width, request.height, add_border_size);
2315 void DrawEnvelopeRequest(char *text)
2317 int last_game_status = game_status; /* save current game status */
2318 char *text_final = text;
2319 char *text_door_style = NULL;
2320 int graphic = IMG_BACKGROUND_REQUEST;
2321 Bitmap *src_bitmap = graphic_info[graphic].bitmap;
2322 int mask_mode = (src_bitmap != NULL ? BLIT_MASKED : BLIT_ON_BACKGROUND);
2323 int font_nr = FONT_REQUEST;
2324 int font_width = getFontWidth(font_nr);
2325 int font_height = getFontHeight(font_nr);
2326 int border_size = request.border_size;
2327 int line_spacing = request.line_spacing;
2328 int line_height = font_height + line_spacing;
2329 int max_text_width = request.width - 2 * border_size;
2330 int max_text_height = request.height - 2 * border_size;
2331 int line_length = max_text_width / font_width;
2332 int max_lines = max_text_height / line_height;
2333 int text_width = line_length * font_width;
2334 int width = request.width;
2335 int height = request.height;
2336 int tile_size = MAX(request.step_offset, 1);
2337 int x_steps = width / tile_size;
2338 int y_steps = height / tile_size;
2339 int sx_offset = border_size;
2340 int sy_offset = border_size;
2344 if (request.centered)
2345 sx_offset = (request.width - text_width) / 2;
2347 if (request.wrap_single_words && !request.autowrap)
2349 char *src_text_ptr, *dst_text_ptr;
2351 text_door_style = checked_malloc(2 * strlen(text) + 1);
2353 src_text_ptr = text;
2354 dst_text_ptr = text_door_style;
2356 while (*src_text_ptr)
2358 if (*src_text_ptr == ' ' ||
2359 *src_text_ptr == '?' ||
2360 *src_text_ptr == '!')
2361 *dst_text_ptr++ = '\n';
2363 if (*src_text_ptr != ' ')
2364 *dst_text_ptr++ = *src_text_ptr;
2369 *dst_text_ptr = '\0';
2371 text_final = text_door_style;
2374 setRequestPosition(&sx, &sy, FALSE);
2376 ClearRectangle(backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE);
2378 for (y = 0; y < y_steps; y++)
2379 for (x = 0; x < x_steps; x++)
2380 DrawEnvelopeBackgroundTiles(graphic, sx, sy,
2381 x, y, x_steps, y_steps,
2382 tile_size, tile_size);
2384 /* force DOOR font inside door area */
2385 game_status = GAME_MODE_PSEUDO_DOOR;
2387 DrawTextBuffer(sx + sx_offset, sy + sy_offset, text_final, font_nr,
2388 line_length, -1, max_lines, line_spacing, mask_mode,
2389 request.autowrap, request.centered, FALSE);
2391 game_status = last_game_status; /* restore current game status */
2393 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
2394 RedrawGadget(tool_gadget[i]);
2396 // store readily prepared envelope request for later use when animating
2397 BlitBitmap(backbuffer, bitmap_db_cross, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2399 if (text_door_style)
2400 free(text_door_style);
2403 void AnimateEnvelopeRequest(int anim_mode, int action)
2405 int graphic = IMG_BACKGROUND_REQUEST;
2406 boolean draw_masked = graphic_info[graphic].draw_masked;
2407 int delay_value_normal = request.step_delay;
2408 int delay_value_fast = delay_value_normal / 2;
2409 boolean ffwd_delay = (tape.playing && tape.fast_forward);
2410 boolean no_delay = (tape.warp_forward);
2411 int delay_value = (ffwd_delay ? delay_value_fast : delay_value_normal);
2412 int anim_delay_value = (no_delay ? 0 : delay_value + 500 * 0) / 2;
2413 unsigned int anim_delay = 0;
2415 int tile_size = MAX(request.step_offset, 1);
2416 int max_xsize = request.width / tile_size;
2417 int max_ysize = request.height / tile_size;
2418 int max_xsize_inner = max_xsize - 2;
2419 int max_ysize_inner = max_ysize - 2;
2421 int xstart = (anim_mode & ANIM_VERTICAL ? max_xsize_inner : 0);
2422 int ystart = (anim_mode & ANIM_HORIZONTAL ? max_ysize_inner : 0);
2423 int xend = max_xsize_inner;
2424 int yend = (anim_mode != ANIM_DEFAULT ? max_ysize_inner : 0);
2425 int xstep = (xstart < xend ? 1 : 0);
2426 int ystep = (ystart < yend || xstep == 0 ? 1 : 0);
2428 int end = MAX(xend - xstart, yend - ystart);
2431 if (setup.quick_doors)
2438 for (i = start; i <= end; i++)
2440 int last_frame = end; // last frame of this "for" loop
2441 int x = xstart + i * xstep;
2442 int y = ystart + i * ystep;
2443 int xsize = (action == ACTION_CLOSING ? xend - (x - xstart) : x) + 2;
2444 int ysize = (action == ACTION_CLOSING ? yend - (y - ystart) : y) + 2;
2445 int xsize_size_left = (xsize - 1) * tile_size;
2446 int ysize_size_top = (ysize - 1) * tile_size;
2447 int max_xsize_pos = (max_xsize - 1) * tile_size;
2448 int max_ysize_pos = (max_ysize - 1) * tile_size;
2449 int width = xsize * tile_size;
2450 int height = ysize * tile_size;
2455 setRequestPosition(&src_x, &src_y, FALSE);
2456 setRequestPositionExt(&dst_x, &dst_y, width, height, FALSE);
2458 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2460 for (yy = 0; yy < 2; yy++)
2462 for (xx = 0; xx < 2; xx++)
2464 int src_xx = src_x + xx * max_xsize_pos;
2465 int src_yy = src_y + yy * max_ysize_pos;
2466 int dst_xx = dst_x + xx * xsize_size_left;
2467 int dst_yy = dst_y + yy * ysize_size_top;
2468 int xx_size = (xx ? tile_size : xsize_size_left);
2469 int yy_size = (yy ? tile_size : ysize_size_top);
2472 BlitBitmapMasked(bitmap_db_cross, backbuffer,
2473 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2475 BlitBitmap(bitmap_db_cross, backbuffer,
2476 src_xx, src_yy, xx_size, yy_size, dst_xx, dst_yy);
2480 redraw_mask |= REDRAW_FIELD;
2485 SkipUntilDelayReached(&anim_delay, anim_delay_value, &i, last_frame);
2489 void ShowEnvelopeRequest(char *text, unsigned int req_state, int action)
2491 int graphic = IMG_BACKGROUND_REQUEST;
2492 int sound_opening = SND_REQUEST_OPENING;
2493 int sound_closing = SND_REQUEST_CLOSING;
2494 int anim_mode_1 = request.anim_mode; /* (higher priority) */
2495 int anim_mode_2 = graphic_info[graphic].anim_mode; /* (lower priority) */
2496 int anim_mode = (anim_mode_1 != ANIM_DEFAULT ? anim_mode_1 : anim_mode_2);
2497 int main_anim_mode = (anim_mode == ANIM_NONE ? ANIM_VERTICAL|ANIM_HORIZONTAL:
2498 anim_mode == ANIM_DEFAULT ? ANIM_VERTICAL : anim_mode);
2500 if (game_status == GAME_MODE_PLAYING)
2501 BlitScreenToBitmap(backbuffer);
2503 SetDrawtoField(DRAW_BACKBUFFER);
2505 // SetDrawBackgroundMask(REDRAW_NONE);
2507 if (action == ACTION_OPENING)
2509 BlitBitmap(backbuffer, bitmap_db_store, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2511 if (req_state & REQ_ASK)
2513 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
2514 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
2516 else if (req_state & REQ_CONFIRM)
2518 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
2520 else if (req_state & REQ_PLAYER)
2522 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
2523 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
2524 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
2525 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
2528 DrawEnvelopeRequest(text);
2530 if (game_status != GAME_MODE_MAIN)
2534 game.envelope_active = TRUE; /* needed for RedrawPlayfield() events */
2536 if (action == ACTION_OPENING)
2538 PlayMenuSoundStereo(sound_opening, SOUND_MIDDLE);
2540 if (anim_mode == ANIM_DEFAULT)
2541 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_OPENING);
2543 AnimateEnvelopeRequest(main_anim_mode, ACTION_OPENING);
2547 PlayMenuSoundStereo(sound_closing, SOUND_MIDDLE);
2549 if (anim_mode != ANIM_NONE)
2550 AnimateEnvelopeRequest(main_anim_mode, ACTION_CLOSING);
2552 if (anim_mode == ANIM_DEFAULT)
2553 AnimateEnvelopeRequest(ANIM_DEFAULT, ACTION_CLOSING);
2556 game.envelope_active = FALSE;
2558 if (action == ACTION_CLOSING)
2560 if (game_status != GAME_MODE_MAIN)
2563 BlitBitmap(bitmap_db_store, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
2566 // SetDrawBackgroundMask(last_draw_background_mask);
2568 redraw_mask |= REDRAW_FIELD;
2570 if (game_status == GAME_MODE_MAIN)
2575 if (action == ACTION_CLOSING &&
2576 game_status == GAME_MODE_PLAYING &&
2577 level.game_engine_type == GAME_ENGINE_TYPE_RND)
2578 SetDrawtoField(DRAW_FIELDBUFFER);
2581 void DrawPreviewElement(int dst_x, int dst_y, int element, int tilesize)
2585 int graphic = el2preimg(element);
2587 getSizedGraphicSource(graphic, 0, tilesize, &src_bitmap, &src_x, &src_y);
2588 BlitBitmap(src_bitmap, drawto, src_x, src_y, tilesize, tilesize, dst_x,dst_y);
2591 void DrawLevel(int draw_background_mask)
2595 SetMainBackgroundImage(IMG_BACKGROUND_PLAYING);
2596 SetDrawBackgroundMask(draw_background_mask);
2600 for (x = BX1; x <= BX2; x++)
2601 for (y = BY1; y <= BY2; y++)
2602 DrawScreenField(x, y);
2604 redraw_mask |= REDRAW_FIELD;
2607 void DrawSizedLevel(int size_x, int size_y, int scroll_x, int scroll_y,
2612 for (x = 0; x < size_x; x++)
2613 for (y = 0; y < size_y; y++)
2614 DrawSizedElementOrWall(x, y, scroll_x, scroll_y, tilesize);
2616 redraw_mask |= REDRAW_FIELD;
2619 void DrawMiniLevel(int size_x, int size_y, int scroll_x, int scroll_y)
2623 for (x = 0; x < size_x; x++)
2624 for (y = 0; y < size_y; y++)
2625 DrawMiniElementOrWall(x, y, scroll_x, scroll_y);
2627 redraw_mask |= REDRAW_FIELD;
2630 static void DrawPreviewLevelPlayfieldExt(int from_x, int from_y)
2632 boolean show_level_border = (BorderElement != EL_EMPTY);
2633 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2634 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2635 int tile_size = preview.tile_size;
2636 int preview_width = preview.xsize * tile_size;
2637 int preview_height = preview.ysize * tile_size;
2638 int real_preview_xsize = MIN(level_xsize, preview.xsize);
2639 int real_preview_ysize = MIN(level_ysize, preview.ysize);
2640 int real_preview_width = real_preview_xsize * tile_size;
2641 int real_preview_height = real_preview_ysize * tile_size;
2642 int dst_x = SX + ALIGNED_XPOS(preview.x, preview_width, preview.align);
2643 int dst_y = SY + ALIGNED_YPOS(preview.y, preview_height, preview.valign);
2646 if (!IN_GFX_FIELD_FULL(dst_x, dst_y + preview_height - 1))
2649 DrawBackground(dst_x, dst_y, preview_width, preview_height);
2651 dst_x += (preview_width - real_preview_width) / 2;
2652 dst_y += (preview_height - real_preview_height) / 2;
2654 for (x = 0; x < real_preview_xsize; x++)
2656 for (y = 0; y < real_preview_ysize; y++)
2658 int lx = from_x + x + (show_level_border ? -1 : 0);
2659 int ly = from_y + y + (show_level_border ? -1 : 0);
2660 int element = (IN_LEV_FIELD(lx, ly) ? level.field[lx][ly] :
2661 getBorderElement(lx, ly));
2663 DrawPreviewElement(dst_x + x * tile_size, dst_y + y * tile_size,
2664 element, tile_size);
2668 redraw_mask |= REDRAW_FIELD;
2671 #define MICROLABEL_EMPTY 0
2672 #define MICROLABEL_LEVEL_NAME 1
2673 #define MICROLABEL_LEVEL_AUTHOR_HEAD 2
2674 #define MICROLABEL_LEVEL_AUTHOR 3
2675 #define MICROLABEL_IMPORTED_FROM_HEAD 4
2676 #define MICROLABEL_IMPORTED_FROM 5
2677 #define MICROLABEL_IMPORTED_BY_HEAD 6
2678 #define MICROLABEL_IMPORTED_BY 7
2680 static int getMaxTextLength(struct TextPosInfo *pos, int font_nr)
2682 int max_text_width = SXSIZE;
2683 int font_width = getFontWidth(font_nr);
2685 if (pos->align == ALIGN_CENTER)
2686 max_text_width = (pos->x < SXSIZE / 2 ? pos->x * 2 : (SXSIZE - pos->x) * 2);
2687 else if (pos->align == ALIGN_RIGHT)
2688 max_text_width = pos->x;
2690 max_text_width = SXSIZE - pos->x;
2692 return max_text_width / font_width;
2695 static void DrawPreviewLevelLabelExt(int mode)
2697 struct TextPosInfo *pos = &menu.main.text.level_info_2;
2698 char label_text[MAX_OUTPUT_LINESIZE + 1];
2699 int max_len_label_text;
2700 int font_nr = pos->font;
2703 if (!IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2706 if (mode == MICROLABEL_LEVEL_AUTHOR_HEAD ||
2707 mode == MICROLABEL_IMPORTED_FROM_HEAD ||
2708 mode == MICROLABEL_IMPORTED_BY_HEAD)
2709 font_nr = pos->font_alt;
2711 max_len_label_text = getMaxTextLength(pos, font_nr);
2713 if (pos->size != -1)
2714 max_len_label_text = pos->size;
2716 for (i = 0; i < max_len_label_text; i++)
2717 label_text[i] = ' ';
2718 label_text[max_len_label_text] = '\0';
2720 if (strlen(label_text) > 0)
2721 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2724 (mode == MICROLABEL_LEVEL_NAME ? level.name :
2725 mode == MICROLABEL_LEVEL_AUTHOR_HEAD ? "created by" :
2726 mode == MICROLABEL_LEVEL_AUTHOR ? level.author :
2727 mode == MICROLABEL_IMPORTED_FROM_HEAD ? "imported from" :
2728 mode == MICROLABEL_IMPORTED_FROM ? leveldir_current->imported_from :
2729 mode == MICROLABEL_IMPORTED_BY_HEAD ? "imported by" :
2730 mode == MICROLABEL_IMPORTED_BY ? leveldir_current->imported_by :""),
2731 max_len_label_text);
2732 label_text[max_len_label_text] = '\0';
2734 if (strlen(label_text) > 0)
2735 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2737 redraw_mask |= REDRAW_FIELD;
2740 static void DrawPreviewLevelExt(boolean restart)
2742 static unsigned int scroll_delay = 0;
2743 static unsigned int label_delay = 0;
2744 static int from_x, from_y, scroll_direction;
2745 static int label_state, label_counter;
2746 unsigned int scroll_delay_value = preview.step_delay;
2747 boolean show_level_border = (BorderElement != EL_EMPTY);
2748 int level_xsize = lev_fieldx + (show_level_border ? 2 : 0);
2749 int level_ysize = lev_fieldy + (show_level_border ? 2 : 0);
2750 int last_game_status = game_status; /* save current game status */
2757 if (preview.anim_mode == ANIM_CENTERED)
2759 if (level_xsize > preview.xsize)
2760 from_x = (level_xsize - preview.xsize) / 2;
2761 if (level_ysize > preview.ysize)
2762 from_y = (level_ysize - preview.ysize) / 2;
2765 from_x += preview.xoffset;
2766 from_y += preview.yoffset;
2768 scroll_direction = MV_RIGHT;
2772 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2773 DrawPreviewLevelLabelExt(label_state);
2775 /* initialize delay counters */
2776 DelayReached(&scroll_delay, 0);
2777 DelayReached(&label_delay, 0);
2779 if (leveldir_current->name)
2781 struct TextPosInfo *pos = &menu.main.text.level_info_1;
2782 char label_text[MAX_OUTPUT_LINESIZE + 1];
2783 int font_nr = pos->font;
2784 int max_len_label_text = getMaxTextLength(pos, font_nr);
2786 if (pos->size != -1)
2787 max_len_label_text = pos->size;
2789 strncpy(label_text, leveldir_current->name, max_len_label_text);
2790 label_text[max_len_label_text] = '\0';
2792 if (IN_GFX_FIELD_FULL(pos->x, pos->y + getFontHeight(pos->font)))
2793 DrawTextSAligned(pos->x, pos->y, label_text, font_nr, pos->align);
2796 game_status = last_game_status; /* restore current game status */
2801 /* scroll preview level, if needed */
2802 if (preview.anim_mode != ANIM_NONE &&
2803 (level_xsize > preview.xsize || level_ysize > preview.ysize) &&
2804 DelayReached(&scroll_delay, scroll_delay_value))
2806 switch (scroll_direction)
2811 from_x -= preview.step_offset;
2812 from_x = (from_x < 0 ? 0 : from_x);
2815 scroll_direction = MV_UP;
2819 if (from_x < level_xsize - preview.xsize)
2821 from_x += preview.step_offset;
2822 from_x = (from_x > level_xsize - preview.xsize ?
2823 level_xsize - preview.xsize : from_x);
2826 scroll_direction = MV_DOWN;
2832 from_y -= preview.step_offset;
2833 from_y = (from_y < 0 ? 0 : from_y);
2836 scroll_direction = MV_RIGHT;
2840 if (from_y < level_ysize - preview.ysize)
2842 from_y += preview.step_offset;
2843 from_y = (from_y > level_ysize - preview.ysize ?
2844 level_ysize - preview.ysize : from_y);
2847 scroll_direction = MV_LEFT;
2854 DrawPreviewLevelPlayfieldExt(from_x, from_y);
2857 /* !!! THIS ALL SUCKS -- SHOULD BE CLEANLY REWRITTEN !!! */
2858 /* redraw micro level label, if needed */
2859 if (!strEqual(level.name, NAMELESS_LEVEL_NAME) &&
2860 !strEqual(level.author, ANONYMOUS_NAME) &&
2861 !strEqual(level.author, leveldir_current->name) &&
2862 DelayReached(&label_delay, MICROLEVEL_LABEL_DELAY))
2864 int max_label_counter = 23;
2866 if (leveldir_current->imported_from != NULL &&
2867 strlen(leveldir_current->imported_from) > 0)
2868 max_label_counter += 14;
2869 if (leveldir_current->imported_by != NULL &&
2870 strlen(leveldir_current->imported_by) > 0)
2871 max_label_counter += 14;
2873 label_counter = (label_counter + 1) % max_label_counter;
2874 label_state = (label_counter >= 0 && label_counter <= 7 ?
2875 MICROLABEL_LEVEL_NAME :
2876 label_counter >= 9 && label_counter <= 12 ?
2877 MICROLABEL_LEVEL_AUTHOR_HEAD :
2878 label_counter >= 14 && label_counter <= 21 ?
2879 MICROLABEL_LEVEL_AUTHOR :
2880 label_counter >= 23 && label_counter <= 26 ?
2881 MICROLABEL_IMPORTED_FROM_HEAD :
2882 label_counter >= 28 && label_counter <= 35 ?
2883 MICROLABEL_IMPORTED_FROM :
2884 label_counter >= 37 && label_counter <= 40 ?
2885 MICROLABEL_IMPORTED_BY_HEAD :
2886 label_counter >= 42 && label_counter <= 49 ?
2887 MICROLABEL_IMPORTED_BY : MICROLABEL_EMPTY);
2889 if (leveldir_current->imported_from == NULL &&
2890 (label_state == MICROLABEL_IMPORTED_FROM_HEAD ||
2891 label_state == MICROLABEL_IMPORTED_FROM))
2892 label_state = (label_state == MICROLABEL_IMPORTED_FROM_HEAD ?
2893 MICROLABEL_IMPORTED_BY_HEAD : MICROLABEL_IMPORTED_BY);
2895 DrawPreviewLevelLabelExt(label_state);
2898 game_status = last_game_status; /* restore current game status */
2901 void DrawPreviewLevelInitial()
2903 DrawPreviewLevelExt(TRUE);
2906 void DrawPreviewLevelAnimation()
2908 DrawPreviewLevelExt(FALSE);
2911 inline static void DrawGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2912 int graphic, int sync_frame,
2915 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2917 if (mask_mode == USE_MASKING)
2918 DrawGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2920 DrawGraphicExt(dst_bitmap, x, y, graphic, frame);
2923 void DrawFixedGraphicAnimationExt(DrawBuffer *dst_bitmap, int x, int y,
2924 int graphic, int sync_frame, int mask_mode)
2926 int frame = getGraphicAnimationFrame(graphic, sync_frame);
2928 if (mask_mode == USE_MASKING)
2929 DrawFixedGraphicThruMaskExt(dst_bitmap, x, y, graphic, frame);
2931 DrawFixedGraphicExt(dst_bitmap, x, y, graphic, frame);
2934 inline static void DrawGraphicAnimation(int x, int y, int graphic)
2936 int lx = LEVELX(x), ly = LEVELY(y);
2938 if (!IN_SCR_FIELD(x, y))
2941 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX_VAR, FY + y * TILEY_VAR,
2942 graphic, GfxFrame[lx][ly], NO_MASKING);
2944 MarkTileDirty(x, y);
2947 void DrawFixedGraphicAnimation(int x, int y, int graphic)
2949 int lx = LEVELX(x), ly = LEVELY(y);
2951 if (!IN_SCR_FIELD(x, y))
2954 DrawGraphicAnimationExt(drawto_field, FX + x * TILEX, FY + y * TILEY,
2955 graphic, GfxFrame[lx][ly], NO_MASKING);
2956 MarkTileDirty(x, y);
2959 void DrawLevelGraphicAnimation(int x, int y, int graphic)
2961 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2964 void DrawLevelElementAnimation(int x, int y, int element)
2966 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
2968 DrawGraphicAnimation(SCREENX(x), SCREENY(y), graphic);
2971 void DrawLevelGraphicAnimationIfNeeded(int x, int y, int graphic)
2973 int sx = SCREENX(x), sy = SCREENY(y);
2975 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
2978 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
2981 DrawGraphicAnimation(sx, sy, graphic);
2984 if (GFX_CRUMBLED(TILE_GFX_ELEMENT(x, y)))
2985 DrawLevelFieldCrumbled(x, y);
2987 if (GFX_CRUMBLED(Feld[x][y]))
2988 DrawLevelFieldCrumbled(x, y);
2992 void DrawLevelElementAnimationIfNeeded(int x, int y, int element)
2994 int sx = SCREENX(x), sy = SCREENY(y);
2997 if (!IN_LEV_FIELD(x, y) || !IN_SCR_FIELD(sx, sy))
3000 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
3002 if (!IS_NEW_FRAME(GfxFrame[x][y], graphic))
3005 DrawGraphicAnimation(sx, sy, graphic);
3007 if (GFX_CRUMBLED(element))
3008 DrawLevelFieldCrumbled(x, y);
3011 static int getPlayerGraphic(struct PlayerInfo *player, int move_dir)
3013 if (player->use_murphy)
3015 /* this works only because currently only one player can be "murphy" ... */
3016 static int last_horizontal_dir = MV_LEFT;
3017 int graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, move_dir);
3019 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3020 last_horizontal_dir = move_dir;
3022 if (graphic == IMG_SP_MURPHY) /* undefined => use special graphic */
3024 int direction = (player->is_snapping ? move_dir : last_horizontal_dir);
3026 graphic = el_act_dir2img(EL_SP_MURPHY, player->GfxAction, direction);
3032 return el_act_dir2img(player->artwork_element, player->GfxAction,move_dir);
3035 static boolean equalGraphics(int graphic1, int graphic2)
3037 struct GraphicInfo *g1 = &graphic_info[graphic1];
3038 struct GraphicInfo *g2 = &graphic_info[graphic2];
3040 return (g1->bitmap == g2->bitmap &&
3041 g1->src_x == g2->src_x &&
3042 g1->src_y == g2->src_y &&
3043 g1->anim_frames == g2->anim_frames &&
3044 g1->anim_delay == g2->anim_delay &&
3045 g1->anim_mode == g2->anim_mode);
3048 void DrawAllPlayers()
3052 for (i = 0; i < MAX_PLAYERS; i++)
3053 if (stored_player[i].active)
3054 DrawPlayer(&stored_player[i]);
3057 void DrawPlayerField(int x, int y)
3059 if (!IS_PLAYER(x, y))
3062 DrawPlayer(PLAYERINFO(x, y));
3065 #define DRAW_PLAYER_OVER_PUSHED_ELEMENT 1
3067 void DrawPlayer(struct PlayerInfo *player)
3069 int jx = player->jx;
3070 int jy = player->jy;
3071 int move_dir = player->MovDir;
3072 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? +1 : 0);
3073 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? +1 : 0);
3074 int last_jx = (player->is_moving ? jx - dx : jx);
3075 int last_jy = (player->is_moving ? jy - dy : jy);
3076 int next_jx = jx + dx;
3077 int next_jy = jy + dy;
3078 boolean player_is_moving = (player->MovPos ? TRUE : FALSE);
3079 boolean player_is_opaque = FALSE;
3080 int sx = SCREENX(jx), sy = SCREENY(jy);
3081 int sxx = 0, syy = 0;
3082 int element = Feld[jx][jy], last_element = Feld[last_jx][last_jy];
3084 int action = ACTION_DEFAULT;
3085 int last_player_graphic = getPlayerGraphic(player, move_dir);
3086 int last_player_frame = player->Frame;
3089 /* GfxElement[][] is set to the element the player is digging or collecting;
3090 remove also for off-screen player if the player is not moving anymore */
3091 if (IN_LEV_FIELD(jx, jy) && !player_is_moving)
3092 GfxElement[jx][jy] = EL_UNDEFINED;
3094 if (!player->active || !IN_SCR_FIELD(SCREENX(last_jx), SCREENY(last_jy)))
3098 if (!IN_LEV_FIELD(jx, jy))
3100 printf("DrawPlayerField(): x = %d, y = %d\n",jx,jy);
3101 printf("DrawPlayerField(): sx = %d, sy = %d\n",sx,sy);
3102 printf("DrawPlayerField(): This should never happen!\n");
3107 if (element == EL_EXPLOSION)
3110 action = (player->is_pushing ? ACTION_PUSHING :
3111 player->is_digging ? ACTION_DIGGING :
3112 player->is_collecting ? ACTION_COLLECTING :
3113 player->is_moving ? ACTION_MOVING :
3114 player->is_snapping ? ACTION_SNAPPING :
3115 player->is_dropping ? ACTION_DROPPING :
3116 player->is_waiting ? player->action_waiting : ACTION_DEFAULT);
3118 if (player->is_waiting)
3119 move_dir = player->dir_waiting;
3121 InitPlayerGfxAnimation(player, action, move_dir);
3123 /* ----------------------------------------------------------------------- */
3124 /* draw things in the field the player is leaving, if needed */
3125 /* ----------------------------------------------------------------------- */
3127 if (player->is_moving)
3129 if (Back[last_jx][last_jy] && IS_DRAWABLE(last_element))
3131 DrawLevelElement(last_jx, last_jy, Back[last_jx][last_jy]);
3133 if (last_element == EL_DYNAMITE_ACTIVE ||
3134 last_element == EL_EM_DYNAMITE_ACTIVE ||
3135 last_element == EL_SP_DISK_RED_ACTIVE)
3136 DrawDynamite(last_jx, last_jy);
3138 DrawLevelFieldThruMask(last_jx, last_jy);
3140 else if (last_element == EL_DYNAMITE_ACTIVE ||
3141 last_element == EL_EM_DYNAMITE_ACTIVE ||
3142 last_element == EL_SP_DISK_RED_ACTIVE)
3143 DrawDynamite(last_jx, last_jy);
3145 /* !!! this is not enough to prevent flickering of players which are
3146 moving next to each others without a free tile between them -- this
3147 can only be solved by drawing all players layer by layer (first the
3148 background, then the foreground etc.) !!! => TODO */
3149 else if (!IS_PLAYER(last_jx, last_jy))
3150 DrawLevelField(last_jx, last_jy);
3153 DrawLevelField(last_jx, last_jy);
3156 if (player->is_pushing && IN_SCR_FIELD(SCREENX(next_jx), SCREENY(next_jy)))
3157 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3160 if (!IN_SCR_FIELD(sx, sy))
3163 /* ----------------------------------------------------------------------- */
3164 /* draw things behind the player, if needed */
3165 /* ----------------------------------------------------------------------- */
3168 DrawLevelElement(jx, jy, Back[jx][jy]);
3169 else if (IS_ACTIVE_BOMB(element))
3170 DrawLevelElement(jx, jy, EL_EMPTY);
3173 if (player_is_moving && GfxElement[jx][jy] != EL_UNDEFINED)
3175 int old_element = GfxElement[jx][jy];
3176 int old_graphic = el_act_dir2img(old_element, action, move_dir);
3177 int frame = getGraphicAnimationFrame(old_graphic, player->StepFrame);
3179 if (GFX_CRUMBLED(old_element))
3180 DrawLevelFieldCrumbledDigging(jx, jy, move_dir, player->StepFrame);
3182 DrawGraphic(sx, sy, old_graphic, frame);
3184 if (graphic_info[old_graphic].anim_mode & ANIM_OPAQUE_PLAYER)
3185 player_is_opaque = TRUE;
3189 GfxElement[jx][jy] = EL_UNDEFINED;
3191 /* make sure that pushed elements are drawn with correct frame rate */
3192 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3194 if (player->is_pushing && player->is_moving && !IS_ANIM_MODE_CE(graphic))
3195 GfxFrame[jx][jy] = player->StepFrame;
3197 DrawLevelField(jx, jy);
3201 #if !DRAW_PLAYER_OVER_PUSHED_ELEMENT
3202 /* ----------------------------------------------------------------------- */
3203 /* draw player himself */
3204 /* ----------------------------------------------------------------------- */
3206 graphic = getPlayerGraphic(player, move_dir);
3208 /* in the case of changed player action or direction, prevent the current
3209 animation frame from being restarted for identical animations */
3210 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3211 player->Frame = last_player_frame;
3213 frame = getGraphicAnimationFrame(graphic, player->Frame);
3217 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3218 sxx = player->GfxPos;
3220 syy = player->GfxPos;
3223 if (player_is_opaque)
3224 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3226 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3228 if (SHIELD_ON(player))
3230 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3231 IMG_SHIELD_NORMAL_ACTIVE);
3232 int frame = getGraphicAnimationFrame(graphic, -1);
3234 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3238 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3241 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3242 sxx = player->GfxPos;
3244 syy = player->GfxPos;
3248 /* ----------------------------------------------------------------------- */
3249 /* draw things the player is pushing, if needed */
3250 /* ----------------------------------------------------------------------- */
3252 if (player->is_pushing && player->is_moving)
3254 int px = SCREENX(jx), py = SCREENY(jy);
3255 int pxx = (TILEX - ABS(sxx)) * dx;
3256 int pyy = (TILEY - ABS(syy)) * dy;
3257 int gfx_frame = GfxFrame[jx][jy];
3263 if (!IS_MOVING(jx, jy)) /* push movement already finished */
3265 element = Feld[next_jx][next_jy];
3266 gfx_frame = GfxFrame[next_jx][next_jy];
3269 graphic = el_act_dir2img(element, ACTION_PUSHING, move_dir);
3271 sync_frame = (IS_ANIM_MODE_CE(graphic) ? gfx_frame : player->StepFrame);
3272 frame = getGraphicAnimationFrame(graphic, sync_frame);
3274 /* draw background element under pushed element (like the Sokoban field) */
3275 if (game.use_masked_pushing && IS_MOVING(jx, jy))
3277 /* this allows transparent pushing animation over non-black background */
3280 DrawLevelElement(jx, jy, Back[jx][jy]);
3282 DrawLevelElement(jx, jy, EL_EMPTY);
3284 if (Back[next_jx][next_jy])
3285 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3287 DrawLevelElement(next_jx, next_jy, EL_EMPTY);
3289 else if (Back[next_jx][next_jy])
3290 DrawLevelElement(next_jx, next_jy, Back[next_jx][next_jy]);
3293 /* do not draw (EM style) pushing animation when pushing is finished */
3294 /* (two-tile animations usually do not contain start and end frame) */
3295 if (graphic_info[graphic].double_movement && !IS_MOVING(jx, jy))
3296 DrawLevelElement(next_jx, next_jy, Feld[next_jx][next_jy]);
3298 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3300 /* masked drawing is needed for EMC style (double) movement graphics */
3301 /* !!! (ONLY WHEN DRAWING PUSHED ELEMENT OVER THE PLAYER) !!! */
3302 DrawGraphicShiftedThruMask(px, py, pxx, pyy, graphic, frame, NO_CUTTING);
3306 #if DRAW_PLAYER_OVER_PUSHED_ELEMENT
3307 /* ----------------------------------------------------------------------- */
3308 /* draw player himself */
3309 /* ----------------------------------------------------------------------- */
3311 graphic = getPlayerGraphic(player, move_dir);
3313 /* in the case of changed player action or direction, prevent the current
3314 animation frame from being restarted for identical animations */
3315 if (player->Frame == 0 && equalGraphics(graphic, last_player_graphic))
3316 player->Frame = last_player_frame;
3318 frame = getGraphicAnimationFrame(graphic, player->Frame);
3322 if (move_dir == MV_LEFT || move_dir == MV_RIGHT)
3323 sxx = player->GfxPos;
3325 syy = player->GfxPos;
3328 if (player_is_opaque)
3329 DrawGraphicShifted(sx, sy, sxx, syy, graphic, frame,NO_CUTTING,NO_MASKING);
3331 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3333 if (SHIELD_ON(player))
3335 int graphic = (player->shield_deadly_time_left ? IMG_SHIELD_DEADLY_ACTIVE :
3336 IMG_SHIELD_NORMAL_ACTIVE);
3337 int frame = getGraphicAnimationFrame(graphic, -1);
3339 DrawGraphicShiftedThruMask(sx, sy, sxx, syy, graphic, frame, NO_CUTTING);
3343 /* ----------------------------------------------------------------------- */
3344 /* draw things in front of player (active dynamite or dynabombs) */
3345 /* ----------------------------------------------------------------------- */
3347 if (IS_ACTIVE_BOMB(element))
3349 graphic = el2img(element);
3350 frame = getGraphicAnimationFrame(graphic, GfxFrame[jx][jy]);
3352 if (game.emulation == EMU_SUPAPLEX)
3353 DrawGraphic(sx, sy, IMG_SP_DISK_RED, frame);
3355 DrawGraphicThruMask(sx, sy, graphic, frame);
3358 if (player_is_moving && last_element == EL_EXPLOSION)
3360 int element = (GfxElement[last_jx][last_jy] != EL_UNDEFINED ?
3361 GfxElement[last_jx][last_jy] : EL_EMPTY);
3362 int graphic = el_act2img(element, ACTION_EXPLODING);
3363 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
3364 int phase = ExplodePhase[last_jx][last_jy] - 1;
3365 int frame = getGraphicAnimationFrame(graphic, phase - delay);
3368 DrawGraphicThruMask(SCREENX(last_jx), SCREENY(last_jy), graphic, frame);
3371 /* ----------------------------------------------------------------------- */
3372 /* draw elements the player is just walking/passing through/under */
3373 /* ----------------------------------------------------------------------- */
3375 if (player_is_moving)
3377 /* handle the field the player is leaving ... */
3378 if (IS_ACCESSIBLE_INSIDE(last_element))
3379 DrawLevelField(last_jx, last_jy);
3380 else if (IS_ACCESSIBLE_UNDER(last_element))
3381 DrawLevelFieldThruMask(last_jx, last_jy);
3384 /* do not redraw accessible elements if the player is just pushing them */
3385 if (!player_is_moving || !player->is_pushing)
3387 /* ... and the field the player is entering */
3388 if (IS_ACCESSIBLE_INSIDE(element))
3389 DrawLevelField(jx, jy);
3390 else if (IS_ACCESSIBLE_UNDER(element))
3391 DrawLevelFieldThruMask(jx, jy);
3394 MarkTileDirty(sx, sy);
3397 /* ------------------------------------------------------------------------- */
3399 void WaitForEventToContinue()
3401 boolean still_wait = TRUE;
3403 /* simulate releasing mouse button over last gadget, if still pressed */
3405 HandleGadgets(-1, -1, 0);
3407 button_status = MB_RELEASED;
3421 case EVENT_BUTTONPRESS:
3422 case EVENT_KEYPRESS:
3426 case EVENT_KEYRELEASE:
3427 ClearPlayerAction();
3431 HandleOtherEvents(&event);
3435 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3442 WaitUntilDelayReached(&sync_frame_delay, sync_frame_delay_value);
3446 #define MAX_REQUEST_LINES 13
3447 #define MAX_REQUEST_LINE_FONT1_LEN 7
3448 #define MAX_REQUEST_LINE_FONT2_LEN 10
3450 static int RequestHandleEvents(unsigned int req_state)
3452 boolean level_solved = (game_status == GAME_MODE_PLAYING &&
3453 local_player->LevelSolved_GameEnd);
3454 int width = request.width;
3455 int height = request.height;
3459 setRequestPosition(&sx, &sy, FALSE);
3461 button_status = MB_RELEASED;
3463 request_gadget_id = -1;
3470 SetDrawtoField(DRAW_FIELDBUFFER);
3472 HandleGameActions();
3474 SetDrawtoField(DRAW_BACKBUFFER);
3476 if (global.use_envelope_request)
3478 /* copy current state of request area to middle of playfield area */
3479 BlitBitmap(bitmap_db_cross, drawto, sx, sy, width, height, sx, sy);
3487 while (NextValidEvent(&event))
3491 case EVENT_BUTTONPRESS:
3492 case EVENT_BUTTONRELEASE:
3493 case EVENT_MOTIONNOTIFY:
3497 if (event.type == EVENT_MOTIONNOTIFY)
3502 motion_status = TRUE;
3503 mx = ((MotionEvent *) &event)->x;
3504 my = ((MotionEvent *) &event)->y;
3508 motion_status = FALSE;
3509 mx = ((ButtonEvent *) &event)->x;
3510 my = ((ButtonEvent *) &event)->y;
3511 if (event.type == EVENT_BUTTONPRESS)
3512 button_status = ((ButtonEvent *) &event)->button;
3514 button_status = MB_RELEASED;
3517 /* this sets 'request_gadget_id' */
3518 HandleGadgets(mx, my, button_status);
3520 switch (request_gadget_id)
3522 case TOOL_CTRL_ID_YES:
3525 case TOOL_CTRL_ID_NO:
3528 case TOOL_CTRL_ID_CONFIRM:
3529 result = TRUE | FALSE;
3532 case TOOL_CTRL_ID_PLAYER_1:
3535 case TOOL_CTRL_ID_PLAYER_2:
3538 case TOOL_CTRL_ID_PLAYER_3:
3541 case TOOL_CTRL_ID_PLAYER_4:
3552 case EVENT_KEYPRESS:
3553 switch (GetEventKey((KeyEvent *)&event, TRUE))
3556 if (req_state & REQ_CONFIRM)
3561 #if defined(TARGET_SDL2)
3568 #if defined(TARGET_SDL2)
3578 if (req_state & REQ_PLAYER)
3582 case EVENT_KEYRELEASE:
3583 ClearPlayerAction();
3587 HandleOtherEvents(&event);
3592 else if (AnyJoystickButton() == JOY_BUTTON_NEW_PRESSED)
3594 int joy = AnyJoystick();
3596 if (joy & JOY_BUTTON_1)
3598 else if (joy & JOY_BUTTON_2)
3604 if (global.use_envelope_request)
3606 /* copy back current state of pressed buttons inside request area */
3607 BlitBitmap(drawto, bitmap_db_cross, sx, sy, width, height, sx, sy);
3617 WaitUntilDelayReached(&sync_frame_delay, sync_frame_delay_value);
3623 static boolean RequestDoor(char *text, unsigned int req_state)
3625 unsigned int old_door_state;
3626 int last_game_status = game_status; /* save current game status */
3627 int max_request_line_len = MAX_REQUEST_LINE_FONT1_LEN;
3628 int font_nr = FONT_TEXT_2;
3633 if (maxWordLengthInString(text) > MAX_REQUEST_LINE_FONT1_LEN)
3635 max_request_line_len = MAX_REQUEST_LINE_FONT2_LEN;
3636 font_nr = FONT_TEXT_1;
3639 if (game_status == GAME_MODE_PLAYING)
3640 BlitScreenToBitmap(backbuffer);
3642 /* disable deactivated drawing when quick-loading level tape recording */
3643 if (tape.playing && tape.deactivate_display)
3644 TapeDeactivateDisplayOff(TRUE);
3646 SetMouseCursor(CURSOR_DEFAULT);
3648 #if defined(NETWORK_AVALIABLE)
3649 /* pause network game while waiting for request to answer */
3650 if (options.network &&
3651 game_status == GAME_MODE_PLAYING &&
3652 req_state & REQUEST_WAIT_FOR_INPUT)
3653 SendToServer_PausePlaying();
3656 old_door_state = GetDoorState();
3658 /* simulate releasing mouse button over last gadget, if still pressed */
3660 HandleGadgets(-1, -1, 0);
3664 /* draw released gadget before proceeding */
3667 if (old_door_state & DOOR_OPEN_1)
3669 CloseDoor(DOOR_CLOSE_1);
3671 /* save old door content */
3672 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
3673 0 * DXSIZE, 0, DXSIZE, DYSIZE, 1 * DXSIZE, 0);
3676 SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3677 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3679 /* clear door drawing field */
3680 DrawBackground(DX, DY, DXSIZE, DYSIZE);
3682 /* force DOOR font inside door area */
3683 game_status = GAME_MODE_PSEUDO_DOOR;
3685 /* write text for request */
3686 for (text_ptr = text, ty = 0; ty < MAX_REQUEST_LINES; ty++)
3688 char text_line[max_request_line_len + 1];
3694 for (tl = 0, tx = 0; tx < max_request_line_len; tl++, tx++)
3696 tc = *(text_ptr + tx);
3697 // if (!tc || tc == ' ')
3698 if (!tc || tc == ' ' || tc == '?' || tc == '!')
3702 if ((tc == '?' || tc == '!') && tl == 0)
3712 strncpy(text_line, text_ptr, tl);
3715 DrawText(DX + (DXSIZE - tl * getFontWidth(font_nr)) / 2,
3716 DY + 8 + ty * (getFontHeight(font_nr) + 2),
3717 text_line, font_nr);
3719 text_ptr += tl + (tc == ' ' ? 1 : 0);
3720 // text_ptr += tl + (tc == ' ' || tc == '?' || tc == '!' ? 1 : 0);
3723 game_status = last_game_status; /* restore current game status */
3725 if (req_state & REQ_ASK)
3727 MapGadget(tool_gadget[TOOL_CTRL_ID_YES]);
3728 MapGadget(tool_gadget[TOOL_CTRL_ID_NO]);
3730 else if (req_state & REQ_CONFIRM)
3732 MapGadget(tool_gadget[TOOL_CTRL_ID_CONFIRM]);
3734 else if (req_state & REQ_PLAYER)
3736 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_1]);
3737 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_2]);
3738 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_3]);
3739 MapGadget(tool_gadget[TOOL_CTRL_ID_PLAYER_4]);
3742 /* copy request gadgets to door backbuffer */
3743 BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3745 OpenDoor(DOOR_OPEN_1);
3747 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3749 if (game_status == GAME_MODE_PLAYING)
3751 SetPanelBackground();
3752 SetDrawBackgroundMask(REDRAW_DOOR_1);
3756 SetDrawBackgroundMask(REDRAW_FIELD);
3762 if (game_status != GAME_MODE_MAIN)
3765 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3767 // ---------- handle request buttons ----------
3768 result = RequestHandleEvents(req_state);
3770 if (game_status != GAME_MODE_MAIN)
3775 if (!(req_state & REQ_STAY_OPEN))
3777 CloseDoor(DOOR_CLOSE_1);
3779 if (((old_door_state & DOOR_OPEN_1) && !(req_state & REQ_STAY_CLOSED)) ||
3780 (req_state & REQ_REOPEN))
3781 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
3786 if (game_status == GAME_MODE_PLAYING)
3788 SetPanelBackground();
3789 SetDrawBackgroundMask(REDRAW_DOOR_1);
3793 SetDrawBackgroundMask(REDRAW_FIELD);
3796 #if defined(NETWORK_AVALIABLE)
3797 /* continue network game after request */
3798 if (options.network &&
3799 game_status == GAME_MODE_PLAYING &&
3800 req_state & REQUEST_WAIT_FOR_INPUT)
3801 SendToServer_ContinuePlaying();
3804 /* restore deactivated drawing when quick-loading level tape recording */
3805 if (tape.playing && tape.deactivate_display)
3806 TapeDeactivateDisplayOn();
3811 static boolean RequestEnvelope(char *text, unsigned int req_state)
3815 if (game_status == GAME_MODE_PLAYING)
3816 BlitScreenToBitmap(backbuffer);
3818 /* disable deactivated drawing when quick-loading level tape recording */
3819 if (tape.playing && tape.deactivate_display)
3820 TapeDeactivateDisplayOff(TRUE);
3822 SetMouseCursor(CURSOR_DEFAULT);
3824 #if defined(NETWORK_AVALIABLE)
3825 /* pause network game while waiting for request to answer */
3826 if (options.network &&
3827 game_status == GAME_MODE_PLAYING &&
3828 req_state & REQUEST_WAIT_FOR_INPUT)
3829 SendToServer_PausePlaying();
3832 /* simulate releasing mouse button over last gadget, if still pressed */
3834 HandleGadgets(-1, -1, 0);
3838 // (replace with setting corresponding request background)
3839 // SetDoorBackgroundImage(IMG_BACKGROUND_DOOR);
3840 // SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3842 /* clear door drawing field */
3843 // DrawBackground(DX, DY, DXSIZE, DYSIZE);
3845 ShowEnvelopeRequest(text, req_state, ACTION_OPENING);
3847 if (!(req_state & REQUEST_WAIT_FOR_INPUT))
3849 if (game_status == GAME_MODE_PLAYING)
3851 SetPanelBackground();
3852 SetDrawBackgroundMask(REDRAW_DOOR_1);
3856 SetDrawBackgroundMask(REDRAW_FIELD);
3862 SetDrawBackgroundMask(REDRAW_FIELD | REDRAW_DOOR_1);
3864 // ---------- handle request buttons ----------
3865 result = RequestHandleEvents(req_state);
3867 if (game_status != GAME_MODE_MAIN)
3872 ShowEnvelopeRequest(text, req_state, ACTION_CLOSING);
3876 if (game_status == GAME_MODE_PLAYING)
3878 SetPanelBackground();
3879 SetDrawBackgroundMask(REDRAW_DOOR_1);
3883 SetDrawBackgroundMask(REDRAW_FIELD);
3886 #if defined(NETWORK_AVALIABLE)
3887 /* continue network game after request */
3888 if (options.network &&
3889 game_status == GAME_MODE_PLAYING &&
3890 req_state & REQUEST_WAIT_FOR_INPUT)
3891 SendToServer_ContinuePlaying();
3894 /* restore deactivated drawing when quick-loading level tape recording */
3895 if (tape.playing && tape.deactivate_display)
3896 TapeDeactivateDisplayOn();
3901 boolean Request(char *text, unsigned int req_state)
3903 if (global.use_envelope_request)
3904 return RequestEnvelope(text, req_state);
3906 return RequestDoor(text, req_state);
3909 static int compareDoorPartOrderInfo(const void *object1, const void *object2)
3911 const struct DoorPartOrderInfo *dpo1 = (struct DoorPartOrderInfo *)object1;
3912 const struct DoorPartOrderInfo *dpo2 = (struct DoorPartOrderInfo *)object2;
3915 if (dpo1->sort_priority != dpo2->sort_priority)
3916 compare_result = dpo1->sort_priority - dpo2->sort_priority;
3918 compare_result = dpo1->nr - dpo2->nr;
3920 return compare_result;
3923 void InitGraphicCompatibilityInfo_Doors()
3929 struct DoorInfo *door;
3933 { DOOR_1, IMG_DOOR_1_GFX_PART_1, IMG_DOOR_1_GFX_PART_8, &door_1 },
3934 { DOOR_2, IMG_DOOR_2_GFX_PART_1, IMG_DOOR_2_GFX_PART_8, &door_2 },
3936 { -1, -1, -1, NULL }
3938 struct Rect door_rect_list[] =
3940 { DX, DY, DXSIZE, DYSIZE },
3941 { VX, VY, VXSIZE, VYSIZE }
3945 for (i = 0; doors[i].door_token != -1; i++)
3947 int door_token = doors[i].door_token;
3948 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
3949 int part_1 = doors[i].part_1;
3950 int part_8 = doors[i].part_8;
3951 int part_2 = part_1 + 1;
3952 int part_3 = part_1 + 2;
3953 struct DoorInfo *door = doors[i].door;
3954 struct Rect *door_rect = &door_rect_list[door_index];
3955 boolean door_gfx_redefined = FALSE;
3957 /* check if any door part graphic definitions have been redefined */
3959 for (j = 0; door_part_controls[j].door_token != -1; j++)
3961 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3962 struct FileInfo *fi = getImageListEntryFromImageID(dpc->graphic);
3964 if (dpc->door_token == door_token && fi->redefined)
3965 door_gfx_redefined = TRUE;
3968 /* check for old-style door graphic/animation modifications */
3970 if (!door_gfx_redefined)
3972 if (door->anim_mode & ANIM_STATIC_PANEL)
3974 door->panel.step_xoffset = 0;
3975 door->panel.step_yoffset = 0;
3978 if (door->anim_mode & (ANIM_HORIZONTAL | ANIM_VERTICAL))
3980 struct GraphicInfo *g_part_1 = &graphic_info[part_1];
3981 struct GraphicInfo *g_part_2 = &graphic_info[part_2];
3982 int num_door_steps, num_panel_steps;
3984 /* remove door part graphics other than the two default wings */
3986 for (j = 0; door_part_controls[j].door_token != -1; j++)
3988 struct DoorPartControlInfo *dpc = &door_part_controls[j];
3989 struct GraphicInfo *g = &graphic_info[dpc->graphic];
3991 if (dpc->graphic >= part_3 &&
3992 dpc->graphic <= part_8)
3996 /* set graphics and screen positions of the default wings */
3998 g_part_1->width = door_rect->width;
3999 g_part_1->height = door_rect->height;
4000 g_part_2->width = door_rect->width;
4001 g_part_2->height = door_rect->height;
4002 g_part_2->src_x = door_rect->width;
4003 g_part_2->src_y = g_part_1->src_y;
4005 door->part_2.x = door->part_1.x;
4006 door->part_2.y = door->part_1.y;
4008 if (door->width != -1)
4010 g_part_1->width = door->width;
4011 g_part_2->width = door->width;
4013 // special treatment for graphics and screen position of right wing
4014 g_part_2->src_x += door_rect->width - door->width;
4015 door->part_2.x += door_rect->width - door->width;
4018 if (door->height != -1)
4020 g_part_1->height = door->height;
4021 g_part_2->height = door->height;
4023 // special treatment for graphics and screen position of bottom wing
4024 g_part_2->src_y += door_rect->height - door->height;
4025 door->part_2.y += door_rect->height - door->height;
4028 /* set animation delays for the default wings and panels */
4030 door->part_1.step_delay = door->step_delay;
4031 door->part_2.step_delay = door->step_delay;
4032 door->panel.step_delay = door->step_delay;
4034 /* set animation draw order for the default wings */
4036 door->part_1.sort_priority = 2; /* draw left wing over ... */
4037 door->part_2.sort_priority = 1; /* ... right wing */
4039 /* set animation draw offset for the default wings */
4041 if (door->anim_mode & ANIM_HORIZONTAL)
4043 door->part_1.step_xoffset = door->step_offset;
4044 door->part_1.step_yoffset = 0;
4045 door->part_2.step_xoffset = door->step_offset * -1;
4046 door->part_2.step_yoffset = 0;
4048 num_door_steps = g_part_1->width / door->step_offset;
4050 else // ANIM_VERTICAL
4052 door->part_1.step_xoffset = 0;
4053 door->part_1.step_yoffset = door->step_offset;
4054 door->part_2.step_xoffset = 0;
4055 door->part_2.step_yoffset = door->step_offset * -1;
4057 num_door_steps = g_part_1->height / door->step_offset;
4060 /* set animation draw offset for the default panels */
4062 if (door->step_offset > 1)
4064 num_panel_steps = 2 * door_rect->height / door->step_offset;
4065 door->panel.start_step = num_panel_steps - num_door_steps;
4066 door->panel.start_step_closing = door->panel.start_step;
4070 num_panel_steps = door_rect->height / door->step_offset;
4071 door->panel.start_step = num_panel_steps - num_door_steps / 2;
4072 door->panel.start_step_closing = door->panel.start_step;
4073 door->panel.step_delay *= 2;
4084 for (i = 0; door_part_controls[i].door_token != -1; i++)
4086 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4087 struct DoorPartOrderInfo *dpo = &door_part_order[i];
4089 /* initialize "start_step_opening" and "start_step_closing", if needed */
4090 if (dpc->pos->start_step_opening == 0 &&
4091 dpc->pos->start_step_closing == 0)
4093 // dpc->pos->start_step_opening = dpc->pos->start_step;
4094 dpc->pos->start_step_closing = dpc->pos->start_step;
4097 /* fill structure for door part draw order (sorted below) */
4099 dpo->sort_priority = dpc->pos->sort_priority;
4102 /* sort door part controls according to sort_priority and graphic number */
4103 qsort(door_part_order, MAX_DOOR_PARTS,
4104 sizeof(struct DoorPartOrderInfo), compareDoorPartOrderInfo);
4107 unsigned int OpenDoor(unsigned int door_state)
4109 if (door_state & DOOR_COPY_BACK)
4111 if (door_state & DOOR_OPEN_1)
4112 BlitBitmap(bitmap_db_door_1, bitmap_db_door_1,
4113 1 * DXSIZE, 0, DXSIZE, DYSIZE, 0 * DXSIZE, 0);
4115 if (door_state & DOOR_OPEN_2)
4116 BlitBitmap(bitmap_db_door_2, bitmap_db_door_2,
4117 1 * VXSIZE, 0, VXSIZE, VYSIZE, 0 * VXSIZE, 0);
4119 door_state &= ~DOOR_COPY_BACK;
4122 return MoveDoor(door_state);
4125 unsigned int CloseDoor(unsigned int door_state)
4127 unsigned int old_door_state = GetDoorState();
4129 if (!(door_state & DOOR_NO_COPY_BACK))
4131 if (old_door_state & DOOR_OPEN_1)
4132 BlitBitmap(backbuffer, bitmap_db_door_1,
4133 DX, DY, DXSIZE, DYSIZE, 0, 0);
4135 if (old_door_state & DOOR_OPEN_2)
4136 BlitBitmap(backbuffer, bitmap_db_door_2,
4137 VX, VY, VXSIZE, VYSIZE, 0, 0);
4139 door_state &= ~DOOR_NO_COPY_BACK;
4142 return MoveDoor(door_state);
4145 unsigned int GetDoorState()
4147 return MoveDoor(DOOR_GET_STATE);
4150 unsigned int SetDoorState(unsigned int door_state)
4152 return MoveDoor(door_state | DOOR_SET_STATE);
4155 int euclid(int a, int b)
4157 return (b ? euclid(b, a % b) : a);
4160 unsigned int MoveDoor(unsigned int door_state)
4162 struct Rect door_rect_list[] =
4164 { DX, DY, DXSIZE, DYSIZE },
4165 { VX, VY, VXSIZE, VYSIZE }
4167 static int door1 = DOOR_OPEN_1;
4168 static int door2 = DOOR_CLOSE_2;
4169 unsigned int door_delay = 0;
4170 unsigned int door_delay_value;
4173 if (door_state == DOOR_GET_STATE)
4174 return (door1 | door2);
4176 if (door_state & DOOR_SET_STATE)
4178 if (door_state & DOOR_ACTION_1)
4179 door1 = door_state & DOOR_ACTION_1;
4180 if (door_state & DOOR_ACTION_2)
4181 door2 = door_state & DOOR_ACTION_2;
4183 return (door1 | door2);
4186 if (!(door_state & DOOR_FORCE_REDRAW))
4188 if (door1 == DOOR_OPEN_1 && door_state & DOOR_OPEN_1)
4189 door_state &= ~DOOR_OPEN_1;
4190 else if (door1 == DOOR_CLOSE_1 && door_state & DOOR_CLOSE_1)
4191 door_state &= ~DOOR_CLOSE_1;
4192 if (door2 == DOOR_OPEN_2 && door_state & DOOR_OPEN_2)
4193 door_state &= ~DOOR_OPEN_2;
4194 else if (door2 == DOOR_CLOSE_2 && door_state & DOOR_CLOSE_2)
4195 door_state &= ~DOOR_CLOSE_2;
4198 if (global.autoplay_leveldir)
4200 door_state |= DOOR_NO_DELAY;
4201 door_state &= ~DOOR_CLOSE_ALL;
4204 if (game_status == GAME_MODE_EDITOR)
4205 door_state |= DOOR_NO_DELAY;
4207 if (door_state & DOOR_ACTION)
4209 boolean door_panel_drawn[NUM_DOORS];
4210 boolean panel_has_doors[NUM_DOORS];
4211 boolean door_part_skip[MAX_DOOR_PARTS];
4212 boolean door_part_done[MAX_DOOR_PARTS];
4213 boolean door_part_done_all;
4214 int num_steps[MAX_DOOR_PARTS];
4215 int max_move_delay = 0; // delay for complete animations of all doors
4216 int max_step_delay = 0; // delay (ms) between two animation frames
4217 int num_move_steps = 0; // number of animation steps for all doors
4218 int max_move_delay_doors_only = 0; // delay for doors only (no panel)
4219 int num_move_steps_doors_only = 0; // steps for doors only (no panel)
4220 int current_move_delay = 0;
4224 for (i = 0; i < NUM_DOORS; i++)
4225 panel_has_doors[i] = FALSE;
4227 for (i = 0; i < MAX_DOOR_PARTS; i++)
4229 struct DoorPartControlInfo *dpc = &door_part_controls[i];
4230 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4231 int door_token = dpc->door_token;
4233 door_part_done[i] = FALSE;
4234 door_part_skip[i] = (!(door_state & door_token) ||
4238 for (i = 0; i < MAX_DOOR_PARTS; i++)
4240 int nr = door_part_order[i].nr;
4241 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4242 struct DoorPartPosInfo *pos = dpc->pos;
4243 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4244 int door_token = dpc->door_token;
4245 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4246 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4247 int step_xoffset = ABS(pos->step_xoffset);
4248 int step_yoffset = ABS(pos->step_yoffset);
4249 int step_delay = pos->step_delay;
4250 int current_door_state = door_state & door_token;
4251 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4252 boolean door_closing = ((current_door_state & DOOR_CLOSE) != 0);
4253 boolean part_opening = (is_panel ? door_closing : door_opening);
4254 int start_step = (part_opening ? pos->start_step_opening :
4255 pos->start_step_closing);
4256 float move_xsize = (step_xoffset ? g->width : 0);
4257 float move_ysize = (step_yoffset ? g->height : 0);
4258 int move_xsteps = (step_xoffset ? ceil(move_xsize / step_xoffset) : 0);
4259 int move_ysteps = (step_yoffset ? ceil(move_ysize / step_yoffset) : 0);
4260 int move_steps = (move_xsteps && move_ysteps ?
4261 MIN(move_xsteps, move_ysteps) :
4262 move_xsteps ? move_xsteps : move_ysteps) - start_step;
4263 int move_delay = move_steps * step_delay;
4265 if (door_part_skip[nr])
4268 max_move_delay = MAX(max_move_delay, move_delay);
4269 max_step_delay = (max_step_delay == 0 ? step_delay :
4270 euclid(max_step_delay, step_delay));
4271 num_steps[nr] = move_steps;
4275 max_move_delay_doors_only = MAX(max_move_delay_doors_only, move_delay);
4277 panel_has_doors[door_index] = TRUE;
4281 max_step_delay = MAX(1, max_step_delay); // prevent division by zero
4283 num_move_steps = max_move_delay / max_step_delay;
4284 num_move_steps_doors_only = max_move_delay_doors_only / max_step_delay;
4286 door_delay_value = max_step_delay;
4288 if ((door_state & DOOR_NO_DELAY) || setup.quick_doors)
4290 start = num_move_steps - 1;
4294 /* opening door sound has priority over simultaneously closing door */
4295 if (door_state & (DOOR_OPEN_1 | DOOR_OPEN_2))
4296 PlayMenuSoundStereo(SND_DOOR_OPENING, SOUND_MIDDLE);
4297 else if (door_state & (DOOR_CLOSE_1 | DOOR_CLOSE_2))
4298 PlayMenuSoundStereo(SND_DOOR_CLOSING, SOUND_MIDDLE);
4301 for (k = start; k < num_move_steps; k++)
4303 int last_frame = num_move_steps - 1; // last frame of this "for" loop
4305 door_part_done_all = TRUE;
4307 for (i = 0; i < NUM_DOORS; i++)
4308 door_panel_drawn[i] = FALSE;
4310 for (i = 0; i < MAX_DOOR_PARTS; i++)
4312 int nr = door_part_order[i].nr;
4313 struct DoorPartControlInfo *dpc = &door_part_controls[nr];
4314 struct DoorPartPosInfo *pos = dpc->pos;
4315 struct GraphicInfo *g = &graphic_info[dpc->graphic];
4316 int door_token = dpc->door_token;
4317 int door_index = DOOR_INDEX_FROM_TOKEN(door_token);
4318 boolean is_panel = DOOR_PART_IS_PANEL(nr);
4319 boolean is_panel_and_door_has_closed = FALSE;
4320 struct Rect *door_rect = &door_rect_list[door_index];
4321 Bitmap *bitmap_db_door = (door_token == DOOR_1 ? bitmap_db_door_1 :
4323 Bitmap *bitmap = (is_panel ? bitmap_db_door : g->bitmap);
4324 int current_door_state = door_state & door_token;
4325 boolean door_opening = ((current_door_state & DOOR_OPEN) != 0);
4326 boolean door_closing = !door_opening;
4327 boolean part_opening = (is_panel ? door_closing : door_opening);
4328 boolean part_closing = !part_opening;
4329 int start_step = (part_opening ? pos->start_step_opening :
4330 pos->start_step_closing);
4331 int step_delay = pos->step_delay;
4332 int step_factor = step_delay / max_step_delay;
4333 int k1 = (step_factor ? k / step_factor + 1 : k);
4334 int k2 = (part_opening ? k1 + start_step : num_steps[nr] - k1);
4335 int kk = MAX(0, k2);
4338 int src_x, src_y, src_xx, src_yy;
4339 int dst_x, dst_y, dst_xx, dst_yy;
4342 if (door_part_skip[nr])
4345 if (!(door_state & door_token))
4353 int k2_door = (door_opening ? k : num_move_steps_doors_only - k - 1);
4354 int kk_door = MAX(0, k2_door);
4355 int sync_frame = kk_door * door_delay_value;
4356 int frame = getGraphicAnimationFrame(dpc->graphic, sync_frame);
4358 getGraphicSource(dpc->graphic, frame, &bitmap, &g_src_x, &g_src_y);
4363 if (!door_panel_drawn[door_index])
4365 ClearRectangle(drawto, door_rect->x, door_rect->y,
4366 door_rect->width, door_rect->height);
4368 door_panel_drawn[door_index] = TRUE;
4371 // draw opening or closing door parts
4373 if (pos->step_xoffset < 0) // door part on right side
4376 dst_xx = pos->x + ABS(kk * pos->step_xoffset);
4379 if (dst_xx + width > door_rect->width)
4380 width = door_rect->width - dst_xx;
4382 else // door part on left side
4385 dst_xx = pos->x - kk * pos->step_xoffset;
4389 src_xx = ABS(dst_xx);
4393 width = g->width - src_xx;
4395 if (width > door_rect->width)
4396 width = door_rect->width;
4398 // printf("::: k == %d [%d] \n", k, start_step);
4401 if (pos->step_yoffset < 0) // door part on bottom side
4404 dst_yy = pos->y + ABS(kk * pos->step_yoffset);
4407 if (dst_yy + height > door_rect->height)
4408 height = door_rect->height - dst_yy;
4410 else // door part on top side
4413 dst_yy = pos->y - kk * pos->step_yoffset;
4417 src_yy = ABS(dst_yy);
4421 height = g->height - src_yy;
4424 src_x = g_src_x + src_xx;
4425 src_y = g_src_y + src_yy;
4427 dst_x = door_rect->x + dst_xx;
4428 dst_y = door_rect->y + dst_yy;
4430 is_panel_and_door_has_closed =
4433 panel_has_doors[door_index] &&
4434 k >= num_move_steps_doors_only - 1);
4436 if (width >= 0 && width <= g->width &&
4437 height >= 0 && height <= g->height &&
4438 !is_panel_and_door_has_closed)
4440 if (is_panel || !pos->draw_masked)
4441 BlitBitmap(bitmap, drawto, src_x, src_y, width, height,
4444 BlitBitmapMasked(bitmap, drawto, src_x, src_y, width, height,
4448 redraw_mask |= REDRAW_DOOR_FROM_TOKEN(door_token);
4450 if ((part_opening && (width < 0 || height < 0)) ||
4451 (part_closing && (width >= g->width && height >= g->height)))
4452 door_part_done[nr] = TRUE;
4454 // continue door part animations, but not panel after door has closed
4455 if (!door_part_done[nr] && !is_panel_and_door_has_closed)
4456 door_part_done_all = FALSE;
4459 if (!(door_state & DOOR_NO_DELAY))
4463 if (game_status == GAME_MODE_MAIN)
4466 SkipUntilDelayReached(&door_delay, door_delay_value, &k, last_frame);
4468 current_move_delay += max_step_delay;
4471 if (door_part_done_all)
4476 if (door_state & DOOR_ACTION_1)
4477 door1 = door_state & DOOR_ACTION_1;
4478 if (door_state & DOOR_ACTION_2)
4479 door2 = door_state & DOOR_ACTION_2;
4481 // draw masked border over door area
4482 DrawMaskedBorder(REDRAW_DOOR_1);
4483 DrawMaskedBorder(REDRAW_DOOR_2);
4485 return (door1 | door2);
4488 static boolean useSpecialEditorDoor()
4490 int graphic = IMG_GLOBAL_BORDER_EDITOR;
4491 boolean redefined = getImageListEntryFromImageID(graphic)->redefined;
4493 // do not draw special editor door if editor border defined or redefined
4494 if (graphic_info[graphic].bitmap != NULL || redefined)
4497 // do not draw special editor door if global border defined to be empty
4498 if (graphic_info[IMG_GLOBAL_BORDER].bitmap == NULL)
4501 // do not draw special editor door if viewport definitions do not match
4505 EY + EYSIZE != VY + VYSIZE)
4511 void DrawSpecialEditorDoor()
4513 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4514 int top_border_width = gfx1->width;
4515 int top_border_height = gfx1->height;
4516 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4517 int ex = EX - outer_border;
4518 int ey = EY - outer_border;
4519 int vy = VY - outer_border;
4520 int exsize = EXSIZE + 2 * outer_border;
4522 if (!useSpecialEditorDoor())
4525 /* draw bigger level editor toolbox window */
4526 BlitBitmap(gfx1->bitmap, drawto, gfx1->src_x, gfx1->src_y,
4527 top_border_width, top_border_height, ex, ey - top_border_height);
4528 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto, ex, vy,
4529 exsize, EYSIZE - VYSIZE + outer_border, ex, ey);
4531 redraw_mask |= REDRAW_ALL;
4534 void UndrawSpecialEditorDoor()
4536 struct GraphicInfo *gfx1 = &graphic_info[IMG_DOOR_2_TOP_BORDER_CORRECTION];
4537 int top_border_width = gfx1->width;
4538 int top_border_height = gfx1->height;
4539 int outer_border = viewport.door_2[GAME_MODE_EDITOR].border_size;
4540 int ex = EX - outer_border;
4541 int ey = EY - outer_border;
4542 int ey_top = ey - top_border_height;
4543 int exsize = EXSIZE + 2 * outer_border;
4544 int eysize = EYSIZE + 2 * outer_border;
4546 if (!useSpecialEditorDoor())
4549 /* draw normal tape recorder window */
4550 if (graphic_info[IMG_GLOBAL_BORDER].bitmap)
4552 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4553 ex, ey_top, top_border_width, top_border_height,
4555 BlitBitmap(graphic_info[IMG_GLOBAL_BORDER].bitmap, drawto,
4556 ex, ey, exsize, eysize, ex, ey);
4560 // if screen background is set to "[NONE]", clear editor toolbox window
4561 ClearRectangle(drawto, ex, ey_top, top_border_width, top_border_height);
4562 ClearRectangle(drawto, ex, ey, exsize, eysize);
4565 redraw_mask |= REDRAW_ALL;
4569 /* ---------- new tool button stuff ---------------------------------------- */
4574 struct TextPosInfo *pos;
4577 } toolbutton_info[NUM_TOOL_BUTTONS] =
4580 IMG_REQUEST_BUTTON_GFX_YES, &request.button.yes,
4581 TOOL_CTRL_ID_YES, "yes"
4584 IMG_REQUEST_BUTTON_GFX_NO, &request.button.no,
4585 TOOL_CTRL_ID_NO, "no"
4588 IMG_REQUEST_BUTTON_GFX_CONFIRM, &request.button.confirm,
4589 TOOL_CTRL_ID_CONFIRM, "confirm"
4592 IMG_REQUEST_BUTTON_GFX_PLAYER_1, &request.button.player_1,
4593 TOOL_CTRL_ID_PLAYER_1, "player 1"
4596 IMG_REQUEST_BUTTON_GFX_PLAYER_2, &request.button.player_2,
4597 TOOL_CTRL_ID_PLAYER_2, "player 2"
4600 IMG_REQUEST_BUTTON_GFX_PLAYER_3, &request.button.player_3,
4601 TOOL_CTRL_ID_PLAYER_3, "player 3"
4604 IMG_REQUEST_BUTTON_GFX_PLAYER_4, &request.button.player_4,
4605 TOOL_CTRL_ID_PLAYER_4, "player 4"
4609 void CreateToolButtons()
4613 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4615 struct GraphicInfo *gfx = &graphic_info[toolbutton_info[i].graphic];
4616 struct TextPosInfo *pos = toolbutton_info[i].pos;
4617 struct GadgetInfo *gi;
4618 Bitmap *deco_bitmap = None;
4619 int deco_x = 0, deco_y = 0, deco_xpos = 0, deco_ypos = 0;
4620 unsigned int event_mask = GD_EVENT_RELEASED;
4623 int gd_x = gfx->src_x;
4624 int gd_y = gfx->src_y;
4625 int gd_xp = gfx->src_x + gfx->pressed_xoffset;
4626 int gd_yp = gfx->src_y + gfx->pressed_yoffset;
4629 if (global.use_envelope_request)
4630 setRequestPosition(&dx, &dy, TRUE);
4632 if (id >= TOOL_CTRL_ID_PLAYER_1 && id <= TOOL_CTRL_ID_PLAYER_4)
4634 int player_nr = id - TOOL_CTRL_ID_PLAYER_1;
4636 getSizedGraphicSource(PLAYER_NR_GFX(IMG_PLAYER_1, player_nr), 0,
4637 pos->size, &deco_bitmap, &deco_x, &deco_y);
4638 deco_xpos = (gfx->width - pos->size) / 2;
4639 deco_ypos = (gfx->height - pos->size) / 2;
4642 gi = CreateGadget(GDI_CUSTOM_ID, id,
4643 GDI_INFO_TEXT, toolbutton_info[i].infotext,
4644 GDI_X, dx + GDI_ACTIVE_POS(pos->x),
4645 GDI_Y, dy + GDI_ACTIVE_POS(pos->y),
4646 GDI_WIDTH, gfx->width,
4647 GDI_HEIGHT, gfx->height,
4648 GDI_TYPE, GD_TYPE_NORMAL_BUTTON,
4649 GDI_STATE, GD_BUTTON_UNPRESSED,
4650 GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
4651 GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
4652 GDI_DECORATION_DESIGN, deco_bitmap, deco_x, deco_y,
4653 GDI_DECORATION_POSITION, deco_xpos, deco_ypos,
4654 GDI_DECORATION_SIZE, pos->size, pos->size,
4655 GDI_DECORATION_SHIFTING, 1, 1,
4656 GDI_DIRECT_DRAW, FALSE,
4657 GDI_EVENT_MASK, event_mask,
4658 GDI_CALLBACK_ACTION, HandleToolButtons,
4662 Error(ERR_EXIT, "cannot create gadget");
4664 tool_gadget[id] = gi;
4668 void FreeToolButtons()
4672 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4673 FreeGadget(tool_gadget[i]);
4676 static void UnmapToolButtons()
4680 for (i = 0; i < NUM_TOOL_BUTTONS; i++)
4681 UnmapGadget(tool_gadget[i]);
4684 static void HandleToolButtons(struct GadgetInfo *gi)
4686 request_gadget_id = gi->custom_id;
4689 static struct Mapping_EM_to_RND_object
4692 boolean is_rnd_to_em_mapping; /* unique mapping EM <-> RND */
4693 boolean is_backside; /* backside of moving element */
4699 em_object_mapping_list[] =
4702 Xblank, TRUE, FALSE,
4706 Yacid_splash_eB, FALSE, FALSE,
4707 EL_ACID_SPLASH_RIGHT, -1, -1
4710 Yacid_splash_wB, FALSE, FALSE,
4711 EL_ACID_SPLASH_LEFT, -1, -1
4714 #ifdef EM_ENGINE_BAD_ROLL
4716 Xstone_force_e, FALSE, FALSE,
4717 EL_ROCK, -1, MV_BIT_RIGHT
4720 Xstone_force_w, FALSE, FALSE,
4721 EL_ROCK, -1, MV_BIT_LEFT
4724 Xnut_force_e, FALSE, FALSE,
4725 EL_NUT, -1, MV_BIT_RIGHT
4728 Xnut_force_w, FALSE, FALSE,
4729 EL_NUT, -1, MV_BIT_LEFT
4732 Xspring_force_e, FALSE, FALSE,
4733 EL_SPRING, -1, MV_BIT_RIGHT
4736 Xspring_force_w, FALSE, FALSE,
4737 EL_SPRING, -1, MV_BIT_LEFT
4740 Xemerald_force_e, FALSE, FALSE,
4741 EL_EMERALD, -1, MV_BIT_RIGHT
4744 Xemerald_force_w, FALSE, FALSE,
4745 EL_EMERALD, -1, MV_BIT_LEFT
4748 Xdiamond_force_e, FALSE, FALSE,
4749 EL_DIAMOND, -1, MV_BIT_RIGHT
4752 Xdiamond_force_w, FALSE, FALSE,
4753 EL_DIAMOND, -1, MV_BIT_LEFT
4756 Xbomb_force_e, FALSE, FALSE,
4757 EL_BOMB, -1, MV_BIT_RIGHT
4760 Xbomb_force_w, FALSE, FALSE,
4761 EL_BOMB, -1, MV_BIT_LEFT
4763 #endif /* EM_ENGINE_BAD_ROLL */
4766 Xstone, TRUE, FALSE,
4770 Xstone_pause, FALSE, FALSE,
4774 Xstone_fall, FALSE, FALSE,
4778 Ystone_s, FALSE, FALSE,
4779 EL_ROCK, ACTION_FALLING, -1
4782 Ystone_sB, FALSE, TRUE,
4783 EL_ROCK, ACTION_FALLING, -1
4786 Ystone_e, FALSE, FALSE,
4787 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4790 Ystone_eB, FALSE, TRUE,
4791 EL_ROCK, ACTION_MOVING, MV_BIT_RIGHT
4794 Ystone_w, FALSE, FALSE,
4795 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4798 Ystone_wB, FALSE, TRUE,
4799 EL_ROCK, ACTION_MOVING, MV_BIT_LEFT
4806 Xnut_pause, FALSE, FALSE,
4810 Xnut_fall, FALSE, FALSE,
4814 Ynut_s, FALSE, FALSE,
4815 EL_NUT, ACTION_FALLING, -1
4818 Ynut_sB, FALSE, TRUE,
4819 EL_NUT, ACTION_FALLING, -1
4822 Ynut_e, FALSE, FALSE,
4823 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4826 Ynut_eB, FALSE, TRUE,
4827 EL_NUT, ACTION_MOVING, MV_BIT_RIGHT
4830 Ynut_w, FALSE, FALSE,
4831 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4834 Ynut_wB, FALSE, TRUE,
4835 EL_NUT, ACTION_MOVING, MV_BIT_LEFT
4838 Xbug_n, TRUE, FALSE,
4842 Xbug_e, TRUE, FALSE,
4843 EL_BUG_RIGHT, -1, -1
4846 Xbug_s, TRUE, FALSE,
4850 Xbug_w, TRUE, FALSE,
4854 Xbug_gon, FALSE, FALSE,
4858 Xbug_goe, FALSE, FALSE,
4859 EL_BUG_RIGHT, -1, -1
4862 Xbug_gos, FALSE, FALSE,
4866 Xbug_gow, FALSE, FALSE,
4870 Ybug_n, FALSE, FALSE,
4871 EL_BUG, ACTION_MOVING, MV_BIT_UP
4874 Ybug_nB, FALSE, TRUE,
4875 EL_BUG, ACTION_MOVING, MV_BIT_UP
4878 Ybug_e, FALSE, FALSE,
4879 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4882 Ybug_eB, FALSE, TRUE,
4883 EL_BUG, ACTION_MOVING, MV_BIT_RIGHT
4886 Ybug_s, FALSE, FALSE,
4887 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4890 Ybug_sB, FALSE, TRUE,
4891 EL_BUG, ACTION_MOVING, MV_BIT_DOWN
4894 Ybug_w, FALSE, FALSE,
4895 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4898 Ybug_wB, FALSE, TRUE,
4899 EL_BUG, ACTION_MOVING, MV_BIT_LEFT
4902 Ybug_w_n, FALSE, FALSE,
4903 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
4906 Ybug_n_e, FALSE, FALSE,
4907 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
4910 Ybug_e_s, FALSE, FALSE,
4911 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
4914 Ybug_s_w, FALSE, FALSE,
4915 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
4918 Ybug_e_n, FALSE, FALSE,
4919 EL_BUG, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
4922 Ybug_s_e, FALSE, FALSE,
4923 EL_BUG, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
4926 Ybug_w_s, FALSE, FALSE,
4927 EL_BUG, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
4930 Ybug_n_w, FALSE, FALSE,
4931 EL_BUG, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
4934 Ybug_stone, FALSE, FALSE,
4935 EL_BUG, ACTION_SMASHED_BY_ROCK, -1
4938 Ybug_spring, FALSE, FALSE,
4939 EL_BUG, ACTION_SMASHED_BY_SPRING, -1
4942 Xtank_n, TRUE, FALSE,
4943 EL_SPACESHIP_UP, -1, -1
4946 Xtank_e, TRUE, FALSE,
4947 EL_SPACESHIP_RIGHT, -1, -1
4950 Xtank_s, TRUE, FALSE,
4951 EL_SPACESHIP_DOWN, -1, -1
4954 Xtank_w, TRUE, FALSE,
4955 EL_SPACESHIP_LEFT, -1, -1
4958 Xtank_gon, FALSE, FALSE,
4959 EL_SPACESHIP_UP, -1, -1
4962 Xtank_goe, FALSE, FALSE,
4963 EL_SPACESHIP_RIGHT, -1, -1
4966 Xtank_gos, FALSE, FALSE,
4967 EL_SPACESHIP_DOWN, -1, -1
4970 Xtank_gow, FALSE, FALSE,
4971 EL_SPACESHIP_LEFT, -1, -1
4974 Ytank_n, FALSE, FALSE,
4975 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4978 Ytank_nB, FALSE, TRUE,
4979 EL_SPACESHIP, ACTION_MOVING, MV_BIT_UP
4982 Ytank_e, FALSE, FALSE,
4983 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4986 Ytank_eB, FALSE, TRUE,
4987 EL_SPACESHIP, ACTION_MOVING, MV_BIT_RIGHT
4990 Ytank_s, FALSE, FALSE,
4991 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4994 Ytank_sB, FALSE, TRUE,
4995 EL_SPACESHIP, ACTION_MOVING, MV_BIT_DOWN
4998 Ytank_w, FALSE, FALSE,
4999 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5002 Ytank_wB, FALSE, TRUE,
5003 EL_SPACESHIP, ACTION_MOVING, MV_BIT_LEFT
5006 Ytank_w_n, FALSE, FALSE,
5007 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_UP
5010 Ytank_n_e, FALSE, FALSE,
5011 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_RIGHT
5014 Ytank_e_s, FALSE, FALSE,
5015 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_DOWN
5018 Ytank_s_w, FALSE, FALSE,
5019 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_LEFT
5022 Ytank_e_n, FALSE, FALSE,
5023 EL_SPACESHIP, ACTION_TURNING_FROM_RIGHT, MV_BIT_UP
5026 Ytank_s_e, FALSE, FALSE,
5027 EL_SPACESHIP, ACTION_TURNING_FROM_DOWN, MV_BIT_RIGHT
5030 Ytank_w_s, FALSE, FALSE,
5031 EL_SPACESHIP, ACTION_TURNING_FROM_LEFT, MV_BIT_DOWN
5034 Ytank_n_w, FALSE, FALSE,
5035 EL_SPACESHIP, ACTION_TURNING_FROM_UP, MV_BIT_LEFT
5038 Ytank_stone, FALSE, FALSE,
5039 EL_SPACESHIP, ACTION_SMASHED_BY_ROCK, -1
5042 Ytank_spring, FALSE, FALSE,
5043 EL_SPACESHIP, ACTION_SMASHED_BY_SPRING, -1
5046 Xandroid, TRUE, FALSE,
5047 EL_EMC_ANDROID, ACTION_ACTIVE, -1
5050 Xandroid_1_n, FALSE, FALSE,
5051 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5054 Xandroid_2_n, FALSE, FALSE,
5055 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_UP
5058 Xandroid_1_e, FALSE, FALSE,
5059 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5062 Xandroid_2_e, FALSE, FALSE,
5063 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_RIGHT
5066 Xandroid_1_w, FALSE, FALSE,
5067 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5070 Xandroid_2_w, FALSE, FALSE,
5071 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_LEFT
5074 Xandroid_1_s, FALSE, FALSE,
5075 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5078 Xandroid_2_s, FALSE, FALSE,
5079 EL_EMC_ANDROID, ACTION_ACTIVE, MV_BIT_DOWN
5082 Yandroid_n, FALSE, FALSE,
5083 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5086 Yandroid_nB, FALSE, TRUE,
5087 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_UP
5090 Yandroid_ne, FALSE, FALSE,
5091 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPRIGHT
5094 Yandroid_neB, FALSE, TRUE,
5095 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPRIGHT
5098 Yandroid_e, FALSE, FALSE,
5099 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5102 Yandroid_eB, FALSE, TRUE,
5103 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_RIGHT
5106 Yandroid_se, FALSE, FALSE,
5107 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNRIGHT
5110 Yandroid_seB, FALSE, TRUE,
5111 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNRIGHT
5114 Yandroid_s, FALSE, FALSE,
5115 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5118 Yandroid_sB, FALSE, TRUE,
5119 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_DOWN
5122 Yandroid_sw, FALSE, FALSE,
5123 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_DOWNLEFT
5126 Yandroid_swB, FALSE, TRUE,
5127 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_DOWNLEFT
5130 Yandroid_w, FALSE, FALSE,
5131 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5134 Yandroid_wB, FALSE, TRUE,
5135 EL_EMC_ANDROID, ACTION_MOVING, MV_BIT_LEFT
5138 Yandroid_nw, FALSE, FALSE,
5139 EL_EMC_ANDROID, ACTION_GROWING, MV_BIT_UPLEFT
5142 Yandroid_nwB, FALSE, TRUE,
5143 EL_EMC_ANDROID, ACTION_SHRINKING, MV_BIT_UPLEFT
5146 Xspring, TRUE, FALSE,
5150 Xspring_pause, FALSE, FALSE,
5154 Xspring_e, FALSE, FALSE,
5158 Xspring_w, FALSE, FALSE,
5162 Xspring_fall, FALSE, FALSE,
5166 Yspring_s, FALSE, FALSE,
5167 EL_SPRING, ACTION_FALLING, -1
5170 Yspring_sB, FALSE, TRUE,
5171 EL_SPRING, ACTION_FALLING, -1
5174 Yspring_e, FALSE, FALSE,
5175 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5178 Yspring_eB, FALSE, TRUE,
5179 EL_SPRING, ACTION_MOVING, MV_BIT_RIGHT
5182 Yspring_w, FALSE, FALSE,
5183 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5186 Yspring_wB, FALSE, TRUE,
5187 EL_SPRING, ACTION_MOVING, MV_BIT_LEFT
5190 Yspring_kill_e, FALSE, FALSE,
5191 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5194 Yspring_kill_eB, FALSE, TRUE,
5195 EL_SPRING, ACTION_EATING, MV_BIT_RIGHT
5198 Yspring_kill_w, FALSE, FALSE,
5199 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5202 Yspring_kill_wB, FALSE, TRUE,
5203 EL_SPRING, ACTION_EATING, MV_BIT_LEFT
5206 Xeater_n, TRUE, FALSE,
5207 EL_YAMYAM_UP, -1, -1
5210 Xeater_e, TRUE, FALSE,
5211 EL_YAMYAM_RIGHT, -1, -1
5214 Xeater_w, TRUE, FALSE,
5215 EL_YAMYAM_LEFT, -1, -1
5218 Xeater_s, TRUE, FALSE,
5219 EL_YAMYAM_DOWN, -1, -1
5222 Yeater_n, FALSE, FALSE,
5223 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5226 Yeater_nB, FALSE, TRUE,
5227 EL_YAMYAM, ACTION_MOVING, MV_BIT_UP
5230 Yeater_e, FALSE, FALSE,
5231 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5234 Yeater_eB, FALSE, TRUE,
5235 EL_YAMYAM, ACTION_MOVING, MV_BIT_RIGHT
5238 Yeater_s, FALSE, FALSE,
5239 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5242 Yeater_sB, FALSE, TRUE,
5243 EL_YAMYAM, ACTION_MOVING, MV_BIT_DOWN
5246 Yeater_w, FALSE, FALSE,
5247 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5250 Yeater_wB, FALSE, TRUE,
5251 EL_YAMYAM, ACTION_MOVING, MV_BIT_LEFT
5254 Yeater_stone, FALSE, FALSE,
5255 EL_YAMYAM, ACTION_SMASHED_BY_ROCK, -1
5258 Yeater_spring, FALSE, FALSE,
5259 EL_YAMYAM, ACTION_SMASHED_BY_SPRING, -1
5262 Xalien, TRUE, FALSE,
5266 Xalien_pause, FALSE, FALSE,
5270 Yalien_n, FALSE, FALSE,
5271 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5274 Yalien_nB, FALSE, TRUE,
5275 EL_ROBOT, ACTION_MOVING, MV_BIT_UP
5278 Yalien_e, FALSE, FALSE,
5279 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5282 Yalien_eB, FALSE, TRUE,
5283 EL_ROBOT, ACTION_MOVING, MV_BIT_RIGHT
5286 Yalien_s, FALSE, FALSE,
5287 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5290 Yalien_sB, FALSE, TRUE,
5291 EL_ROBOT, ACTION_MOVING, MV_BIT_DOWN
5294 Yalien_w, FALSE, FALSE,
5295 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5298 Yalien_wB, FALSE, TRUE,
5299 EL_ROBOT, ACTION_MOVING, MV_BIT_LEFT
5302 Yalien_stone, FALSE, FALSE,
5303 EL_ROBOT, ACTION_SMASHED_BY_ROCK, -1
5306 Yalien_spring, FALSE, FALSE,
5307 EL_ROBOT, ACTION_SMASHED_BY_SPRING, -1
5310 Xemerald, TRUE, FALSE,
5314 Xemerald_pause, FALSE, FALSE,
5318 Xemerald_fall, FALSE, FALSE,
5322 Xemerald_shine, FALSE, FALSE,
5323 EL_EMERALD, ACTION_TWINKLING, -1
5326 Yemerald_s, FALSE, FALSE,
5327 EL_EMERALD, ACTION_FALLING, -1
5330 Yemerald_sB, FALSE, TRUE,
5331 EL_EMERALD, ACTION_FALLING, -1
5334 Yemerald_e, FALSE, FALSE,
5335 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5338 Yemerald_eB, FALSE, TRUE,
5339 EL_EMERALD, ACTION_MOVING, MV_BIT_RIGHT
5342 Yemerald_w, FALSE, FALSE,
5343 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5346 Yemerald_wB, FALSE, TRUE,
5347 EL_EMERALD, ACTION_MOVING, MV_BIT_LEFT
5350 Yemerald_eat, FALSE, FALSE,
5351 EL_EMERALD, ACTION_COLLECTING, -1
5354 Yemerald_stone, FALSE, FALSE,
5355 EL_NUT, ACTION_BREAKING, -1
5358 Xdiamond, TRUE, FALSE,
5362 Xdiamond_pause, FALSE, FALSE,
5366 Xdiamond_fall, FALSE, FALSE,
5370 Xdiamond_shine, FALSE, FALSE,
5371 EL_DIAMOND, ACTION_TWINKLING, -1
5374 Ydiamond_s, FALSE, FALSE,
5375 EL_DIAMOND, ACTION_FALLING, -1
5378 Ydiamond_sB, FALSE, TRUE,
5379 EL_DIAMOND, ACTION_FALLING, -1
5382 Ydiamond_e, FALSE, FALSE,
5383 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5386 Ydiamond_eB, FALSE, TRUE,
5387 EL_DIAMOND, ACTION_MOVING, MV_BIT_RIGHT
5390 Ydiamond_w, FALSE, FALSE,
5391 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5394 Ydiamond_wB, FALSE, TRUE,
5395 EL_DIAMOND, ACTION_MOVING, MV_BIT_LEFT
5398 Ydiamond_eat, FALSE, FALSE,
5399 EL_DIAMOND, ACTION_COLLECTING, -1
5402 Ydiamond_stone, FALSE, FALSE,
5403 EL_DIAMOND, ACTION_SMASHED_BY_ROCK, -1
5406 Xdrip_fall, TRUE, FALSE,
5407 EL_AMOEBA_DROP, -1, -1
5410 Xdrip_stretch, FALSE, FALSE,
5411 EL_AMOEBA_DROP, ACTION_FALLING, -1
5414 Xdrip_stretchB, FALSE, TRUE,
5415 EL_AMOEBA_DROP, ACTION_FALLING, -1
5418 Xdrip_eat, FALSE, FALSE,
5419 EL_AMOEBA_DROP, ACTION_GROWING, -1
5422 Ydrip_s1, FALSE, FALSE,
5423 EL_AMOEBA_DROP, ACTION_FALLING, -1
5426 Ydrip_s1B, FALSE, TRUE,
5427 EL_AMOEBA_DROP, ACTION_FALLING, -1
5430 Ydrip_s2, FALSE, FALSE,
5431 EL_AMOEBA_DROP, ACTION_FALLING, -1
5434 Ydrip_s2B, FALSE, TRUE,
5435 EL_AMOEBA_DROP, ACTION_FALLING, -1
5442 Xbomb_pause, FALSE, FALSE,
5446 Xbomb_fall, FALSE, FALSE,
5450 Ybomb_s, FALSE, FALSE,
5451 EL_BOMB, ACTION_FALLING, -1
5454 Ybomb_sB, FALSE, TRUE,
5455 EL_BOMB, ACTION_FALLING, -1
5458 Ybomb_e, FALSE, FALSE,
5459 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5462 Ybomb_eB, FALSE, TRUE,
5463 EL_BOMB, ACTION_MOVING, MV_BIT_RIGHT
5466 Ybomb_w, FALSE, FALSE,
5467 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5470 Ybomb_wB, FALSE, TRUE,
5471 EL_BOMB, ACTION_MOVING, MV_BIT_LEFT
5474 Ybomb_eat, FALSE, FALSE,
5475 EL_BOMB, ACTION_ACTIVATING, -1
5478 Xballoon, TRUE, FALSE,
5482 Yballoon_n, FALSE, FALSE,
5483 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5486 Yballoon_nB, FALSE, TRUE,
5487 EL_BALLOON, ACTION_MOVING, MV_BIT_UP
5490 Yballoon_e, FALSE, FALSE,
5491 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5494 Yballoon_eB, FALSE, TRUE,
5495 EL_BALLOON, ACTION_MOVING, MV_BIT_RIGHT
5498 Yballoon_s, FALSE, FALSE,
5499 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5502 Yballoon_sB, FALSE, TRUE,
5503 EL_BALLOON, ACTION_MOVING, MV_BIT_DOWN
5506 Yballoon_w, FALSE, FALSE,
5507 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5510 Yballoon_wB, FALSE, TRUE,
5511 EL_BALLOON, ACTION_MOVING, MV_BIT_LEFT
5514 Xgrass, TRUE, FALSE,
5515 EL_EMC_GRASS, -1, -1
5518 Ygrass_nB, FALSE, FALSE,
5519 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_UP
5522 Ygrass_eB, FALSE, FALSE,
5523 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_RIGHT
5526 Ygrass_sB, FALSE, FALSE,
5527 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_DOWN
5530 Ygrass_wB, FALSE, FALSE,
5531 EL_EMC_GRASS, ACTION_DIGGING, MV_BIT_LEFT
5538 Ydirt_nB, FALSE, FALSE,
5539 EL_SAND, ACTION_DIGGING, MV_BIT_UP
5542 Ydirt_eB, FALSE, FALSE,
5543 EL_SAND, ACTION_DIGGING, MV_BIT_RIGHT
5546 Ydirt_sB, FALSE, FALSE,
5547 EL_SAND, ACTION_DIGGING, MV_BIT_DOWN
5550 Ydirt_wB, FALSE, FALSE,
5551 EL_SAND, ACTION_DIGGING, MV_BIT_LEFT
5554 Xacid_ne, TRUE, FALSE,
5555 EL_ACID_POOL_TOPRIGHT, -1, -1
5558 Xacid_se, TRUE, FALSE,
5559 EL_ACID_POOL_BOTTOMRIGHT, -1, -1
5562 Xacid_s, TRUE, FALSE,
5563 EL_ACID_POOL_BOTTOM, -1, -1
5566 Xacid_sw, TRUE, FALSE,
5567 EL_ACID_POOL_BOTTOMLEFT, -1, -1
5570 Xacid_nw, TRUE, FALSE,
5571 EL_ACID_POOL_TOPLEFT, -1, -1
5574 Xacid_1, TRUE, FALSE,
5578 Xacid_2, FALSE, FALSE,
5582 Xacid_3, FALSE, FALSE,
5586 Xacid_4, FALSE, FALSE,
5590 Xacid_5, FALSE, FALSE,
5594 Xacid_6, FALSE, FALSE,
5598 Xacid_7, FALSE, FALSE,
5602 Xacid_8, FALSE, FALSE,
5606 Xball_1, TRUE, FALSE,
5607 EL_EMC_MAGIC_BALL, -1, -1
5610 Xball_1B, FALSE, FALSE,
5611 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5614 Xball_2, FALSE, FALSE,
5615 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5618 Xball_2B, FALSE, FALSE,
5619 EL_EMC_MAGIC_BALL, ACTION_ACTIVE, -1
5622 Yball_eat, FALSE, FALSE,
5623 EL_EMC_MAGIC_BALL, ACTION_DROPPING, -1
5626 Ykey_1_eat, FALSE, FALSE,
5627 EL_EM_KEY_1, ACTION_COLLECTING, -1
5630 Ykey_2_eat, FALSE, FALSE,
5631 EL_EM_KEY_2, ACTION_COLLECTING, -1
5634 Ykey_3_eat, FALSE, FALSE,
5635 EL_EM_KEY_3, ACTION_COLLECTING, -1
5638 Ykey_4_eat, FALSE, FALSE,
5639 EL_EM_KEY_4, ACTION_COLLECTING, -1
5642 Ykey_5_eat, FALSE, FALSE,
5643 EL_EMC_KEY_5, ACTION_COLLECTING, -1
5646 Ykey_6_eat, FALSE, FALSE,
5647 EL_EMC_KEY_6, ACTION_COLLECTING, -1
5650 Ykey_7_eat, FALSE, FALSE,
5651 EL_EMC_KEY_7, ACTION_COLLECTING, -1
5654 Ykey_8_eat, FALSE, FALSE,
5655 EL_EMC_KEY_8, ACTION_COLLECTING, -1
5658 Ylenses_eat, FALSE, FALSE,
5659 EL_EMC_LENSES, ACTION_COLLECTING, -1
5662 Ymagnify_eat, FALSE, FALSE,
5663 EL_EMC_MAGNIFIER, ACTION_COLLECTING, -1
5666 Ygrass_eat, FALSE, FALSE,
5667 EL_EMC_GRASS, ACTION_SNAPPING, -1
5670 Ydirt_eat, FALSE, FALSE,
5671 EL_SAND, ACTION_SNAPPING, -1
5674 Xgrow_ns, TRUE, FALSE,
5675 EL_EXPANDABLE_WALL_VERTICAL, -1, -1
5678 Ygrow_ns_eat, FALSE, FALSE,
5679 EL_EXPANDABLE_WALL_VERTICAL, ACTION_GROWING, -1
5682 Xgrow_ew, TRUE, FALSE,
5683 EL_EXPANDABLE_WALL_HORIZONTAL, -1, -1
5686 Ygrow_ew_eat, FALSE, FALSE,
5687 EL_EXPANDABLE_WALL_HORIZONTAL, ACTION_GROWING, -1
5690 Xwonderwall, TRUE, FALSE,
5691 EL_MAGIC_WALL, -1, -1
5694 XwonderwallB, FALSE, FALSE,
5695 EL_MAGIC_WALL, ACTION_ACTIVE, -1
5698 Xamoeba_1, TRUE, FALSE,
5699 EL_AMOEBA_DRY, ACTION_OTHER, -1
5702 Xamoeba_2, FALSE, FALSE,
5703 EL_AMOEBA_DRY, ACTION_OTHER, -1
5706 Xamoeba_3, FALSE, FALSE,
5707 EL_AMOEBA_DRY, ACTION_OTHER, -1
5710 Xamoeba_4, FALSE, FALSE,
5711 EL_AMOEBA_DRY, ACTION_OTHER, -1
5714 Xamoeba_5, TRUE, FALSE,
5715 EL_AMOEBA_WET, ACTION_OTHER, -1
5718 Xamoeba_6, FALSE, FALSE,
5719 EL_AMOEBA_WET, ACTION_OTHER, -1
5722 Xamoeba_7, FALSE, FALSE,
5723 EL_AMOEBA_WET, ACTION_OTHER, -1
5726 Xamoeba_8, FALSE, FALSE,
5727 EL_AMOEBA_WET, ACTION_OTHER, -1
5730 Xdoor_1, TRUE, FALSE,
5731 EL_EM_GATE_1, -1, -1
5734 Xdoor_2, TRUE, FALSE,
5735 EL_EM_GATE_2, -1, -1
5738 Xdoor_3, TRUE, FALSE,
5739 EL_EM_GATE_3, -1, -1
5742 Xdoor_4, TRUE, FALSE,
5743 EL_EM_GATE_4, -1, -1
5746 Xdoor_5, TRUE, FALSE,
5747 EL_EMC_GATE_5, -1, -1
5750 Xdoor_6, TRUE, FALSE,
5751 EL_EMC_GATE_6, -1, -1
5754 Xdoor_7, TRUE, FALSE,
5755 EL_EMC_GATE_7, -1, -1
5758 Xdoor_8, TRUE, FALSE,
5759 EL_EMC_GATE_8, -1, -1
5762 Xkey_1, TRUE, FALSE,
5766 Xkey_2, TRUE, FALSE,
5770 Xkey_3, TRUE, FALSE,
5774 Xkey_4, TRUE, FALSE,
5778 Xkey_5, TRUE, FALSE,
5779 EL_EMC_KEY_5, -1, -1
5782 Xkey_6, TRUE, FALSE,
5783 EL_EMC_KEY_6, -1, -1
5786 Xkey_7, TRUE, FALSE,
5787 EL_EMC_KEY_7, -1, -1
5790 Xkey_8, TRUE, FALSE,
5791 EL_EMC_KEY_8, -1, -1
5794 Xwind_n, TRUE, FALSE,
5795 EL_BALLOON_SWITCH_UP, -1, -1
5798 Xwind_e, TRUE, FALSE,
5799 EL_BALLOON_SWITCH_RIGHT, -1, -1
5802 Xwind_s, TRUE, FALSE,
5803 EL_BALLOON_SWITCH_DOWN, -1, -1
5806 Xwind_w, TRUE, FALSE,
5807 EL_BALLOON_SWITCH_LEFT, -1, -1
5810 Xwind_nesw, TRUE, FALSE,
5811 EL_BALLOON_SWITCH_ANY, -1, -1
5814 Xwind_stop, TRUE, FALSE,
5815 EL_BALLOON_SWITCH_NONE, -1, -1
5819 EL_EM_EXIT_CLOSED, -1, -1
5822 Xexit_1, TRUE, FALSE,
5823 EL_EM_EXIT_OPEN, -1, -1
5826 Xexit_2, FALSE, FALSE,
5827 EL_EM_EXIT_OPEN, -1, -1
5830 Xexit_3, FALSE, FALSE,
5831 EL_EM_EXIT_OPEN, -1, -1
5834 Xdynamite, TRUE, FALSE,
5835 EL_EM_DYNAMITE, -1, -1
5838 Ydynamite_eat, FALSE, FALSE,
5839 EL_EM_DYNAMITE, ACTION_COLLECTING, -1
5842 Xdynamite_1, TRUE, FALSE,
5843 EL_EM_DYNAMITE_ACTIVE, -1, -1
5846 Xdynamite_2, FALSE, FALSE,
5847 EL_EM_DYNAMITE_ACTIVE, -1, -1
5850 Xdynamite_3, FALSE, FALSE,
5851 EL_EM_DYNAMITE_ACTIVE, -1, -1
5854 Xdynamite_4, FALSE, FALSE,
5855 EL_EM_DYNAMITE_ACTIVE, -1, -1
5858 Xbumper, TRUE, FALSE,
5859 EL_EMC_SPRING_BUMPER, -1, -1
5862 XbumperB, FALSE, FALSE,
5863 EL_EMC_SPRING_BUMPER, ACTION_ACTIVE, -1
5866 Xwheel, TRUE, FALSE,
5867 EL_ROBOT_WHEEL, -1, -1
5870 XwheelB, FALSE, FALSE,
5871 EL_ROBOT_WHEEL, ACTION_ACTIVE, -1
5874 Xswitch, TRUE, FALSE,
5875 EL_EMC_MAGIC_BALL_SWITCH, -1, -1
5878 XswitchB, FALSE, FALSE,
5879 EL_EMC_MAGIC_BALL_SWITCH, ACTION_ACTIVE, -1
5883 EL_QUICKSAND_EMPTY, -1, -1
5886 Xsand_stone, TRUE, FALSE,
5887 EL_QUICKSAND_FULL, -1, -1
5890 Xsand_stonein_1, FALSE, TRUE,
5891 EL_ROCK, ACTION_FILLING, -1
5894 Xsand_stonein_2, FALSE, TRUE,
5895 EL_ROCK, ACTION_FILLING, -1
5898 Xsand_stonein_3, FALSE, TRUE,
5899 EL_ROCK, ACTION_FILLING, -1
5902 Xsand_stonein_4, FALSE, TRUE,
5903 EL_ROCK, ACTION_FILLING, -1
5906 Xsand_stonesand_1, FALSE, FALSE,
5907 EL_QUICKSAND_EMPTYING, -1, -1
5910 Xsand_stonesand_2, FALSE, FALSE,
5911 EL_QUICKSAND_EMPTYING, -1, -1
5914 Xsand_stonesand_3, FALSE, FALSE,
5915 EL_QUICKSAND_EMPTYING, -1, -1
5918 Xsand_stonesand_4, FALSE, FALSE,
5919 EL_QUICKSAND_EMPTYING, -1, -1
5922 Xsand_stonesand_quickout_1, FALSE, FALSE,
5923 EL_QUICKSAND_EMPTYING, -1, -1
5926 Xsand_stonesand_quickout_2, FALSE, FALSE,
5927 EL_QUICKSAND_EMPTYING, -1, -1
5930 Xsand_stoneout_1, FALSE, FALSE,
5931 EL_ROCK, ACTION_EMPTYING, -1
5934 Xsand_stoneout_2, FALSE, FALSE,
5935 EL_ROCK, ACTION_EMPTYING, -1
5938 Xsand_sandstone_1, FALSE, FALSE,
5939 EL_QUICKSAND_FILLING, -1, -1
5942 Xsand_sandstone_2, FALSE, FALSE,
5943 EL_QUICKSAND_FILLING, -1, -1
5946 Xsand_sandstone_3, FALSE, FALSE,
5947 EL_QUICKSAND_FILLING, -1, -1
5950 Xsand_sandstone_4, FALSE, FALSE,
5951 EL_QUICKSAND_FILLING, -1, -1
5954 Xplant, TRUE, FALSE,
5955 EL_EMC_PLANT, -1, -1
5958 Yplant, FALSE, FALSE,
5959 EL_EMC_PLANT, -1, -1
5962 Xlenses, TRUE, FALSE,
5963 EL_EMC_LENSES, -1, -1
5966 Xmagnify, TRUE, FALSE,
5967 EL_EMC_MAGNIFIER, -1, -1
5970 Xdripper, TRUE, FALSE,
5971 EL_EMC_DRIPPER, -1, -1
5974 XdripperB, FALSE, FALSE,
5975 EL_EMC_DRIPPER, ACTION_ACTIVE, -1
5978 Xfake_blank, TRUE, FALSE,
5979 EL_INVISIBLE_WALL, -1, -1
5982 Xfake_blankB, FALSE, FALSE,
5983 EL_INVISIBLE_WALL, ACTION_ACTIVE, -1
5986 Xfake_grass, TRUE, FALSE,
5987 EL_EMC_FAKE_GRASS, -1, -1
5990 Xfake_grassB, FALSE, FALSE,
5991 EL_EMC_FAKE_GRASS, ACTION_ACTIVE, -1
5994 Xfake_door_1, TRUE, FALSE,
5995 EL_EM_GATE_1_GRAY, -1, -1
5998 Xfake_door_2, TRUE, FALSE,
5999 EL_EM_GATE_2_GRAY, -1, -1
6002 Xfake_door_3, TRUE, FALSE,
6003 EL_EM_GATE_3_GRAY, -1, -1
6006 Xfake_door_4, TRUE, FALSE,
6007 EL_EM_GATE_4_GRAY, -1, -1
6010 Xfake_door_5, TRUE, FALSE,
6011 EL_EMC_GATE_5_GRAY, -1, -1
6014 Xfake_door_6, TRUE, FALSE,
6015 EL_EMC_GATE_6_GRAY, -1, -1
6018 Xfake_door_7, TRUE, FALSE,
6019 EL_EMC_GATE_7_GRAY, -1, -1
6022 Xfake_door_8, TRUE, FALSE,
6023 EL_EMC_GATE_8_GRAY, -1, -1
6026 Xfake_acid_1, TRUE, FALSE,
6027 EL_EMC_FAKE_ACID, -1, -1
6030 Xfake_acid_2, FALSE, FALSE,
6031 EL_EMC_FAKE_ACID, -1, -1
6034 Xfake_acid_3, FALSE, FALSE,
6035 EL_EMC_FAKE_ACID, -1, -1
6038 Xfake_acid_4, FALSE, FALSE,
6039 EL_EMC_FAKE_ACID, -1, -1
6042 Xfake_acid_5, FALSE, FALSE,
6043 EL_EMC_FAKE_ACID, -1, -1
6046 Xfake_acid_6, FALSE, FALSE,
6047 EL_EMC_FAKE_ACID, -1, -1
6050 Xfake_acid_7, FALSE, FALSE,
6051 EL_EMC_FAKE_ACID, -1, -1
6054 Xfake_acid_8, FALSE, FALSE,
6055 EL_EMC_FAKE_ACID, -1, -1
6058 Xsteel_1, TRUE, FALSE,
6059 EL_STEELWALL, -1, -1
6062 Xsteel_2, TRUE, FALSE,
6063 EL_EMC_STEELWALL_2, -1, -1
6066 Xsteel_3, TRUE, FALSE,
6067 EL_EMC_STEELWALL_3, -1, -1
6070 Xsteel_4, TRUE, FALSE,
6071 EL_EMC_STEELWALL_4, -1, -1
6074 Xwall_1, TRUE, FALSE,
6078 Xwall_2, TRUE, FALSE,
6079 EL_EMC_WALL_14, -1, -1
6082 Xwall_3, TRUE, FALSE,
6083 EL_EMC_WALL_15, -1, -1
6086 Xwall_4, TRUE, FALSE,
6087 EL_EMC_WALL_16, -1, -1
6090 Xround_wall_1, TRUE, FALSE,
6091 EL_WALL_SLIPPERY, -1, -1
6094 Xround_wall_2, TRUE, FALSE,
6095 EL_EMC_WALL_SLIPPERY_2, -1, -1
6098 Xround_wall_3, TRUE, FALSE,
6099 EL_EMC_WALL_SLIPPERY_3, -1, -1
6102 Xround_wall_4, TRUE, FALSE,
6103 EL_EMC_WALL_SLIPPERY_4, -1, -1
6106 Xdecor_1, TRUE, FALSE,
6107 EL_EMC_WALL_8, -1, -1
6110 Xdecor_2, TRUE, FALSE,
6111 EL_EMC_WALL_6, -1, -1
6114 Xdecor_3, TRUE, FALSE,
6115 EL_EMC_WALL_4, -1, -1
6118 Xdecor_4, TRUE, FALSE,
6119 EL_EMC_WALL_7, -1, -1
6122 Xdecor_5, TRUE, FALSE,
6123 EL_EMC_WALL_5, -1, -1
6126 Xdecor_6, TRUE, FALSE,
6127 EL_EMC_WALL_9, -1, -1
6130 Xdecor_7, TRUE, FALSE,
6131 EL_EMC_WALL_10, -1, -1
6134 Xdecor_8, TRUE, FALSE,
6135 EL_EMC_WALL_1, -1, -1
6138 Xdecor_9, TRUE, FALSE,
6139 EL_EMC_WALL_2, -1, -1
6142 Xdecor_10, TRUE, FALSE,
6143 EL_EMC_WALL_3, -1, -1
6146 Xdecor_11, TRUE, FALSE,
6147 EL_EMC_WALL_11, -1, -1
6150 Xdecor_12, TRUE, FALSE,
6151 EL_EMC_WALL_12, -1, -1
6154 Xalpha_0, TRUE, FALSE,
6155 EL_CHAR('0'), -1, -1
6158 Xalpha_1, TRUE, FALSE,
6159 EL_CHAR('1'), -1, -1
6162 Xalpha_2, TRUE, FALSE,
6163 EL_CHAR('2'), -1, -1
6166 Xalpha_3, TRUE, FALSE,
6167 EL_CHAR('3'), -1, -1
6170 Xalpha_4, TRUE, FALSE,
6171 EL_CHAR('4'), -1, -1
6174 Xalpha_5, TRUE, FALSE,
6175 EL_CHAR('5'), -1, -1
6178 Xalpha_6, TRUE, FALSE,
6179 EL_CHAR('6'), -1, -1
6182 Xalpha_7, TRUE, FALSE,
6183 EL_CHAR('7'), -1, -1
6186 Xalpha_8, TRUE, FALSE,
6187 EL_CHAR('8'), -1, -1
6190 Xalpha_9, TRUE, FALSE,
6191 EL_CHAR('9'), -1, -1
6194 Xalpha_excla, TRUE, FALSE,
6195 EL_CHAR('!'), -1, -1
6198 Xalpha_quote, TRUE, FALSE,
6199 EL_CHAR('"'), -1, -1
6202 Xalpha_comma, TRUE, FALSE,
6203 EL_CHAR(','), -1, -1
6206 Xalpha_minus, TRUE, FALSE,
6207 EL_CHAR('-'), -1, -1
6210 Xalpha_perio, TRUE, FALSE,
6211 EL_CHAR('.'), -1, -1
6214 Xalpha_colon, TRUE, FALSE,
6215 EL_CHAR(':'), -1, -1
6218 Xalpha_quest, TRUE, FALSE,
6219 EL_CHAR('?'), -1, -1
6222 Xalpha_a, TRUE, FALSE,
6223 EL_CHAR('A'), -1, -1
6226 Xalpha_b, TRUE, FALSE,
6227 EL_CHAR('B'), -1, -1
6230 Xalpha_c, TRUE, FALSE,
6231 EL_CHAR('C'), -1, -1
6234 Xalpha_d, TRUE, FALSE,
6235 EL_CHAR('D'), -1, -1
6238 Xalpha_e, TRUE, FALSE,
6239 EL_CHAR('E'), -1, -1
6242 Xalpha_f, TRUE, FALSE,
6243 EL_CHAR('F'), -1, -1
6246 Xalpha_g, TRUE, FALSE,
6247 EL_CHAR('G'), -1, -1
6250 Xalpha_h, TRUE, FALSE,
6251 EL_CHAR('H'), -1, -1
6254 Xalpha_i, TRUE, FALSE,
6255 EL_CHAR('I'), -1, -1
6258 Xalpha_j, TRUE, FALSE,
6259 EL_CHAR('J'), -1, -1
6262 Xalpha_k, TRUE, FALSE,
6263 EL_CHAR('K'), -1, -1
6266 Xalpha_l, TRUE, FALSE,
6267 EL_CHAR('L'), -1, -1
6270 Xalpha_m, TRUE, FALSE,
6271 EL_CHAR('M'), -1, -1
6274 Xalpha_n, TRUE, FALSE,
6275 EL_CHAR('N'), -1, -1
6278 Xalpha_o, TRUE, FALSE,
6279 EL_CHAR('O'), -1, -1
6282 Xalpha_p, TRUE, FALSE,
6283 EL_CHAR('P'), -1, -1
6286 Xalpha_q, TRUE, FALSE,
6287 EL_CHAR('Q'), -1, -1
6290 Xalpha_r, TRUE, FALSE,
6291 EL_CHAR('R'), -1, -1
6294 Xalpha_s, TRUE, FALSE,
6295 EL_CHAR('S'), -1, -1
6298 Xalpha_t, TRUE, FALSE,
6299 EL_CHAR('T'), -1, -1
6302 Xalpha_u, TRUE, FALSE,
6303 EL_CHAR('U'), -1, -1
6306 Xalpha_v, TRUE, FALSE,
6307 EL_CHAR('V'), -1, -1
6310 Xalpha_w, TRUE, FALSE,
6311 EL_CHAR('W'), -1, -1
6314 Xalpha_x, TRUE, FALSE,
6315 EL_CHAR('X'), -1, -1
6318 Xalpha_y, TRUE, FALSE,
6319 EL_CHAR('Y'), -1, -1
6322 Xalpha_z, TRUE, FALSE,
6323 EL_CHAR('Z'), -1, -1
6326 Xalpha_arrow_e, TRUE, FALSE,
6327 EL_CHAR('>'), -1, -1
6330 Xalpha_arrow_w, TRUE, FALSE,
6331 EL_CHAR('<'), -1, -1
6334 Xalpha_copyr, TRUE, FALSE,
6335 EL_CHAR(CHAR_BYTE_COPYRIGHT), -1, -1
6339 Xboom_bug, FALSE, FALSE,
6340 EL_BUG, ACTION_EXPLODING, -1
6343 Xboom_bomb, FALSE, FALSE,
6344 EL_BOMB, ACTION_EXPLODING, -1
6347 Xboom_android, FALSE, FALSE,
6348 EL_EMC_ANDROID, ACTION_OTHER, -1
6351 Xboom_1, FALSE, FALSE,
6352 EL_DEFAULT, ACTION_EXPLODING, -1
6355 Xboom_2, FALSE, FALSE,
6356 EL_DEFAULT, ACTION_EXPLODING, -1
6359 Znormal, FALSE, FALSE,
6363 Zdynamite, FALSE, FALSE,
6367 Zplayer, FALSE, FALSE,
6371 ZBORDER, FALSE, FALSE,
6381 static struct Mapping_EM_to_RND_player
6390 em_player_mapping_list[] =
6394 EL_PLAYER_1, ACTION_MOVING, MV_BIT_UP,
6398 EL_PLAYER_1, ACTION_MOVING, MV_BIT_RIGHT,
6402 EL_PLAYER_1, ACTION_MOVING, MV_BIT_DOWN,
6406 EL_PLAYER_1, ACTION_MOVING, MV_BIT_LEFT,
6410 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_UP,
6414 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_RIGHT,
6418 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_DOWN,
6422 EL_PLAYER_1, ACTION_PUSHING, MV_BIT_LEFT,
6426 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_UP,
6430 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_RIGHT,
6434 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_DOWN,
6438 EL_PLAYER_1, ACTION_SNAPPING, MV_BIT_LEFT,
6442 EL_PLAYER_2, ACTION_MOVING, MV_BIT_UP,
6446 EL_PLAYER_2, ACTION_MOVING, MV_BIT_RIGHT,
6450 EL_PLAYER_2, ACTION_MOVING, MV_BIT_DOWN,
6454 EL_PLAYER_2, ACTION_MOVING, MV_BIT_LEFT,
6458 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_UP,
6462 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_RIGHT,
6466 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_DOWN,
6470 EL_PLAYER_2, ACTION_PUSHING, MV_BIT_LEFT,
6474 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_UP,
6478 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_RIGHT,
6482 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_DOWN,
6486 EL_PLAYER_2, ACTION_SNAPPING, MV_BIT_LEFT,
6490 EL_PLAYER_1, ACTION_DEFAULT, -1,
6494 EL_PLAYER_2, ACTION_DEFAULT, -1,
6498 EL_PLAYER_3, ACTION_MOVING, MV_BIT_UP,
6502 EL_PLAYER_3, ACTION_MOVING, MV_BIT_RIGHT,
6506 EL_PLAYER_3, ACTION_MOVING, MV_BIT_DOWN,
6510 EL_PLAYER_3, ACTION_MOVING, MV_BIT_LEFT,
6514 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_UP,
6518 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_RIGHT,
6522 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_DOWN,
6526 EL_PLAYER_3, ACTION_PUSHING, MV_BIT_LEFT,
6530 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_UP,
6534 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_RIGHT,
6538 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_DOWN,
6542 EL_PLAYER_3, ACTION_SNAPPING, MV_BIT_LEFT,
6546 EL_PLAYER_4, ACTION_MOVING, MV_BIT_UP,
6550 EL_PLAYER_4, ACTION_MOVING, MV_BIT_RIGHT,
6554 EL_PLAYER_4, ACTION_MOVING, MV_BIT_DOWN,
6558 EL_PLAYER_4, ACTION_MOVING, MV_BIT_LEFT,
6562 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_UP,
6566 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_RIGHT,
6570 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_DOWN,
6574 EL_PLAYER_4, ACTION_PUSHING, MV_BIT_LEFT,
6578 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_UP,
6582 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_RIGHT,
6586 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_DOWN,
6590 EL_PLAYER_4, ACTION_SNAPPING, MV_BIT_LEFT,
6594 EL_PLAYER_3, ACTION_DEFAULT, -1,
6598 EL_PLAYER_4, ACTION_DEFAULT, -1,
6607 int map_element_RND_to_EM(int element_rnd)
6609 static unsigned short mapping_RND_to_EM[NUM_FILE_ELEMENTS];
6610 static boolean mapping_initialized = FALSE;
6612 if (!mapping_initialized)
6616 /* return "Xalpha_quest" for all undefined elements in mapping array */
6617 for (i = 0; i < NUM_FILE_ELEMENTS; i++)
6618 mapping_RND_to_EM[i] = Xalpha_quest;
6620 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6621 if (em_object_mapping_list[i].is_rnd_to_em_mapping)
6622 mapping_RND_to_EM[em_object_mapping_list[i].element_rnd] =
6623 em_object_mapping_list[i].element_em;
6625 mapping_initialized = TRUE;
6628 if (element_rnd >= 0 && element_rnd < NUM_FILE_ELEMENTS)
6629 return mapping_RND_to_EM[element_rnd];
6631 Error(ERR_WARN, "invalid RND level element %d", element_rnd);
6636 int map_element_EM_to_RND(int element_em)
6638 static unsigned short mapping_EM_to_RND[TILE_MAX];
6639 static boolean mapping_initialized = FALSE;
6641 if (!mapping_initialized)
6645 /* return "EL_UNKNOWN" for all undefined elements in mapping array */
6646 for (i = 0; i < TILE_MAX; i++)
6647 mapping_EM_to_RND[i] = EL_UNKNOWN;
6649 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
6650 mapping_EM_to_RND[em_object_mapping_list[i].element_em] =
6651 em_object_mapping_list[i].element_rnd;
6653 mapping_initialized = TRUE;
6656 if (element_em >= 0 && element_em < TILE_MAX)
6657 return mapping_EM_to_RND[element_em];
6659 Error(ERR_WARN, "invalid EM level element %d", element_em);
6664 void map_android_clone_elements_RND_to_EM(struct LevelInfo *level)
6666 struct LevelInfo_EM *level_em = level->native_em_level;
6667 struct LEVEL *lev = level_em->lev;
6670 for (i = 0; i < TILE_MAX; i++)
6671 lev->android_array[i] = Xblank;
6673 for (i = 0; i < level->num_android_clone_elements; i++)
6675 int element_rnd = level->android_clone_element[i];
6676 int element_em = map_element_RND_to_EM(element_rnd);
6678 for (j = 0; em_object_mapping_list[j].element_em != -1; j++)
6679 if (em_object_mapping_list[j].element_rnd == element_rnd)
6680 lev->android_array[em_object_mapping_list[j].element_em] = element_em;
6684 void map_android_clone_elements_EM_to_RND(struct LevelInfo *level)
6686 struct LevelInfo_EM *level_em = level->native_em_level;
6687 struct LEVEL *lev = level_em->lev;
6690 level->num_android_clone_elements = 0;
6692 for (i = 0; i < TILE_MAX; i++)
6694 int element_em = lev->android_array[i];
6696 boolean element_found = FALSE;
6698 if (element_em == Xblank)
6701 element_rnd = map_element_EM_to_RND(element_em);
6703 for (j = 0; j < level->num_android_clone_elements; j++)
6704 if (level->android_clone_element[j] == element_rnd)
6705 element_found = TRUE;
6709 level->android_clone_element[level->num_android_clone_elements++] =
6712 if (level->num_android_clone_elements == MAX_ANDROID_ELEMENTS)
6717 if (level->num_android_clone_elements == 0)
6719 level->num_android_clone_elements = 1;
6720 level->android_clone_element[0] = EL_EMPTY;
6724 int map_direction_RND_to_EM(int direction)
6726 return (direction == MV_UP ? 0 :
6727 direction == MV_RIGHT ? 1 :
6728 direction == MV_DOWN ? 2 :
6729 direction == MV_LEFT ? 3 :
6733 int map_direction_EM_to_RND(int direction)
6735 return (direction == 0 ? MV_UP :
6736 direction == 1 ? MV_RIGHT :
6737 direction == 2 ? MV_DOWN :
6738 direction == 3 ? MV_LEFT :
6742 int map_element_RND_to_SP(int element_rnd)
6744 int element_sp = 0x20; /* map unknown elements to yellow "hardware" */
6746 if (element_rnd >= EL_SP_START &&
6747 element_rnd <= EL_SP_END)
6748 element_sp = element_rnd - EL_SP_START;
6749 else if (element_rnd == EL_EMPTY_SPACE)
6751 else if (element_rnd == EL_INVISIBLE_WALL)
6757 int map_element_SP_to_RND(int element_sp)
6759 int element_rnd = EL_UNKNOWN;
6761 if (element_sp >= 0x00 &&
6763 element_rnd = EL_SP_START + element_sp;
6764 else if (element_sp == 0x28)
6765 element_rnd = EL_INVISIBLE_WALL;
6770 int map_action_SP_to_RND(int action_sp)
6774 case actActive: return ACTION_ACTIVE;
6775 case actImpact: return ACTION_IMPACT;
6776 case actExploding: return ACTION_EXPLODING;
6777 case actDigging: return ACTION_DIGGING;
6778 case actSnapping: return ACTION_SNAPPING;
6779 case actCollecting: return ACTION_COLLECTING;
6780 case actPassing: return ACTION_PASSING;
6781 case actPushing: return ACTION_PUSHING;
6782 case actDropping: return ACTION_DROPPING;
6784 default: return ACTION_DEFAULT;
6788 int get_next_element(int element)
6792 case EL_QUICKSAND_FILLING: return EL_QUICKSAND_FULL;
6793 case EL_QUICKSAND_EMPTYING: return EL_QUICKSAND_EMPTY;
6794 case EL_QUICKSAND_FAST_FILLING: return EL_QUICKSAND_FAST_FULL;
6795 case EL_QUICKSAND_FAST_EMPTYING: return EL_QUICKSAND_FAST_EMPTY;
6796 case EL_MAGIC_WALL_FILLING: return EL_MAGIC_WALL_FULL;
6797 case EL_MAGIC_WALL_EMPTYING: return EL_MAGIC_WALL_ACTIVE;
6798 case EL_BD_MAGIC_WALL_FILLING: return EL_BD_MAGIC_WALL_FULL;
6799 case EL_BD_MAGIC_WALL_EMPTYING: return EL_BD_MAGIC_WALL_ACTIVE;
6800 case EL_DC_MAGIC_WALL_FILLING: return EL_DC_MAGIC_WALL_FULL;
6801 case EL_DC_MAGIC_WALL_EMPTYING: return EL_DC_MAGIC_WALL_ACTIVE;
6802 case EL_AMOEBA_DROPPING: return EL_AMOEBA_WET;
6804 default: return element;
6808 int el_act_dir2img(int element, int action, int direction)
6810 element = GFX_ELEMENT(element);
6811 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6813 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6814 return element_info[element].direction_graphic[action][direction];
6817 static int el_act_dir2crm(int element, int action, int direction)
6819 element = GFX_ELEMENT(element);
6820 direction = MV_DIR_TO_BIT(direction); /* default: MV_NONE => MV_DOWN */
6822 /* direction_graphic[][] == graphic[] for undefined direction graphics */
6823 return element_info[element].direction_crumbled[action][direction];
6826 int el_act2img(int element, int action)
6828 element = GFX_ELEMENT(element);
6830 return element_info[element].graphic[action];
6833 int el_act2crm(int element, int action)
6835 element = GFX_ELEMENT(element);
6837 return element_info[element].crumbled[action];
6840 int el_dir2img(int element, int direction)
6842 element = GFX_ELEMENT(element);
6844 return el_act_dir2img(element, ACTION_DEFAULT, direction);
6847 int el2baseimg(int element)
6849 return element_info[element].graphic[ACTION_DEFAULT];
6852 int el2img(int element)
6854 element = GFX_ELEMENT(element);
6856 return element_info[element].graphic[ACTION_DEFAULT];
6859 int el2edimg(int element)
6861 element = GFX_ELEMENT(element);
6863 return element_info[element].special_graphic[GFX_SPECIAL_ARG_EDITOR];
6866 int el2preimg(int element)
6868 element = GFX_ELEMENT(element);
6870 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PREVIEW];
6873 int el2panelimg(int element)
6875 element = GFX_ELEMENT(element);
6877 return element_info[element].special_graphic[GFX_SPECIAL_ARG_PANEL];
6880 int font2baseimg(int font_nr)
6882 return font_info[font_nr].special_graphic[GFX_SPECIAL_ARG_DEFAULT];
6885 int getBeltNrFromBeltElement(int element)
6887 return (element < EL_CONVEYOR_BELT_2_LEFT ? 0 :
6888 element < EL_CONVEYOR_BELT_3_LEFT ? 1 :
6889 element < EL_CONVEYOR_BELT_4_LEFT ? 2 : 3);
6892 int getBeltNrFromBeltActiveElement(int element)
6894 return (element < EL_CONVEYOR_BELT_2_LEFT_ACTIVE ? 0 :
6895 element < EL_CONVEYOR_BELT_3_LEFT_ACTIVE ? 1 :
6896 element < EL_CONVEYOR_BELT_4_LEFT_ACTIVE ? 2 : 3);
6899 int getBeltNrFromBeltSwitchElement(int element)
6901 return (element < EL_CONVEYOR_BELT_2_SWITCH_LEFT ? 0 :
6902 element < EL_CONVEYOR_BELT_3_SWITCH_LEFT ? 1 :
6903 element < EL_CONVEYOR_BELT_4_SWITCH_LEFT ? 2 : 3);
6906 int getBeltDirNrFromBeltElement(int element)
6908 static int belt_base_element[4] =
6910 EL_CONVEYOR_BELT_1_LEFT,
6911 EL_CONVEYOR_BELT_2_LEFT,
6912 EL_CONVEYOR_BELT_3_LEFT,
6913 EL_CONVEYOR_BELT_4_LEFT
6916 int belt_nr = getBeltNrFromBeltElement(element);
6917 int belt_dir_nr = element - belt_base_element[belt_nr];
6919 return (belt_dir_nr % 3);
6922 int getBeltDirNrFromBeltSwitchElement(int element)
6924 static int belt_base_element[4] =
6926 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6927 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6928 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6929 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6932 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6933 int belt_dir_nr = element - belt_base_element[belt_nr];
6935 return (belt_dir_nr % 3);
6938 int getBeltDirFromBeltElement(int element)
6940 static int belt_move_dir[3] =
6947 int belt_dir_nr = getBeltDirNrFromBeltElement(element);
6949 return belt_move_dir[belt_dir_nr];
6952 int getBeltDirFromBeltSwitchElement(int element)
6954 static int belt_move_dir[3] =
6961 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(element);
6963 return belt_move_dir[belt_dir_nr];
6966 int getBeltElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6968 static int belt_base_element[4] =
6970 EL_CONVEYOR_BELT_1_LEFT,
6971 EL_CONVEYOR_BELT_2_LEFT,
6972 EL_CONVEYOR_BELT_3_LEFT,
6973 EL_CONVEYOR_BELT_4_LEFT
6976 return belt_base_element[belt_nr] + belt_dir_nr;
6979 int getBeltElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
6981 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
6983 return getBeltElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
6986 int getBeltSwitchElementFromBeltNrAndBeltDirNr(int belt_nr, int belt_dir_nr)
6988 static int belt_base_element[4] =
6990 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6991 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6992 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6993 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6996 return belt_base_element[belt_nr] + belt_dir_nr;
6999 int getBeltSwitchElementFromBeltNrAndBeltDir(int belt_nr, int belt_dir)
7001 int belt_dir_nr = (belt_dir == MV_LEFT ? 0 : belt_dir == MV_RIGHT ? 2 : 1);
7003 return getBeltSwitchElementFromBeltNrAndBeltDirNr(belt_nr, belt_dir_nr);
7006 boolean getTeamMode_EM()
7008 return game.team_mode;
7011 int getGameFrameDelay_EM(int native_em_game_frame_delay)
7013 int game_frame_delay_value;
7015 game_frame_delay_value =
7016 (tape.playing && tape.fast_forward ? FfwdFrameDelay :
7017 GameFrameDelay == GAME_FRAME_DELAY ? native_em_game_frame_delay :
7020 if (tape.playing && tape.warp_forward && !tape.pausing)
7021 game_frame_delay_value = 0;
7023 return game_frame_delay_value;
7026 unsigned int InitRND(int seed)
7028 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
7029 return InitEngineRandom_EM(seed);
7030 else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
7031 return InitEngineRandom_SP(seed);
7033 return InitEngineRandom_RND(seed);
7036 static struct Mapping_EM_to_RND_object object_mapping[TILE_MAX];
7037 static struct Mapping_EM_to_RND_player player_mapping[MAX_PLAYERS][SPR_MAX];
7039 inline static int get_effective_element_EM(int tile, int frame_em)
7041 int element = object_mapping[tile].element_rnd;
7042 int action = object_mapping[tile].action;
7043 boolean is_backside = object_mapping[tile].is_backside;
7044 boolean action_removing = (action == ACTION_DIGGING ||
7045 action == ACTION_SNAPPING ||
7046 action == ACTION_COLLECTING);
7052 case Yacid_splash_eB:
7053 case Yacid_splash_wB:
7054 return (frame_em > 5 ? EL_EMPTY : element);
7060 else /* frame_em == 7 */
7064 case Yacid_splash_eB:
7065 case Yacid_splash_wB:
7068 case Yemerald_stone:
7071 case Ydiamond_stone:
7075 case Xdrip_stretchB:
7094 case Xsand_stonein_1:
7095 case Xsand_stonein_2:
7096 case Xsand_stonein_3:
7097 case Xsand_stonein_4:
7101 return (is_backside || action_removing ? EL_EMPTY : element);
7106 inline static boolean check_linear_animation_EM(int tile)
7110 case Xsand_stonesand_1:
7111 case Xsand_stonesand_quickout_1:
7112 case Xsand_sandstone_1:
7113 case Xsand_stonein_1:
7114 case Xsand_stoneout_1:
7133 case Yacid_splash_eB:
7134 case Yacid_splash_wB:
7135 case Yemerald_stone:
7142 inline static void set_crumbled_graphics_EM(struct GraphicInfo_EM *g_em,
7143 boolean has_crumbled_graphics,
7144 int crumbled, int sync_frame)
7146 /* if element can be crumbled, but certain action graphics are just empty
7147 space (like instantly snapping sand to empty space in 1 frame), do not
7148 treat these empty space graphics as crumbled graphics in EMC engine */
7149 if (crumbled == IMG_EMPTY_SPACE)
7150 has_crumbled_graphics = FALSE;
7152 if (has_crumbled_graphics)
7154 struct GraphicInfo *g_crumbled = &graphic_info[crumbled];
7155 int frame_crumbled = getAnimationFrame(g_crumbled->anim_frames,
7156 g_crumbled->anim_delay,
7157 g_crumbled->anim_mode,
7158 g_crumbled->anim_start_frame,
7161 getGraphicSource(crumbled, frame_crumbled, &g_em->crumbled_bitmap,
7162 &g_em->crumbled_src_x, &g_em->crumbled_src_y);
7164 g_em->crumbled_border_size = graphic_info[crumbled].border_size;
7166 g_em->has_crumbled_graphics = TRUE;
7170 g_em->crumbled_bitmap = NULL;
7171 g_em->crumbled_src_x = 0;
7172 g_em->crumbled_src_y = 0;
7173 g_em->crumbled_border_size = 0;
7175 g_em->has_crumbled_graphics = FALSE;
7179 void ResetGfxAnimation_EM(int x, int y, int tile)
7184 void SetGfxAnimation_EM(struct GraphicInfo_EM *g_em,
7185 int tile, int frame_em, int x, int y)
7187 int action = object_mapping[tile].action;
7188 int direction = object_mapping[tile].direction;
7189 int effective_element = get_effective_element_EM(tile, frame_em);
7190 int graphic = (direction == MV_NONE ?
7191 el_act2img(effective_element, action) :
7192 el_act_dir2img(effective_element, action, direction));
7193 struct GraphicInfo *g = &graphic_info[graphic];
7195 boolean action_removing = (action == ACTION_DIGGING ||
7196 action == ACTION_SNAPPING ||
7197 action == ACTION_COLLECTING);
7198 boolean action_moving = (action == ACTION_FALLING ||
7199 action == ACTION_MOVING ||
7200 action == ACTION_PUSHING ||
7201 action == ACTION_EATING ||
7202 action == ACTION_FILLING ||
7203 action == ACTION_EMPTYING);
7204 boolean action_falling = (action == ACTION_FALLING ||
7205 action == ACTION_FILLING ||
7206 action == ACTION_EMPTYING);
7208 /* special case: graphic uses "2nd movement tile" and has defined
7209 7 frames for movement animation (or less) => use default graphic
7210 for last (8th) frame which ends the movement animation */
7211 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7213 action = ACTION_DEFAULT; /* (keep action_* unchanged for now) */
7214 graphic = (direction == MV_NONE ?
7215 el_act2img(effective_element, action) :
7216 el_act_dir2img(effective_element, action, direction));
7218 g = &graphic_info[graphic];
7221 if ((action_removing || check_linear_animation_EM(tile)) && frame_em == 0)
7225 else if (action_moving)
7227 boolean is_backside = object_mapping[tile].is_backside;
7231 int direction = object_mapping[tile].direction;
7232 int move_dir = (action_falling ? MV_DOWN : direction);
7237 /* !!! TEST !!! NEW !!! DOES NOT WORK RIGHT YET !!! */
7238 if (g->double_movement && frame_em == 0)
7242 if (move_dir == MV_LEFT)
7243 GfxFrame[x - 1][y] = GfxFrame[x][y];
7244 else if (move_dir == MV_RIGHT)
7245 GfxFrame[x + 1][y] = GfxFrame[x][y];
7246 else if (move_dir == MV_UP)
7247 GfxFrame[x][y - 1] = GfxFrame[x][y];
7248 else if (move_dir == MV_DOWN)
7249 GfxFrame[x][y + 1] = GfxFrame[x][y];
7256 /* special case: animation for Xsand_stonesand_quickout_1/2 twice as fast */
7257 if (tile == Xsand_stonesand_quickout_1 ||
7258 tile == Xsand_stonesand_quickout_2)
7262 if (graphic_info[graphic].anim_global_sync)
7263 sync_frame = FrameCounter;
7264 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7265 sync_frame = GfxFrame[x][y];
7267 sync_frame = 0; /* playfield border (pseudo steel) */
7269 SetRandomAnimationValue(x, y);
7271 int frame = getAnimationFrame(g->anim_frames,
7274 g->anim_start_frame,
7277 g_em->unique_identifier =
7278 (graphic << 16) | ((frame % 8) << 12) | (g_em->width << 6) | g_em->height;
7281 void getGraphicSourceObjectExt_EM(struct GraphicInfo_EM *g_em,
7282 int tile, int frame_em, int x, int y)
7284 int action = object_mapping[tile].action;
7285 int direction = object_mapping[tile].direction;
7286 boolean is_backside = object_mapping[tile].is_backside;
7287 int effective_element = get_effective_element_EM(tile, frame_em);
7288 int effective_action = action;
7289 int graphic = (direction == MV_NONE ?
7290 el_act2img(effective_element, effective_action) :
7291 el_act_dir2img(effective_element, effective_action,
7293 int crumbled = (direction == MV_NONE ?
7294 el_act2crm(effective_element, effective_action) :
7295 el_act_dir2crm(effective_element, effective_action,
7297 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7298 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7299 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7300 struct GraphicInfo *g = &graphic_info[graphic];
7303 /* special case: graphic uses "2nd movement tile" and has defined
7304 7 frames for movement animation (or less) => use default graphic
7305 for last (8th) frame which ends the movement animation */
7306 if (g->double_movement && g->anim_frames < 8 && frame_em == 7)
7308 effective_action = ACTION_DEFAULT;
7309 graphic = (direction == MV_NONE ?
7310 el_act2img(effective_element, effective_action) :
7311 el_act_dir2img(effective_element, effective_action,
7313 crumbled = (direction == MV_NONE ?
7314 el_act2crm(effective_element, effective_action) :
7315 el_act_dir2crm(effective_element, effective_action,
7318 g = &graphic_info[graphic];
7321 if (graphic_info[graphic].anim_global_sync)
7322 sync_frame = FrameCounter;
7323 else if (IN_FIELD(x, y, MAX_LEV_FIELDX, MAX_LEV_FIELDY))
7324 sync_frame = GfxFrame[x][y];
7326 sync_frame = 0; /* playfield border (pseudo steel) */
7328 SetRandomAnimationValue(x, y);
7330 int frame = getAnimationFrame(g->anim_frames,
7333 g->anim_start_frame,
7336 getGraphicSourceExt(graphic, frame, &g_em->bitmap, &g_em->src_x, &g_em->src_y,
7337 g->double_movement && is_backside);
7339 /* (updating the "crumbled" graphic definitions is probably not really needed,
7340 as animations for crumbled graphics can't be longer than one EMC cycle) */
7341 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7345 void getGraphicSourcePlayerExt_EM(struct GraphicInfo_EM *g_em,
7346 int player_nr, int anim, int frame_em)
7348 int element = player_mapping[player_nr][anim].element_rnd;
7349 int action = player_mapping[player_nr][anim].action;
7350 int direction = player_mapping[player_nr][anim].direction;
7351 int graphic = (direction == MV_NONE ?
7352 el_act2img(element, action) :
7353 el_act_dir2img(element, action, direction));
7354 struct GraphicInfo *g = &graphic_info[graphic];
7357 InitPlayerGfxAnimation(&stored_player[player_nr], action, direction);
7359 stored_player[player_nr].StepFrame = frame_em;
7361 sync_frame = stored_player[player_nr].Frame;
7363 int frame = getAnimationFrame(g->anim_frames,
7366 g->anim_start_frame,
7369 getGraphicSourceExt(graphic, frame, &g_em->bitmap,
7370 &g_em->src_x, &g_em->src_y, FALSE);
7373 void InitGraphicInfo_EM(void)
7378 int num_em_gfx_errors = 0;
7380 if (graphic_info_em_object[0][0].bitmap == NULL)
7382 /* EM graphics not yet initialized in em_open_all() */
7387 printf("::: [4 errors can be ignored (1 x 'bomb', 3 x 'em_dynamite']\n");
7390 /* always start with reliable default values */
7391 for (i = 0; i < TILE_MAX; i++)
7393 object_mapping[i].element_rnd = EL_UNKNOWN;
7394 object_mapping[i].is_backside = FALSE;
7395 object_mapping[i].action = ACTION_DEFAULT;
7396 object_mapping[i].direction = MV_NONE;
7399 /* always start with reliable default values */
7400 for (p = 0; p < MAX_PLAYERS; p++)
7402 for (i = 0; i < SPR_MAX; i++)
7404 player_mapping[p][i].element_rnd = EL_UNKNOWN;
7405 player_mapping[p][i].action = ACTION_DEFAULT;
7406 player_mapping[p][i].direction = MV_NONE;
7410 for (i = 0; em_object_mapping_list[i].element_em != -1; i++)
7412 int e = em_object_mapping_list[i].element_em;
7414 object_mapping[e].element_rnd = em_object_mapping_list[i].element_rnd;
7415 object_mapping[e].is_backside = em_object_mapping_list[i].is_backside;
7417 if (em_object_mapping_list[i].action != -1)
7418 object_mapping[e].action = em_object_mapping_list[i].action;
7420 if (em_object_mapping_list[i].direction != -1)
7421 object_mapping[e].direction =
7422 MV_DIR_FROM_BIT(em_object_mapping_list[i].direction);
7425 for (i = 0; em_player_mapping_list[i].action_em != -1; i++)
7427 int a = em_player_mapping_list[i].action_em;
7428 int p = em_player_mapping_list[i].player_nr;
7430 player_mapping[p][a].element_rnd = em_player_mapping_list[i].element_rnd;
7432 if (em_player_mapping_list[i].action != -1)
7433 player_mapping[p][a].action = em_player_mapping_list[i].action;
7435 if (em_player_mapping_list[i].direction != -1)
7436 player_mapping[p][a].direction =
7437 MV_DIR_FROM_BIT(em_player_mapping_list[i].direction);
7440 for (i = 0; i < TILE_MAX; i++)
7442 int element = object_mapping[i].element_rnd;
7443 int action = object_mapping[i].action;
7444 int direction = object_mapping[i].direction;
7445 boolean is_backside = object_mapping[i].is_backside;
7446 boolean action_exploding = ((action == ACTION_EXPLODING ||
7447 action == ACTION_SMASHED_BY_ROCK ||
7448 action == ACTION_SMASHED_BY_SPRING) &&
7449 element != EL_DIAMOND);
7450 boolean action_active = (action == ACTION_ACTIVE);
7451 boolean action_other = (action == ACTION_OTHER);
7453 for (j = 0; j < 8; j++)
7455 int effective_element = get_effective_element_EM(i, j);
7456 int effective_action = (j < 7 ? action :
7457 i == Xdrip_stretch ? action :
7458 i == Xdrip_stretchB ? action :
7459 i == Ydrip_s1 ? action :
7460 i == Ydrip_s1B ? action :
7461 i == Xball_1B ? action :
7462 i == Xball_2 ? action :
7463 i == Xball_2B ? action :
7464 i == Yball_eat ? action :
7465 i == Ykey_1_eat ? action :
7466 i == Ykey_2_eat ? action :
7467 i == Ykey_3_eat ? action :
7468 i == Ykey_4_eat ? action :
7469 i == Ykey_5_eat ? action :
7470 i == Ykey_6_eat ? action :
7471 i == Ykey_7_eat ? action :
7472 i == Ykey_8_eat ? action :
7473 i == Ylenses_eat ? action :
7474 i == Ymagnify_eat ? action :
7475 i == Ygrass_eat ? action :
7476 i == Ydirt_eat ? action :
7477 i == Xsand_stonein_1 ? action :
7478 i == Xsand_stonein_2 ? action :
7479 i == Xsand_stonein_3 ? action :
7480 i == Xsand_stonein_4 ? action :
7481 i == Xsand_stoneout_1 ? action :
7482 i == Xsand_stoneout_2 ? action :
7483 i == Xboom_android ? ACTION_EXPLODING :
7484 action_exploding ? ACTION_EXPLODING :
7485 action_active ? action :
7486 action_other ? action :
7488 int graphic = (el_act_dir2img(effective_element, effective_action,
7490 int crumbled = (el_act_dir2crm(effective_element, effective_action,
7492 int base_graphic = el_act2img(effective_element, ACTION_DEFAULT);
7493 int base_crumbled = el_act2crm(effective_element, ACTION_DEFAULT);
7494 boolean has_action_graphics = (graphic != base_graphic);
7495 boolean has_crumbled_graphics = (base_crumbled != base_graphic);
7496 struct GraphicInfo *g = &graphic_info[graphic];
7497 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7500 /* ensure to get symmetric 3-frame, 2-delay animations as used in EM */
7501 boolean special_animation = (action != ACTION_DEFAULT &&
7502 g->anim_frames == 3 &&
7503 g->anim_delay == 2 &&
7504 g->anim_mode & ANIM_LINEAR);
7505 int sync_frame = (i == Xdrip_stretch ? 7 :
7506 i == Xdrip_stretchB ? 7 :
7507 i == Ydrip_s2 ? j + 8 :
7508 i == Ydrip_s2B ? j + 8 :
7517 i == Xfake_acid_1 ? 0 :
7518 i == Xfake_acid_2 ? 10 :
7519 i == Xfake_acid_3 ? 20 :
7520 i == Xfake_acid_4 ? 30 :
7521 i == Xfake_acid_5 ? 40 :
7522 i == Xfake_acid_6 ? 50 :
7523 i == Xfake_acid_7 ? 60 :
7524 i == Xfake_acid_8 ? 70 :
7526 i == Xball_2B ? j + 8 :
7527 i == Yball_eat ? j + 1 :
7528 i == Ykey_1_eat ? j + 1 :
7529 i == Ykey_2_eat ? j + 1 :
7530 i == Ykey_3_eat ? j + 1 :
7531 i == Ykey_4_eat ? j + 1 :
7532 i == Ykey_5_eat ? j + 1 :
7533 i == Ykey_6_eat ? j + 1 :
7534 i == Ykey_7_eat ? j + 1 :
7535 i == Ykey_8_eat ? j + 1 :
7536 i == Ylenses_eat ? j + 1 :
7537 i == Ymagnify_eat ? j + 1 :
7538 i == Ygrass_eat ? j + 1 :
7539 i == Ydirt_eat ? j + 1 :
7540 i == Xamoeba_1 ? 0 :
7541 i == Xamoeba_2 ? 1 :
7542 i == Xamoeba_3 ? 2 :
7543 i == Xamoeba_4 ? 3 :
7544 i == Xamoeba_5 ? 0 :
7545 i == Xamoeba_6 ? 1 :
7546 i == Xamoeba_7 ? 2 :
7547 i == Xamoeba_8 ? 3 :
7548 i == Xexit_2 ? j + 8 :
7549 i == Xexit_3 ? j + 16 :
7550 i == Xdynamite_1 ? 0 :
7551 i == Xdynamite_2 ? 8 :
7552 i == Xdynamite_3 ? 16 :
7553 i == Xdynamite_4 ? 24 :
7554 i == Xsand_stonein_1 ? j + 1 :
7555 i == Xsand_stonein_2 ? j + 9 :
7556 i == Xsand_stonein_3 ? j + 17 :
7557 i == Xsand_stonein_4 ? j + 25 :
7558 i == Xsand_stoneout_1 && j == 0 ? 0 :
7559 i == Xsand_stoneout_1 && j == 1 ? 0 :
7560 i == Xsand_stoneout_1 && j == 2 ? 1 :
7561 i == Xsand_stoneout_1 && j == 3 ? 2 :
7562 i == Xsand_stoneout_1 && j == 4 ? 2 :
7563 i == Xsand_stoneout_1 && j == 5 ? 3 :
7564 i == Xsand_stoneout_1 && j == 6 ? 4 :
7565 i == Xsand_stoneout_1 && j == 7 ? 4 :
7566 i == Xsand_stoneout_2 && j == 0 ? 5 :
7567 i == Xsand_stoneout_2 && j == 1 ? 6 :
7568 i == Xsand_stoneout_2 && j == 2 ? 7 :
7569 i == Xsand_stoneout_2 && j == 3 ? 8 :
7570 i == Xsand_stoneout_2 && j == 4 ? 9 :
7571 i == Xsand_stoneout_2 && j == 5 ? 11 :
7572 i == Xsand_stoneout_2 && j == 6 ? 13 :
7573 i == Xsand_stoneout_2 && j == 7 ? 15 :
7574 i == Xboom_bug && j == 1 ? 2 :
7575 i == Xboom_bug && j == 2 ? 2 :
7576 i == Xboom_bug && j == 3 ? 4 :
7577 i == Xboom_bug && j == 4 ? 4 :
7578 i == Xboom_bug && j == 5 ? 2 :
7579 i == Xboom_bug && j == 6 ? 2 :
7580 i == Xboom_bug && j == 7 ? 0 :
7581 i == Xboom_bomb && j == 1 ? 2 :
7582 i == Xboom_bomb && j == 2 ? 2 :
7583 i == Xboom_bomb && j == 3 ? 4 :
7584 i == Xboom_bomb && j == 4 ? 4 :
7585 i == Xboom_bomb && j == 5 ? 2 :
7586 i == Xboom_bomb && j == 6 ? 2 :
7587 i == Xboom_bomb && j == 7 ? 0 :
7588 i == Xboom_android && j == 7 ? 6 :
7589 i == Xboom_1 && j == 1 ? 2 :
7590 i == Xboom_1 && j == 2 ? 2 :
7591 i == Xboom_1 && j == 3 ? 4 :
7592 i == Xboom_1 && j == 4 ? 4 :
7593 i == Xboom_1 && j == 5 ? 6 :
7594 i == Xboom_1 && j == 6 ? 6 :
7595 i == Xboom_1 && j == 7 ? 8 :
7596 i == Xboom_2 && j == 0 ? 8 :
7597 i == Xboom_2 && j == 1 ? 8 :
7598 i == Xboom_2 && j == 2 ? 10 :
7599 i == Xboom_2 && j == 3 ? 10 :
7600 i == Xboom_2 && j == 4 ? 10 :
7601 i == Xboom_2 && j == 5 ? 12 :
7602 i == Xboom_2 && j == 6 ? 12 :
7603 i == Xboom_2 && j == 7 ? 12 :
7604 special_animation && j == 4 ? 3 :
7605 effective_action != action ? 0 :
7609 Bitmap *debug_bitmap = g_em->bitmap;
7610 int debug_src_x = g_em->src_x;
7611 int debug_src_y = g_em->src_y;
7614 int frame = getAnimationFrame(g->anim_frames,
7617 g->anim_start_frame,
7620 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y,
7621 g->double_movement && is_backside);
7623 g_em->bitmap = src_bitmap;
7624 g_em->src_x = src_x;
7625 g_em->src_y = src_y;
7626 g_em->src_offset_x = 0;
7627 g_em->src_offset_y = 0;
7628 g_em->dst_offset_x = 0;
7629 g_em->dst_offset_y = 0;
7630 g_em->width = TILEX;
7631 g_em->height = TILEY;
7633 g_em->preserve_background = FALSE;
7635 set_crumbled_graphics_EM(g_em, has_crumbled_graphics, crumbled,
7638 if ((!g->double_movement && (effective_action == ACTION_FALLING ||
7639 effective_action == ACTION_MOVING ||
7640 effective_action == ACTION_PUSHING ||
7641 effective_action == ACTION_EATING)) ||
7642 (!has_action_graphics && (effective_action == ACTION_FILLING ||
7643 effective_action == ACTION_EMPTYING)))
7646 (effective_action == ACTION_FALLING ||
7647 effective_action == ACTION_FILLING ||
7648 effective_action == ACTION_EMPTYING ? MV_DOWN : direction);
7649 int dx = (move_dir == MV_LEFT ? -1 : move_dir == MV_RIGHT ? 1 : 0);
7650 int dy = (move_dir == MV_UP ? -1 : move_dir == MV_DOWN ? 1 : 0);
7651 int num_steps = (i == Ydrip_s1 ? 16 :
7652 i == Ydrip_s1B ? 16 :
7653 i == Ydrip_s2 ? 16 :
7654 i == Ydrip_s2B ? 16 :
7655 i == Xsand_stonein_1 ? 32 :
7656 i == Xsand_stonein_2 ? 32 :
7657 i == Xsand_stonein_3 ? 32 :
7658 i == Xsand_stonein_4 ? 32 :
7659 i == Xsand_stoneout_1 ? 16 :
7660 i == Xsand_stoneout_2 ? 16 : 8);
7661 int cx = ABS(dx) * (TILEX / num_steps);
7662 int cy = ABS(dy) * (TILEY / num_steps);
7663 int step_frame = (i == Ydrip_s2 ? j + 8 :
7664 i == Ydrip_s2B ? j + 8 :
7665 i == Xsand_stonein_2 ? j + 8 :
7666 i == Xsand_stonein_3 ? j + 16 :
7667 i == Xsand_stonein_4 ? j + 24 :
7668 i == Xsand_stoneout_2 ? j + 8 : j) + 1;
7669 int step = (is_backside ? step_frame : num_steps - step_frame);
7671 if (is_backside) /* tile where movement starts */
7673 if (dx < 0 || dy < 0)
7675 g_em->src_offset_x = cx * step;
7676 g_em->src_offset_y = cy * step;
7680 g_em->dst_offset_x = cx * step;
7681 g_em->dst_offset_y = cy * step;
7684 else /* tile where movement ends */
7686 if (dx < 0 || dy < 0)
7688 g_em->dst_offset_x = cx * step;
7689 g_em->dst_offset_y = cy * step;
7693 g_em->src_offset_x = cx * step;
7694 g_em->src_offset_y = cy * step;
7698 g_em->width = TILEX - cx * step;
7699 g_em->height = TILEY - cy * step;
7702 /* create unique graphic identifier to decide if tile must be redrawn */
7703 /* bit 31 - 16 (16 bit): EM style graphic
7704 bit 15 - 12 ( 4 bit): EM style frame
7705 bit 11 - 6 ( 6 bit): graphic width
7706 bit 5 - 0 ( 6 bit): graphic height */
7707 g_em->unique_identifier =
7708 (graphic << 16) | (frame << 12) | (g_em->width << 6) | g_em->height;
7712 /* skip check for EMC elements not contained in original EMC artwork */
7713 if (element == EL_EMC_FAKE_ACID)
7716 if (g_em->bitmap != debug_bitmap ||
7717 g_em->src_x != debug_src_x ||
7718 g_em->src_y != debug_src_y ||
7719 g_em->src_offset_x != 0 ||
7720 g_em->src_offset_y != 0 ||
7721 g_em->dst_offset_x != 0 ||
7722 g_em->dst_offset_y != 0 ||
7723 g_em->width != TILEX ||
7724 g_em->height != TILEY)
7726 static int last_i = -1;
7734 printf("::: EMC GFX ERROR for element %d -> %d ('%s', '%s', %d)",
7735 i, element, element_info[element].token_name,
7736 element_action_info[effective_action].suffix, direction);
7738 if (element != effective_element)
7739 printf(" [%d ('%s')]",
7741 element_info[effective_element].token_name);
7745 if (g_em->bitmap != debug_bitmap)
7746 printf(" %d (%d): different bitmap! (0x%08x != 0x%08x)\n",
7747 j, is_backside, (int)(g_em->bitmap), (int)(debug_bitmap));
7749 if (g_em->src_x != debug_src_x ||
7750 g_em->src_y != debug_src_y)
7751 printf(" frame %d (%c): %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7752 j, (is_backside ? 'B' : 'F'),
7753 g_em->src_x, g_em->src_y,
7754 g_em->src_x / 32, g_em->src_y / 32,
7755 debug_src_x, debug_src_y,
7756 debug_src_x / 32, debug_src_y / 32);
7758 if (g_em->src_offset_x != 0 ||
7759 g_em->src_offset_y != 0 ||
7760 g_em->dst_offset_x != 0 ||
7761 g_em->dst_offset_y != 0)
7762 printf(" %d (%d): offsets %d,%d and %d,%d should be all 0\n",
7764 g_em->src_offset_x, g_em->src_offset_y,
7765 g_em->dst_offset_x, g_em->dst_offset_y);
7767 if (g_em->width != TILEX ||
7768 g_em->height != TILEY)
7769 printf(" %d (%d): size %d,%d should be %d,%d\n",
7771 g_em->width, g_em->height, TILEX, TILEY);
7773 num_em_gfx_errors++;
7780 for (i = 0; i < TILE_MAX; i++)
7782 for (j = 0; j < 8; j++)
7784 int element = object_mapping[i].element_rnd;
7785 int action = object_mapping[i].action;
7786 int direction = object_mapping[i].direction;
7787 boolean is_backside = object_mapping[i].is_backside;
7788 int graphic_action = el_act_dir2img(element, action, direction);
7789 int graphic_default = el_act_dir2img(element, ACTION_DEFAULT, direction);
7791 if ((action == ACTION_SMASHED_BY_ROCK ||
7792 action == ACTION_SMASHED_BY_SPRING ||
7793 action == ACTION_EATING) &&
7794 graphic_action == graphic_default)
7796 int e = (action == ACTION_SMASHED_BY_ROCK ? Ystone_s :
7797 action == ACTION_SMASHED_BY_SPRING ? Yspring_s :
7798 direction == MV_LEFT ? (is_backside? Yspring_wB: Yspring_w) :
7799 direction == MV_RIGHT ? (is_backside? Yspring_eB: Yspring_e) :
7802 /* no separate animation for "smashed by rock" -- use rock instead */
7803 struct GraphicInfo_EM *g_em = &graphic_info_em_object[i][7 - j];
7804 struct GraphicInfo_EM *g_xx = &graphic_info_em_object[e][7 - j];
7806 g_em->bitmap = g_xx->bitmap;
7807 g_em->src_x = g_xx->src_x;
7808 g_em->src_y = g_xx->src_y;
7809 g_em->src_offset_x = g_xx->src_offset_x;
7810 g_em->src_offset_y = g_xx->src_offset_y;
7811 g_em->dst_offset_x = g_xx->dst_offset_x;
7812 g_em->dst_offset_y = g_xx->dst_offset_y;
7813 g_em->width = g_xx->width;
7814 g_em->height = g_xx->height;
7815 g_em->unique_identifier = g_xx->unique_identifier;
7818 g_em->preserve_background = TRUE;
7823 for (p = 0; p < MAX_PLAYERS; p++)
7825 for (i = 0; i < SPR_MAX; i++)
7827 int element = player_mapping[p][i].element_rnd;
7828 int action = player_mapping[p][i].action;
7829 int direction = player_mapping[p][i].direction;
7831 for (j = 0; j < 8; j++)
7833 int effective_element = element;
7834 int effective_action = action;
7835 int graphic = (direction == MV_NONE ?
7836 el_act2img(effective_element, effective_action) :
7837 el_act_dir2img(effective_element, effective_action,
7839 struct GraphicInfo *g = &graphic_info[graphic];
7840 struct GraphicInfo_EM *g_em = &graphic_info_em_player[p][i][7 - j];
7846 Bitmap *debug_bitmap = g_em->bitmap;
7847 int debug_src_x = g_em->src_x;
7848 int debug_src_y = g_em->src_y;
7851 int frame = getAnimationFrame(g->anim_frames,
7854 g->anim_start_frame,
7857 getGraphicSourceExt(graphic, frame, &src_bitmap, &src_x, &src_y, FALSE);
7859 g_em->bitmap = src_bitmap;
7860 g_em->src_x = src_x;
7861 g_em->src_y = src_y;
7862 g_em->src_offset_x = 0;
7863 g_em->src_offset_y = 0;
7864 g_em->dst_offset_x = 0;
7865 g_em->dst_offset_y = 0;
7866 g_em->width = TILEX;
7867 g_em->height = TILEY;
7871 /* skip check for EMC elements not contained in original EMC artwork */
7872 if (element == EL_PLAYER_3 ||
7873 element == EL_PLAYER_4)
7876 if (g_em->bitmap != debug_bitmap ||
7877 g_em->src_x != debug_src_x ||
7878 g_em->src_y != debug_src_y)
7880 static int last_i = -1;
7888 printf("::: EMC GFX ERROR for p/a %d/%d -> %d ('%s', '%s', %d)",
7889 p, i, element, element_info[element].token_name,
7890 element_action_info[effective_action].suffix, direction);
7892 if (element != effective_element)
7893 printf(" [%d ('%s')]",
7895 element_info[effective_element].token_name);
7899 if (g_em->bitmap != debug_bitmap)
7900 printf(" %d: different bitmap! (0x%08x != 0x%08x)\n",
7901 j, (int)(g_em->bitmap), (int)(debug_bitmap));
7903 if (g_em->src_x != debug_src_x ||
7904 g_em->src_y != debug_src_y)
7905 printf(" frame %d: %d,%d (%d,%d) should be %d,%d (%d,%d)\n",
7907 g_em->src_x, g_em->src_y,
7908 g_em->src_x / 32, g_em->src_y / 32,
7909 debug_src_x, debug_src_y,
7910 debug_src_x / 32, debug_src_y / 32);
7912 num_em_gfx_errors++;
7922 printf("::: [%d errors found]\n", num_em_gfx_errors);
7928 void CheckSaveEngineSnapshot_EM(byte action[MAX_PLAYERS], int frame,
7929 boolean any_player_moving,
7930 boolean any_player_snapping,
7931 boolean any_player_dropping)
7933 static boolean player_was_waiting = TRUE;
7935 if (frame == 0 && !any_player_dropping)
7937 if (!player_was_waiting)
7939 if (!SaveEngineSnapshotToList())
7942 player_was_waiting = TRUE;
7945 else if (any_player_moving || any_player_snapping || any_player_dropping)
7947 player_was_waiting = FALSE;
7951 void CheckSaveEngineSnapshot_SP(boolean murphy_is_waiting,
7952 boolean murphy_is_dropping)
7954 static boolean player_was_waiting = TRUE;
7956 if (murphy_is_waiting)
7958 if (!player_was_waiting)
7960 if (!SaveEngineSnapshotToList())
7963 player_was_waiting = TRUE;
7968 player_was_waiting = FALSE;
7972 void CheckSingleStepMode_EM(byte action[MAX_PLAYERS], int frame,
7973 boolean any_player_moving,
7974 boolean any_player_snapping,
7975 boolean any_player_dropping)
7977 if (tape.single_step && tape.recording && !tape.pausing)
7978 if (frame == 0 && !any_player_dropping)
7979 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7981 CheckSaveEngineSnapshot_EM(action, frame, any_player_moving,
7982 any_player_snapping, any_player_dropping);
7985 void CheckSingleStepMode_SP(boolean murphy_is_waiting,
7986 boolean murphy_is_dropping)
7988 if (tape.single_step && tape.recording && !tape.pausing)
7989 if (murphy_is_waiting)
7990 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
7992 CheckSaveEngineSnapshot_SP(murphy_is_waiting, murphy_is_dropping);
7995 void getGraphicSource_SP(struct GraphicInfo_SP *g_sp,
7996 int graphic, int sync_frame, int x, int y)
7998 int frame = getGraphicAnimationFrame(graphic, sync_frame);
8000 getGraphicSource(graphic, frame, &g_sp->bitmap, &g_sp->src_x, &g_sp->src_y);
8003 boolean isNextAnimationFrame_SP(int graphic, int sync_frame)
8005 return (IS_NEXT_FRAME(sync_frame, graphic));
8008 int getGraphicInfo_Delay(int graphic)
8010 return graphic_info[graphic].anim_delay;
8013 void PlayMenuSoundExt(int sound)
8015 if (sound == SND_UNDEFINED)
8018 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8019 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8022 if (IS_LOOP_SOUND(sound))
8023 PlaySoundLoop(sound);
8028 void PlayMenuSound()
8030 PlayMenuSoundExt(menu.sound[game_status]);
8033 void PlayMenuSoundStereo(int sound, int stereo_position)
8035 if (sound == SND_UNDEFINED)
8038 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8039 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8042 if (IS_LOOP_SOUND(sound))
8043 PlaySoundExt(sound, SOUND_MAX_VOLUME, stereo_position, SND_CTRL_PLAY_LOOP);
8045 PlaySoundStereo(sound, stereo_position);
8048 void PlayMenuSoundIfLoopExt(int sound)
8050 if (sound == SND_UNDEFINED)
8053 if ((!setup.sound_simple && !IS_LOOP_SOUND(sound)) ||
8054 (!setup.sound_loops && IS_LOOP_SOUND(sound)))
8057 if (IS_LOOP_SOUND(sound))
8058 PlaySoundLoop(sound);
8061 void PlayMenuSoundIfLoop()
8063 PlayMenuSoundIfLoopExt(menu.sound[game_status]);
8066 void PlayMenuMusicExt(int music)
8068 if (music == MUS_UNDEFINED)
8071 if (!setup.sound_music)
8077 void PlayMenuMusic()
8079 PlayMenuMusicExt(menu.music[game_status]);
8082 void PlaySoundActivating()
8085 PlaySound(SND_MENU_ITEM_ACTIVATING);
8089 void PlaySoundSelecting()
8092 PlaySound(SND_MENU_ITEM_SELECTING);
8096 void ToggleFullscreenOrChangeWindowScalingIfNeeded()
8098 boolean change_fullscreen = (setup.fullscreen !=
8099 video.fullscreen_enabled);
8100 boolean change_fullscreen_mode = (video.fullscreen_enabled &&
8101 !strEqual(setup.fullscreen_mode,
8102 video.fullscreen_mode_current));
8103 boolean change_window_scaling_percent = (!video.fullscreen_enabled &&
8104 setup.window_scaling_percent !=
8105 video.window_scaling_percent);
8107 if (change_window_scaling_percent && video.fullscreen_enabled)
8110 if (!change_window_scaling_percent && !video.fullscreen_available)
8113 #if defined(TARGET_SDL2)
8114 if (change_window_scaling_percent)
8116 SDLSetWindowScaling(setup.window_scaling_percent);
8120 else if (change_fullscreen)
8122 SDLSetWindowFullscreen(setup.fullscreen);
8124 /* set setup value according to successfully changed fullscreen mode */
8125 setup.fullscreen = video.fullscreen_enabled;
8131 if (change_fullscreen ||
8132 change_fullscreen_mode ||
8133 change_window_scaling_percent)
8135 Bitmap *tmp_backbuffer = CreateBitmap(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH);
8137 /* save backbuffer content which gets lost when toggling fullscreen mode */
8138 BlitBitmap(backbuffer, tmp_backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8140 if (change_fullscreen_mode)
8142 /* keep fullscreen, but change fullscreen mode (screen resolution) */
8143 video.fullscreen_enabled = FALSE; /* force new fullscreen mode */
8146 if (change_window_scaling_percent)
8148 /* keep window mode, but change window scaling */
8149 video.fullscreen_enabled = TRUE; /* force new window scaling */
8152 /* toggle fullscreen */
8153 ChangeVideoModeIfNeeded(setup.fullscreen);
8155 /* set setup value according to successfully changed fullscreen mode */
8156 setup.fullscreen = video.fullscreen_enabled;
8158 /* restore backbuffer content from temporary backbuffer backup bitmap */
8159 BlitBitmap(tmp_backbuffer, backbuffer, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8161 FreeBitmap(tmp_backbuffer);
8163 /* update visible window/screen */
8164 BlitBitmap(backbuffer, window, 0, 0, WIN_XSIZE, WIN_YSIZE, 0, 0);
8168 void JoinRectangles(int *x, int *y, int *width, int *height,
8169 int x2, int y2, int width2, int height2)
8171 // do not join with "off-screen" rectangle
8172 if (x2 == -1 || y2 == -1)
8177 *width = MAX(*width, width2);
8178 *height = MAX(*height, height2);
8181 void ChangeViewportPropertiesIfNeeded()
8183 int gfx_game_mode = game_status;
8184 int gfx_game_mode2 = (game_status == GAME_MODE_EDITOR ? GAME_MODE_DEFAULT :
8186 struct RectWithBorder *vp_window = &viewport.window[gfx_game_mode];
8187 struct RectWithBorder *vp_playfield = &viewport.playfield[gfx_game_mode];
8188 struct RectWithBorder *vp_door_1 = &viewport.door_1[gfx_game_mode];
8189 struct RectWithBorder *vp_door_2 = &viewport.door_2[gfx_game_mode2];
8190 struct RectWithBorder *vp_door_3 = &viewport.door_2[GAME_MODE_EDITOR];
8191 int new_win_xsize = vp_window->width;
8192 int new_win_ysize = vp_window->height;
8193 int border_size = vp_playfield->border_size;
8194 int new_sx = vp_playfield->x + border_size;
8195 int new_sy = vp_playfield->y + border_size;
8196 int new_sxsize = vp_playfield->width - 2 * border_size;
8197 int new_sysize = vp_playfield->height - 2 * border_size;
8198 int new_real_sx = vp_playfield->x;
8199 int new_real_sy = vp_playfield->y;
8200 int new_full_sxsize = vp_playfield->width;
8201 int new_full_sysize = vp_playfield->height;
8202 int new_dx = vp_door_1->x;
8203 int new_dy = vp_door_1->y;
8204 int new_dxsize = vp_door_1->width;
8205 int new_dysize = vp_door_1->height;
8206 int new_vx = vp_door_2->x;
8207 int new_vy = vp_door_2->y;
8208 int new_vxsize = vp_door_2->width;
8209 int new_vysize = vp_door_2->height;
8210 int new_ex = vp_door_3->x;
8211 int new_ey = vp_door_3->y;
8212 int new_exsize = vp_door_3->width;
8213 int new_eysize = vp_door_3->height;
8214 int new_tilesize_var =
8215 (setup.small_game_graphics ? MINI_TILESIZE : game.tile_size);
8217 int tilesize = (gfx_game_mode == GAME_MODE_PLAYING ? new_tilesize_var :
8218 gfx_game_mode == GAME_MODE_EDITOR ? MINI_TILESIZE : TILESIZE);
8219 int new_scr_fieldx = new_sxsize / tilesize;
8220 int new_scr_fieldy = new_sysize / tilesize;
8221 int new_scr_fieldx_buffers = new_sxsize / new_tilesize_var;
8222 int new_scr_fieldy_buffers = new_sysize / new_tilesize_var;
8223 boolean init_gfx_buffers = FALSE;
8224 boolean init_video_buffer = FALSE;
8225 boolean init_gadgets_and_toons = FALSE;
8226 boolean init_em_graphics = FALSE;
8228 if (new_win_xsize != WIN_XSIZE ||
8229 new_win_ysize != WIN_YSIZE)
8231 WIN_XSIZE = new_win_xsize;
8232 WIN_YSIZE = new_win_ysize;
8234 init_video_buffer = TRUE;
8235 init_gfx_buffers = TRUE;
8237 // printf("::: video: init_video_buffer, init_gfx_buffers\n");
8240 if (new_scr_fieldx != SCR_FIELDX ||
8241 new_scr_fieldy != SCR_FIELDY)
8243 /* this always toggles between MAIN and GAME when using small tile size */
8245 SCR_FIELDX = new_scr_fieldx;
8246 SCR_FIELDY = new_scr_fieldy;
8248 // printf("::: new_scr_fieldx != SCR_FIELDX ...\n");
8259 new_sxsize != SXSIZE ||
8260 new_sysize != SYSIZE ||
8261 new_dxsize != DXSIZE ||
8262 new_dysize != DYSIZE ||
8263 new_vxsize != VXSIZE ||
8264 new_vysize != VYSIZE ||
8265 new_exsize != EXSIZE ||
8266 new_eysize != EYSIZE ||
8267 new_real_sx != REAL_SX ||
8268 new_real_sy != REAL_SY ||
8269 new_full_sxsize != FULL_SXSIZE ||
8270 new_full_sysize != FULL_SYSIZE ||
8271 new_tilesize_var != TILESIZE_VAR
8274 // ------------------------------------------------------------------------
8275 // determine next fading area for changed viewport definitions
8276 // ------------------------------------------------------------------------
8278 // start with current playfield area (default fading area)
8281 FADE_SXSIZE = FULL_SXSIZE;
8282 FADE_SYSIZE = FULL_SYSIZE;
8284 // add new playfield area if position or size has changed
8285 if (new_real_sx != REAL_SX || new_real_sy != REAL_SY ||
8286 new_full_sxsize != FULL_SXSIZE || new_full_sysize != FULL_SYSIZE)
8288 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8289 new_real_sx, new_real_sy, new_full_sxsize,new_full_sysize);
8292 // add current and new door 1 area if position or size has changed
8293 if (new_dx != DX || new_dy != DY ||
8294 new_dxsize != DXSIZE || new_dysize != DYSIZE)
8296 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8297 DX, DY, DXSIZE, DYSIZE);
8298 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8299 new_dx, new_dy, new_dxsize, new_dysize);
8302 // add current and new door 2 area if position or size has changed
8303 if (new_dx != VX || new_dy != VY ||
8304 new_dxsize != VXSIZE || new_dysize != VYSIZE)
8306 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8307 VX, VY, VXSIZE, VYSIZE);
8308 JoinRectangles(&FADE_SX, &FADE_SY, &FADE_SXSIZE, &FADE_SYSIZE,
8309 new_vx, new_vy, new_vxsize, new_vysize);
8312 // ------------------------------------------------------------------------
8313 // handle changed tile size
8314 // ------------------------------------------------------------------------
8316 if (new_tilesize_var != TILESIZE_VAR)
8318 // printf("::: new_tilesize_var != TILESIZE_VAR\n");
8320 // changing tile size invalidates scroll values of engine snapshots
8321 FreeEngineSnapshotSingle();
8323 // changing tile size requires update of graphic mapping for EM engine
8324 init_em_graphics = TRUE;
8335 SXSIZE = new_sxsize;
8336 SYSIZE = new_sysize;
8337 DXSIZE = new_dxsize;
8338 DYSIZE = new_dysize;
8339 VXSIZE = new_vxsize;
8340 VYSIZE = new_vysize;
8341 EXSIZE = new_exsize;
8342 EYSIZE = new_eysize;
8343 REAL_SX = new_real_sx;
8344 REAL_SY = new_real_sy;
8345 FULL_SXSIZE = new_full_sxsize;
8346 FULL_SYSIZE = new_full_sysize;
8347 TILESIZE_VAR = new_tilesize_var;
8349 init_gfx_buffers = TRUE;
8350 init_gadgets_and_toons = TRUE;
8352 // printf("::: viewports: init_gfx_buffers\n");
8353 // printf("::: viewports: init_gadgets_and_toons\n");
8356 if (init_gfx_buffers)
8358 // printf("::: init_gfx_buffers\n");
8360 SCR_FIELDX = new_scr_fieldx_buffers;
8361 SCR_FIELDY = new_scr_fieldy_buffers;
8365 SCR_FIELDX = new_scr_fieldx;
8366 SCR_FIELDY = new_scr_fieldy;
8368 SetDrawDeactivationMask(REDRAW_NONE);
8369 SetDrawBackgroundMask(REDRAW_FIELD);
8372 if (init_video_buffer)
8374 // printf("::: init_video_buffer\n");
8376 InitVideoBuffer(WIN_XSIZE, WIN_YSIZE, DEFAULT_DEPTH, setup.fullscreen);
8377 InitImageTextures();
8380 if (init_gadgets_and_toons)
8382 // printf("::: init_gadgets_and_toons\n");
8388 if (init_em_graphics)
8390 InitGraphicInfo_EM();